> For the complete documentation index, see [llms.txt](https://bound-1.gitbook.io/bound-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bound-1.gitbook.io/bound-docs/developer-guide/api-reference/auth.md).

# Auth

## Authenticate user with BIP322 signature verification

> Authenticates a user by verifying their BIP322 signature and creates a wallet with JWT tokens

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/authenticate":{"post":{"operationId":"AuthController_authenticate","summary":"Authenticate user with BIP322 signature verification","description":"Authenticates a user by verifying their BIP322 signature and creates a wallet with JWT tokens","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticateDto"}}}},"responses":{"200":{"description":"User authenticated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseResponseDto"}}}},"400":{"description":"Invalid signature, invalid address, or authentication failed","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"string"}}}}}}},"tags":["auth"]}}},"components":{"schemas":{"AuthenticateDto":{"type":"object","properties":{"message":{"type":"string","description":"The message that was signed"},"signature":{"type":"string","description":"The BIP322 signature of the message"},"address":{"type":"string","description":"The Bitcoin address that signed the message"},"publicKey":{"type":"string","description":"The public key used for signing"}},"required":["message","signature","address","publicKey"]},"BaseResponseDto":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"type":"object"},"metaData":{"$ref":"#/components/schemas/ResponseMetaData"}},"required":["code","message","data","metaData"]},"ResponseMetaData":{"type":"object","properties":{"totalItems":{"type":"number"},"currentPage":{"type":"number"},"pageSize":{"type":"number"},"totalPages":{"type":"number"}},"required":["totalItems","currentPage","pageSize","totalPages"]}}}}
```

## POST /api/auth/refresh-token

> Refresh access token using refresh token

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/refresh-token":{"post":{"operationId":"AuthController_refreshToken","summary":"Refresh access token using refresh token","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenDto"}}}},"responses":{"200":{"description":"New access token and rotated refresh token generated successfully"},"400":{"description":"Invalid or expired refresh token","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"string"}}}}}}},"tags":["auth"]}}},"components":{"schemas":{"RefreshTokenDto":{"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token to generate new access token"}},"required":["refreshToken"]}}}}
```

## GET /api/auth/webauthn/challenge

> Get WebAuthn challenge for passkey registration or login

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/webauthn/challenge":{"get":{"operationId":"AuthController_getWebAuthnChallenge","summary":"Get WebAuthn challenge for passkey registration or login","parameters":[],"responses":{"200":{"description":""}},"tags":["auth"]}}}}
```

## POST /api/auth/email-verification/request

> Request email OTP for registration — sends a 6-digit code to the given email address

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/email-verification/request":{"post":{"operationId":"AuthController_requestEmailVerification","summary":"Request email OTP for registration — sends a 6-digit code to the given email address","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerificationRequestDto"}}}},"responses":{"200":{"description":"OTP queued"},"400":{"description":"Email already registered"},"429":{"description":"Rate limit exceeded"}},"tags":["auth"]}}},"components":{"schemas":{"EmailVerificationRequestDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address to send OTP to"}},"required":["email"]}}}}
```

## POST /api/auth/register

> Register a new bound wallet account

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/register":{"post":{"operationId":"AuthController_register","summary":"Register a new bound wallet account","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDto"}}}},"responses":{"201":{"description":"Account registered successfully"}},"tags":["auth"]}}},"components":{"schemas":{"RegisterDto":{"type":"object","properties":{"email":{"type":"string"},"otp":{"type":"string"},"authType":{"type":"string","enum":["passkey","srp","wallet"]},"passkey":{"$ref":"#/components/schemas/RegisterPasskeyDto"},"srpData":{"$ref":"#/components/schemas/RegisterSrpDto"},"walletAuth":{"$ref":"#/components/schemas/RegisterWalletAuthDto"},"wallets":{"$ref":"#/components/schemas/RegisterWalletsDto"},"referralCode":{"type":"string","description":"Referral code (base64url-encoded referrer accountId). Throws if invalid."}},"required":["email","otp","authType","wallets"]},"RegisterPasskeyDto":{"type":"object","properties":{"challengeId":{"type":"string"},"credentialId":{"type":"string"},"webauthnPublicKey":{"type":"string"},"attestation":{"type":"string"},"clientDataJson":{"type":"string"},"authenticatorData":{"type":"string"},"encryptedBlob":{"type":"string"},"deviceName":{"type":"string"}},"required":["challengeId","credentialId","webauthnPublicKey","attestation","clientDataJson","authenticatorData","encryptedBlob"]},"RegisterSrpDto":{"type":"object","properties":{"srpSalt":{"type":"string","description":"Random salt generated client-side (base64, 32 bytes)"},"srpVerifier":{"type":"string","description":"SRP verifier v = g^x mod N (base64), x = H(salt || H(email:password))"},"encryptedBlob":{"type":"string"},"passwordHint":{"type":"string"}},"required":["srpSalt","srpVerifier","encryptedBlob"]},"RegisterWalletAuthDto":{"type":"object","properties":{"address":{"type":"string"},"chain":{"type":"string","enum":["evm","solana","bitcoin"]},"publicKey":{"type":"string"},"provider":{"type":"string"},"challengeId":{"type":"string"},"signature":{"type":"string"},"encryptedBlob":{"type":"string"}},"required":["address","chain","publicKey","provider","challengeId","signature","encryptedBlob"]},"RegisterWalletsDto":{"type":"object","properties":{"btc":{"$ref":"#/components/schemas/RegisterBtcWalletDto"},"evm":{"$ref":"#/components/schemas/RegisterEvmWalletDto"},"sol":{"$ref":"#/components/schemas/RegisterSolWalletDto"}},"required":["btc","evm","sol"]},"RegisterBtcWalletDto":{"type":"object","properties":{"publicKey":{"type":"string"},"address":{"type":"string"},"message":{"type":"string"},"signature":{"type":"string"}},"required":["publicKey","address","message","signature"]},"RegisterEvmWalletDto":{"type":"object","properties":{"publicKey":{"type":"string"},"message":{"type":"string"},"signature":{"type":"string"}},"required":["publicKey","message","signature"]},"RegisterSolWalletDto":{"type":"object","properties":{"publicKey":{"type":"string"},"message":{"type":"string"},"signature":{"type":"string"}},"required":["publicKey","message","signature"]}}}}
```

## POST /api/auth/passkey/login

> Login with passkey or password

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/passkey/login":{"post":{"operationId":"AuthController_login","summary":"Login with passkey or password","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginDto"}}}},"responses":{"200":{"description":"Login successful"}},"tags":["auth"]}}},"components":{"schemas":{"LoginDto":{"type":"object","properties":{"credentialId":{"type":"string"},"challengeId":{"type":"string"},"webauthnAssertion":{"$ref":"#/components/schemas/WebAuthnAssertionDto"}},"required":["credentialId","challengeId","webauthnAssertion"]},"WebAuthnAssertionDto":{"type":"object","properties":{"authenticatorData":{"type":"string"},"clientDataJson":{"type":"string"},"signature":{"type":"string"}},"required":["authenticatorData","clientDataJson","signature"]}}}}
```

## SRP step 1 — get salt and server public key

> Client sends email. Server returns salt and serverPublic (B) for SRP-6a authentication.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/srp/init":{"post":{"operationId":"AuthController_srpInit","summary":"SRP step 1 — get salt and server public key","description":"Client sends email. Server returns salt and serverPublic (B) for SRP-6a authentication.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SrpInitDto"}}}},"responses":{"200":{"description":"Returns sessionId, salt, and serverPublic (B)"}},"tags":["auth"]}}},"components":{"schemas":{"SrpInitDto":{"type":"object","properties":{"email":{"type":"string","description":"Account email"}},"required":["email"]}}}}
```

## POST /api/auth/logout

> Logout — revoke current session and refresh token

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/auth/logout":{"post":{"operationId":"AuthController_logout","summary":"Logout — revoke current session and refresh token","parameters":[],"responses":{"200":{"description":"Logged out successfully"}},"tags":["auth"]}}}}
```

## SRP step 2 — verify client proof and complete login

> Client sends clientPublic (A) and clientProof (M1). Server verifies and returns serverProof (M2) plus tokens.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/srp/verify":{"post":{"operationId":"AuthController_srpVerify","summary":"SRP step 2 — verify client proof and complete login","description":"Client sends clientPublic (A) and clientProof (M1). Server verifies and returns serverProof (M2) plus tokens.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SrpVerifyDto"}}}},"responses":{"200":{"description":"Login successful — returns serverProof, tokens, and encryptedBlob"}},"tags":["auth"]}}},"components":{"schemas":{"SrpVerifyDto":{"type":"object","properties":{"sessionId":{"type":"string","description":"Session ID returned by srp/init"},"clientPublic":{"type":"string","description":"Client ephemeral public key A = g^a mod N (base64)"},"clientProof":{"type":"string","description":"Client proof M1 = H(H(N)⊕H(g) || H(I) || salt || A || B || K) (base64)"}},"required":["sessionId","clientPublic","clientProof"]}}}}
```

## GET /api/auth/wallet/challenge

> Wallet auth — issue a fresh login nonce for an auth wallet address

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/wallet/challenge":{"get":{"operationId":"AuthController_getWalletChallenge","summary":"Wallet auth — issue a fresh login nonce for an auth wallet address","parameters":[{"name":"address","required":true,"in":"query","description":"Auth wallet address to issue a login nonce for","schema":{"type":"string"}}],"responses":{"200":{"description":"Returns challengeId, challenge (nonce), expiresAt"}},"tags":["auth"]}}}}
```

## POST /api/auth/wallet/login

> Wallet auth — log in by signing the issued nonce

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/wallet/login":{"post":{"operationId":"AuthController_walletLogin","summary":"Wallet auth — log in by signing the issued nonce","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletLoginDto"}}}},"responses":{"200":{"description":"Login successful — tokens, encryptedBlob, account, wallets"}},"tags":["auth"]}}},"components":{"schemas":{"WalletLoginDto":{"type":"object","properties":{"address":{"type":"string","description":"Registered auth wallet address"},"challengeId":{"type":"string","description":"challengeId from GET /auth/wallet/challenge"},"signature":{"type":"string","description":"Wallet signature over the issued server nonce"}},"required":["address","challengeId","signature"]}}}}
```

## 2FA TOTP verify — exchange pending token + OTP code for full JWT pair

> Accepts the pendingToken returned by srp/verify when 2FA is enabled, plus the current TOTP code. Returns the full login response.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/auth/2fa/verify":{"post":{"operationId":"AuthController_verifyTotpLogin","summary":"2FA TOTP verify — exchange pending token + OTP code for full JWT pair","description":"Accepts the pendingToken returned by srp/verify when 2FA is enabled, plus the current TOTP code. Returns the full login response.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpVerifyDto"}}}},"responses":{"200":{"description":"OTP verified — returns accessToken, refreshToken, encryptedBlob, account, wallets"},"400":{"description":"Invalid or expired pendingToken, or wrong OTP code"}},"tags":["auth"]}}},"components":{"schemas":{"TotpVerifyDto":{"type":"object","properties":{}}}}}
```
