Skip to main content

Authentication API

Client-facing authentication endpoints for user signup, sign-in, session management, OAuth, and account operations.

Base URL

https://your-project.edgebase.dev/api

Authentication Header

Endpoints marked with Auth: Bearer Token require a valid access token:

Authorization: Bearer <accessToken>

Sign Up

POST /api/auth/signup

Create a new account with email and password. Returns the user object along with access and refresh tokens.

Auth: None (public)

Request BodyTypeRequiredDescription
emailstringYesEmail address (automatically lowercased)
passwordstringYesPassword (minimum 8 characters)
dataobjectOptional profile data (e.g., name)
curl -X POST https://your-project.edgebase.dev/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "secret123",
"data": { "name": "Jane" }
}'

Response 201

{
"user": {
"id": "01J...",
"email": "user@example.com",
"name": "Jane",
"role": "user",
"emailVerified": false,
"createdAt": "2026-01-01T00:00:00.000Z"
},
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}
ErrorStatusDescription
Email already registered409An account with this email already exists
Password too short400Password is fewer than 8 characters
Rate limited429Exceeded 10 requests per 60 seconds per IP

Sign In

POST /api/auth/signin

Sign in with email and password.

Auth: None (public)

Request BodyTypeRequiredDescription
emailstringYesEmail address
passwordstringYesPassword
curl -X POST https://your-project.edgebase.dev/api/auth/signin \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secret123"}'

Response 200 -- Same format as signup (user, accessToken, refreshToken)

ErrorStatusDescription
Invalid credentials401Email or password is incorrect
Rate limited429Exceeded 10 requests per 60 seconds per email

Anonymous Sign-In

POST /api/auth/signin/anonymous

Sign in as an anonymous user. Requires auth.anonymousAuth: true in your EdgeBase configuration.

Auth: None (public)

Request Body: None (empty object {} or no body)

curl -X POST https://your-project.edgebase.dev/api/auth/signin/anonymous \
-H "Content-Type: application/json"

Response 201

{
"user": {
"id": "01J...",
"email": null,
"role": "user",
"createdAt": "2026-01-01T00:00:00.000Z"
},
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}

Sign Out

POST /api/auth/signout

End the current session. Only the session associated with the provided refresh token is invalidated.

Auth: None (identified by refresh token)

Request BodyTypeRequiredDescription
refreshTokenstringYesThe refresh token of the session to end
curl -X POST https://your-project.edgebase.dev/api/auth/signout \
-H "Content-Type: application/json" \
-d '{"refreshToken": "eyJhbG..."}'

Response 200

{ "ok": true }

Refresh Token

POST /api/auth/refresh

Refresh an expired access token. Uses token rotation with a 30-second grace period -- the old refresh token remains valid for 30 seconds after rotation to handle concurrent requests.

Auth: None (identified by refresh token)

Request BodyTypeRequiredDescription
refreshTokenstringYesCurrent refresh token
curl -X POST https://your-project.edgebase.dev/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "eyJhbG..."}'

Response 200

{
"accessToken": "eyJhbG...(new)",
"refreshToken": "eyJhbG...(new)"
}
ErrorStatusDescription
Invalid/expired token401Token is expired or already used

Update Profile

PATCH /api/auth/profile

Update the currently authenticated user's profile.

Auth: Bearer Token

Request BodyTypeRequiredDescription
displayNamestringDisplay name
avatarUrlstringProfile image URL
emailVisibilitystring'public' or 'private'
curl -X PATCH https://your-project.edgebase.dev/api/auth/profile \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{
"displayName": "Updated Name",
"avatarUrl": "https://example.com/photo.jpg",
"emailVisibility": "public"
}'

Response 200

{
"user": {
"id": "01J...",
"email": "user@example.com",
"displayName": "Updated Name",
"avatarUrl": "https://example.com/photo.jpg",
"emailVisibility": "public"
}
}

List Sessions

GET /api/auth/sessions

List all active sessions for the currently authenticated user.

Auth: Bearer Token

curl https://your-project.edgebase.dev/api/auth/sessions \
-H "Authorization: Bearer <accessToken>"

Response 200

{
"sessions": [
{
"id": "session_1",
"ip": "1.2.3.4",
"userAgent": "Mozilla/5.0...",
"createdAt": "2026-01-01T00:00:00.000Z"
},
{
"id": "session_2",
"ip": "5.6.7.8",
"userAgent": "EdgeBase-SDK/1.0",
"createdAt": "2026-01-02T00:00:00.000Z"
}
]
}

Revoke Session

DELETE /api/auth/sessions/:id

Revoke a specific session by its ID.

Auth: Bearer Token

Path ParameterDescription
idThe session ID to revoke
curl -X DELETE https://your-project.edgebase.dev/api/auth/sessions/session_1 \
-H "Authorization: Bearer <accessToken>"

Response 200

{ "ok": true }

Change Password

POST /api/auth/change-password

Change the current user's password. Requires the current password for verification.

Auth: Bearer Token

Request BodyTypeRequiredDescription
currentPasswordstringYesCurrent password
newPasswordstringYesNew password (minimum 8 characters)
curl -X POST https://your-project.edgebase.dev/api/auth/change-password \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{"currentPassword": "oldpass123", "newPassword": "newpass456"}'

Response 200

{
"user": {
"id": "01J...",
"email": "user@example.com",
"role": "user",
"createdAt": "2026-01-01T00:00:00.000Z"
},
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}

POST /api/auth/link/email

Link an email and password to an existing anonymous account. After linking, the user can sign in with the email and password. New tokens are issued.

Auth: Bearer Token (anonymous account)

Request BodyTypeRequiredDescription
emailstringYesEmail to link
passwordstringYesPassword to set (minimum 8 characters)
curl -X POST https://your-project.edgebase.dev/api/auth/link/email \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secret123"}'

Response 200 -- Includes new tokens (user, accessToken, refreshToken)


Email OTP (Passwordless)

POST /api/auth/signin/email-otp

Send a one-time code to an email address for passwordless sign-in.

Auth: None (public)

Request BodyTypeRequiredDescription
emailstringYesEmail address
curl -X POST https://your-project.edgebase.dev/api/auth/signin/email-otp \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'

Response 200

{ "message": "OTP sent" }

In development mode (no email provider configured), the OTP code is included in the response:

{ "message": "OTP sent", "code": "123456" }

POST /api/auth/verify-email-otp

Verify the OTP code and create a session. Auto-creates a new user if the email is not registered (configurable via auth.emailOtp.autoCreate).

Auth: None (public)

Request BodyTypeRequiredDescription
emailstringYesEmail address
codestringYes6-digit OTP code
curl -X POST https://your-project.edgebase.dev/api/auth/verify-email-otp \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "code": "123456"}'

Response 200

{
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}
ErrorStatusDescription
Invalid/expired OTP400Code is incorrect or expired (5-minute TTL)
Account disabled403User account has been disabled

Change Email

POST /api/auth/change-email

Request an email change. Requires re-authentication with the current password. A verification email is sent to the new address.

Auth: Bearer Token

Request BodyTypeRequiredDescription
newEmailstringYesNew email address
passwordstringYesCurrent password
curl -X POST https://your-project.edgebase.dev/api/auth/change-email \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{"newEmail": "new@example.com", "password": "current_password"}'

Response 200

{ "message": "Verification email sent" }
ErrorStatusDescription
Invalid password401Current password is incorrect
Email already in use409The new email is already registered
Rate limited429Exceeded 3 requests per user per hour

POST /api/auth/verify-email-change

Confirm the email change using the token received via email. The token is valid for 24 hours and is single-use.

Auth: None

Request BodyTypeRequiredDescription
tokenstringYesVerification token from the email
curl -X POST https://your-project.edgebase.dev/api/auth/verify-email-change \
-H "Content-Type: application/json" \
-d '{"token": "token_from_email"}'

Response 200

{
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}
ErrorStatusDescription
Invalid/expired token400Token is expired (24h) or already used
Email already in use409The new email was registered by another user since the request

Passkeys (WebAuthn)

POST /api/auth/passkeys/register-options

Get WebAuthn registration challenge. The response contains the PublicKeyCredentialCreationOptions for the browser's navigator.credentials.create() call.

Auth: Bearer Token

curl -X POST https://your-project.edgebase.dev/api/auth/passkeys/register-options \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json"

Response 200

{
"options": {
"challenge": "base64url-encoded-challenge",
"rp": { "name": "My App", "id": "example.com" },
"user": { "id": "...", "name": "user@example.com", "displayName": "User" },
"pubKeyCredParams": [],
"excludeCredentials": []
}
}

POST /api/auth/passkeys/register

Register a WebAuthn credential. Send the attestation response from navigator.credentials.create().

Auth: Bearer Token

Request BodyTypeRequiredDescription
responseobjectYesWebAuthn attestation response

Response 200

{ "credentialId": "base64url-encoded-credential-id" }

POST /api/auth/passkeys/auth-options

Get WebAuthn authentication challenge. The response contains the PublicKeyCredentialRequestOptions for the browser's navigator.credentials.get() call.

Auth: None (public)

Request BodyTypeRequiredDescription
emailstringOptional -- narrows to user's credentials

Response 200

{
"options": {
"challenge": "base64url-encoded-challenge",
"rpId": "example.com",
"allowCredentials": [{ "id": "...", "transports": ["internal"] }],
"userVerification": "preferred"
}
}

POST /api/auth/passkeys/authenticate

Authenticate with a passkey. Send the assertion response from navigator.credentials.get().

Auth: None (public)

Request BodyTypeRequiredDescription
responseobjectYesWebAuthn assertion response

Response 200

{
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG...",
"user": {
"id": "01J...",
"email": "user@example.com"
}
}

GET /api/auth/passkeys

List all registered passkeys for the authenticated user.

Auth: Bearer Token

curl https://your-project.edgebase.dev/api/auth/passkeys \
-H "Authorization: Bearer <accessToken>"

Response 200

{
"passkeys": [
{ "credentialId": "...", "createdAt": "2026-01-01T00:00:00.000Z" }
]
}

DELETE /api/auth/passkeys/:credentialId

Delete a registered passkey.

Auth: Bearer Token

Path ParameterDescription
credentialIdThe credential ID to delete
curl -X DELETE https://your-project.edgebase.dev/api/auth/passkeys/credential-id-here \
-H "Authorization: Bearer <accessToken>"

Response 200

{ "ok": true }

Passkeys Configuration

Enable passkeys in edgebase.config.ts:

auth: {
passkeys: {
enabled: true,
rpName: 'My App',
rpID: 'example.com',
origin: 'https://example.com' // or array of origins
}
}
OptionTypeRequiredDescription
enabledbooleanYesEnable WebAuthn/Passkeys
rpNamestringYesRelying Party name (shown in authenticator UI)
rpIDstringYesYour domain (e.g., example.com)
originstring | string[]YesExpected origin(s) for WebAuthn requests

Start OAuth Flow

GET /api/auth/oauth/:provider

Initiate an OAuth login flow. This endpoint redirects the user's browser to the OAuth provider's authorization page.

Auth: None

Path ParameterDescription
providerOAuth provider name: google, github, apple, discord, microsoft, facebook, kakao, naver, x, line, slack, spotify, twitch
Query ParameterDescription
redirectURL to redirect to after authentication completes
GET https://your-project.edgebase.dev/api/auth/oauth/google?redirect=https://myapp.com/callback

Response: 302 redirect to the OAuth provider's authorization page


POST /api/auth/oauth/link/:provider

Link an OAuth account to an existing anonymous account. After linking, the user can sign in via the OAuth provider.

Auth: Bearer Token (anonymous account)

Path ParameterDescription
providerOAuth provider name
Request BodyTypeRequiredDescription
codestringYesOAuth authorization code
redirectstringYesRedirect URI

Response 200 -- Includes new tokens (user, accessToken, refreshToken)


Verify Email

POST /api/auth/verify-email

Verify a user's email address using the token sent via email.

Auth: None

Request BodyTypeRequiredDescription
tokenstringYesVerification token from the email
curl -X POST https://your-project.edgebase.dev/api/auth/verify-email \
-H "Content-Type: application/json" \
-d '{"token": "verification-token-here"}'

Response 200

{ "ok": true, "message": "Email verified." }

Request Password Reset

POST /api/auth/request-password-reset

Send a password reset email. This endpoint always returns a success response regardless of whether the email exists, to prevent email enumeration.

Auth: None

Request BodyTypeRequiredDescription
emailstringYesAccount email address
curl -X POST https://your-project.edgebase.dev/api/auth/request-password-reset \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'

Response 200 (always, regardless of whether the email exists)

{ "ok": true, "message": "If the email exists, a reset link has been sent." }

Reset Password

POST /api/auth/reset-password

Reset a user's password using the token received via email.

Auth: None

Request BodyTypeRequiredDescription
tokenstringYesPassword reset token from the email
newPasswordstringYesNew password (minimum 8 characters)
curl -X POST https://your-project.edgebase.dev/api/auth/reset-password \
-H "Content-Type: application/json" \
-d '{"token": "reset-token-here", "newPassword": "newpass456"}'

Response 200

{ "ok": true }

Error Format

All error responses follow this structure:

{
"code": 400,
"message": "Validation failed.",
"data": {
"email": { "code": "invalid_format", "message": "Invalid email format." }
}
}
HTTP StatusMeaning
400Bad request / validation error
401Authentication required or failed
409Conflict (e.g., email already exists)
429Rate limit exceeded