Authenticating Users
Using REST API directly to sign in or up with Hyphen account.
Process
A. Signing In with Challenge
Starts SignIn Challenge. This is required when the user is trying to sign in from the device already registered.
The client should request with the public key of the device. If it's not registered,
it will raise HTTP 400 with PleaseRegisterKey error.
Issues a random 32-byte challengeData to the client.
The client should respond by signing the data with the device's key, within 5 minutes.
The challenge type should be specified with either passKey or deviceKey.
The public key of the PassKey in client-side. Should be 64-byte hex string concatenated with [x: 32byte, y: 32byte].
Ok
The challenge issued by the server. Randomly-generated 16-byte hex string.. Client should be respond by signing the challenge. PLEASE NOTE THAT the hex should be decoded before signing!
deadbeefdeadbeefdeadbeefdeadbeefThe deadline of the challenge. Usually 5m after now.
2023-08-01T00:00:00.000ZPOST /auth/v1/signin/challenge HTTP/1.1
Host: api.dev.hyphen.at/
Content-Type: application/json
Accept: */*
Content-Length: 199
{
"challengeType": "deviceKey",
"request": {
"method": "firebase",
"token": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"chainName": "flow-mainnet"
},
"publicKey": "text",
"passKey": {
"username": "jun@meowauth.xyz"
}
}Ok
{
"challengeData": "deadbeefdeadbeefdeadbeefdeadbeef",
"expiresAt": "2023-08-01T00:00:00.000Z"
}Finishes sign in challenge.
The client should provide the signature of the challengeData issued by the server.
If valid, it will return the API credentials and account information.
Ok
POST /auth/v1/signin/challenge/respond HTTP/1.1
Host: api.dev.hyphen.at/
Content-Type: application/json
Accept: */*
Content-Length: 227
{
"challengeType": "deviceKey",
"challengeData": "text",
"deviceKey": {
"signature": "text"
},
"passKey": {
"clientDataJSON": "text",
"authenticatorData": "h4tyVoWj/Tc2Lzxtu79kl9kYtTtraUYwBbIV1ymJaugdAAAAAA==",
"signature": "MEUCI...p33Sz9c="
}
}Ok
{
"account": {
"id": "text",
"addresses": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"parent": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
"transaction": {
"id": "text",
"chainName": "flow-mainnet",
"refUrl": "text"
},
"credentials": {
"accessToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"refreshToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc"
}
}B. Signing In with 2FA
Sign in with two-factor authentication from other device. This is required when the user is trying to sign on the new device.
Ok
The ephemeral access token for the client to use for signing the key add transaction. This token is valid for 5 minutes, and can be used only for calling the Sign API.
eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6VcPOST /auth/v1/signin/2fa HTTP/1.1
Host: api.dev.hyphen.at/
Content-Type: application/json
Accept: */*
Content-Length: 441
{
"request": {
"method": "firebase",
"token": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"chainName": "flow-mainnet"
},
"userKey": {
"type": "device",
"publicKey": "text",
"device": {
"publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
"pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"name": "iPhone 14",
"osName": "iOS",
"osVersion": "16.2",
"deviceManufacturer": "Apple",
"deviceModel": "SM-265N",
"lang": "en",
"type": "mobile"
}
}
}Ok
{
"twoFactorAuth": {
"id": "text",
"accountId": "text",
"request": {
"id": "faceb00c-cafe-babe-badd-deadbeef1234",
"app": {
"appId": "swirl-dev",
"appName": "Swirl"
},
"userOpInfo": {
"type": "sign-in",
"signIn": {
"email": "john@acme.com",
"ip": "127.0.0.1",
"location": "Seoul, KR"
}
},
"srcDevice": {
"id": "deadbeef-dead-beef-cafe-deadbeefcafe",
"publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
"sdkVersion": "1.0.0",
"pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"name": "iPhone 14",
"osName": "iOS",
"osVersion": "16.2",
"deviceManufacturer": "Apple",
"deviceModel": "SM-265N",
"lang": "en",
"type": "mobile"
},
"destDevice": {
"id": "deadbeef-dead-beef-cafe-deadbeefcafe",
"publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
"sdkVersion": "1.0.0",
"pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"name": "iPhone 14",
"osName": "iOS",
"osVersion": "16.2",
"deviceManufacturer": "Apple",
"deviceModel": "SM-265N",
"lang": "en",
"type": "mobile"
},
"message": "deadbeefdeadbeefdeadbeef",
"requestedAt": "2021-01-01T00:00:00Z"
},
"status": "pending",
"extra": null,
"result": {
"txId": "text"
},
"expiresAt": "2021-01-01T00:00:00Z"
},
"ephemeralAccessToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc"
}Finish sign in after the user authorized the request on the other device. This API will return the API credentials and account information.
The latest 2FA request ID.
deadbeef-dead-beef-dead-beefdeadbeefOk
POST /auth/v1/signin/2fa/finish HTTP/1.1
Host: api.dev.hyphen.at/
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 65
{
"twoFactorAuthRequestId": "deadbeef-dead-beef-dead-beefdeadbeef"
}Ok
{
"account": {
"id": "text",
"addresses": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"parent": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
"transaction": {
"id": "text",
"chainName": "flow-mainnet",
"refUrl": "text"
},
"credentials": {
"accessToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"refreshToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc"
}
}C. Creating an Account
Creates new account and register the current device's key as the initial user key. This involves deploying the account on the chain you've specified.
The login channel the user is currently signing in/up.
firebasePossible values: The access token / ID token / redirect code retrieved as a result of OAuth 2.0 Sign In. Server uses it to call the OAuth provider to verify that the client correctly finished OAuth flow, and fetches users' basic profile information such as email.
Its value differs by channel:
- For
apple, it's the ID token returned after finishing the SIWA process from the client. - For
firebase, it's the redirect code from OAuth2 Redirect URI.
eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6VcThe user key (usually device key / PassKey) created from the client SDK. Used as one of initial multi-sig keys for creating an account.
POST /auth/v1/signup HTTP/1.1
Host: api.dev.hyphen.at/
Content-Type: application/json
Accept: */*
Content-Length: 429
{
"method": "firebase",
"token": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"chainName": "flow-mainnet",
"userKey": {
"type": "device",
"publicKey": "text",
"device": {
"publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
"pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"name": "iPhone 14",
"osName": "iOS",
"osVersion": "16.2",
"deviceManufacturer": "Apple",
"deviceModel": "SM-265N",
"lang": "en",
"type": "mobile"
}
}
}{
"account": {
"id": "text",
"addresses": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"parent": [
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"profileImageUrl": "text",
"domainName": "vitalik.eth",
"chainName": "flow-mainnet",
"chainId": 80001,
"chainType": "evm"
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
"transaction": {
"id": "text",
"chainName": "flow-mainnet",
"refUrl": "text"
},
"credentials": {
"accessToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc",
"refreshToken": "eyJhbGciOiJIUzI...Qedy-rosPJLzs3jArh6Vc"
}
}D. Finishing Authorization
Last updated
Was this helpful?