Authentication
Register users, sign them in, and manage their sessions with auth.*. Read and update the signed-in user's profile with user.*.
Overview
Identity gives your users a way in. Every project includes a complete user system: sign-up, sign-in, sessions, and access control, with no backend code. Your agents work with your data; your users get their own accounts and see their own records.
Key features:
- Email and password signup and signin
- Passwordless signin with one-time email codes
- OAuth with Google, GitHub, Facebook, and LinkedIn
- Short-lived ID tokens with rotating refresh tokens
- Password reset, email change, and username change with codes
- Self-service profile updates
How it works:
- The user signs in with
auth.signinand receives anid_tokenand arefresh_token. - Your app sends
Authorization: Bearer <id_token>on requests that act as the user. - Before the ID token expires, call
auth.refreshto get a new pair.
To create or manage users from your server or an agent, use Users. To add sign-in to a website without writing UI, use the Auth UI web component.
Operations
| Operation | Description | Bearer token |
|---|---|---|
auth.settings | Read public authentication settings | No |
auth.signup | Create an email/password account | No |
auth.signin | Sign in with a password or an email code | No |
auth.refresh | Rotate session tokens | No (tokens go in the payload) |
auth.signout | Revoke all of the user's sessions | Required |
auth.request_code | Email a one-time code for a purpose | No |
auth.confirm_code | Reset a password or change email/username with a code | No |
auth.change_password | Set a new password for the current user | Required |
auth.nonce | Get a nonce for OAuth | No |
auth.oauth_connect | Start OAuth signin, signup, or account linking | No |
auth.oauth_signin | Exchange an OAuth access code for a session | No |
user.get | Get the current user's profile | Required |
user.update | Update the current user's profile | Required |
Audience: Authentication flows that use account identity accept aud, which defaults to "web". auth.settings and auth.nonce need no payload fields. Use the same audience for the whole flow. Self-service signup always uses the default audience and roles.
Sign Up
auth.signup
Create an email/password account.
json
{
"operation": "auth.signup",
"payload": {
"email": "jane@example.com",
"password": "ExamplePass123!",
"first_name": "Jane",
"last_name": "Doe",
"metadata": { "plan": "free" }
}
}Parameters:
| Field | Required | Description |
|---|---|---|
email | Yes | Email address. Stored in lowercase |
password | Yes | Must meet the project's password policy |
first_name | Yes | First name. Alias name |
last_name | No | Last name. Alias surname |
phone | No | Phone number. Alias phone_number |
profile_photo | No | Photo URL. Alias photo_url |
metadata | No | Custom profile data |
Response:
json
{
"data": {
"id": "8f14e45fceea167a5a36dedd4bea2543",
"next_action": "SIGNIN",
"next_operation": "auth.signin"
}
}Behavior:
- Signup does not sign the user in. Call
auth.signinnext. next_actionisSIGNIN_WITH_CODEwhen the project requires email verification or an email second factor.- The response is the same whether or not the email already exists, so it never reveals registered accounts.
- Email subaddresses such as
jane+app@example.comare rejected. - Sending
rolesor a customaudreturnsCUSTOM_SIGNUP_CLAIMS_NOT_ALLOWED.
Sign In
auth.signin
Example - Password:
json
{
"operation": "auth.signin",
"payload": {
"email": "jane@example.com",
"password": "ExamplePass123!"
}
}Example - Email code (after auth.request_code with purpose: "signin"):
json
{
"operation": "auth.signin",
"payload": {
"email": "jane@example.com",
"grant_type": "code",
"code": "123456"
}
}Parameters:
| Field | Description |
|---|---|
email | Email address (required) |
grant_type | "password" (default) or "code" |
password | Password, for password signin |
code | One-time code, for code signin. Alias otp |
purpose | "invite" when accepting an invitation |
When the project requires email verification or an email second factor, send both password and code.
Response:
json
{
"data": {
"id_token": "<ID_TOKEN>",
"refresh_token": "<REFRESH_TOKEN>",
"token_type": "bearer",
"user_profile": {
"id": "user_123",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"aud": "web",
"roles": ["user"]
},
"next_action": null,
"next_operation": null,
"token_info": {
"ttl": 900,
"exp": 1791044100,
"iat": 1791043200,
"aud": "web",
"id": "user_123"
}
}
}The profile is abbreviated. See user.get for every field.
Tokens:
- id_token - Send as
Authorization: Bearer <id_token>. Lasts 15 minutes - refresh_token - Store securely. Exchange with
auth.refresh. Sessions last 30 days
Example - Accept an invitation:
Request a code with purpose: "invite", then set the password while signing in:
json
{
"operation": "auth.signin",
"payload": {
"email": "jane@example.com",
"grant_type": "code",
"purpose": "invite",
"code": "123456",
"password": "ExamplePass123!"
}
}Sessions
auth.refresh
Get a new token pair. The ID token may already be expired, but its session must still be valid.
json
{
"operation": "auth.refresh",
"payload": {
"id_token": "<ID_TOKEN>",
"refresh_token": "<REFRESH_TOKEN>"
}
}Returns a new session, in the same shape as auth.signin. Replace both stored tokens: the old refresh token is consumed, and reusing it fails.
auth.signout
Revoke every session for the current user. Send the ID token in the Authorization header.
json
{
"operation": "auth.signout",
"payload": {}
}Returns data: {"signout": true}. Clear the tokens stored in your app.
Example - Keep a session alive:
javascript
async function refreshSession(session) {
const result = await singlebase('auth.refresh', {
id_token: session.id_token,
refresh_token: session.refresh_token
});
return result.data; // store the new id_token and refresh_token
}One-Time Codes
auth.request_code
Email a one-time code for a specific purpose.
json
{
"operation": "auth.request_code",
"payload": {
"email": "jane@example.com",
"purpose": "password_reset"
}
}Response:
json
{
"data": {
"accepted": true,
"purpose": "password_reset",
"next_action": "RESET_PASSWORD_WITH_CODE",
"next_operation": "auth.confirm_code"
}
}| Purpose | next_action | Next operation | Field to send with the code |
|---|---|---|---|
signin | SIGNIN_WITH_CODE | auth.signin | None |
invite | ACCEPT_INVITE_WITH_CODE | auth.signin | password |
password_reset | RESET_PASSWORD_WITH_CODE | auth.confirm_code | new_password |
email_change | CHANGE_EMAIL_WITH_CODE | auth.confirm_code | new_email |
username_change | CHANGE_USERNAME_WITH_CODE | auth.confirm_code | new_username |
Behavior:
- Codes expire after 10 minutes and work once.
- Codes are bound to the account, audience, and purpose.
accepted: truedoes not reveal whether the account exists or a code was sent.- Password recovery and account changes require the matching email-code setting in the project.
auth.confirm_code
Use a code to reset a password or change an email or username.
json
{
"operation": "auth.confirm_code",
"payload": {
"email": "jane@example.com",
"purpose": "password_reset",
"code": "123456",
"new_password": "NewExamplePass123!"
}
}Parameters:
| Field | Description |
|---|---|
email | Current account email (required) |
purpose | password_reset, email_change, or username_change (required) |
code | One-time code (required). Alias otp |
new_password, new_email, new_username | The value for the purpose (required) |
For an email change, the code goes to the current email. The new email starts unverified.
Confirmation revokes existing sessions and returns a new session. Store the new tokens.
Passwords
auth.change_password
Set a new password for the signed-in user. Send the ID token in the Authorization header.
json
{
"operation": "auth.change_password",
"payload": {
"password": "NewExamplePass123!"
}
}Revokes existing sessions and returns a new session. Send plain passwords only; password_hash and password_algo are rejected.
Forgot password flow:
auth.request_codewithpurpose: "password_reset"- The user enters the emailed code
auth.confirm_codewithcodeandnew_password- Store the session it returns
OAuth
Sign users in with Google, GitHub, Facebook, or LinkedIn. Providers must be enabled in the project.
Flow:
- Call
auth.nonceand keep the nonce in the browser. - Call
auth.oauth_connectwith the provider, nonce, and intent. - Redirect the browser to
oauth_redirect_url. - The provider returns to Singlebase, which redirects to your project's OAuth return URL with an
access_code. - Call
auth.oauth_signinwith theaccess_codeand the same nonce.
auth.nonce
json
{
"operation": "auth.nonce",
"payload": {}
}Returns data: {"nonce": "<NONCE>"}. The nonce expires after 10 minutes.
auth.oauth_connect
json
{
"operation": "auth.oauth_connect",
"payload": {
"provider": "google",
"nonce": "<NONCE>",
"intent": "signin"
}
}Parameters:
| Field | Description |
|---|---|
provider | google, github, facebook, or linkedin (required) |
nonce | Nonce from auth.nonce (required) |
intent | signin (default), signup, or link |
id_token, refresh_token | Current session. Required for link |
OAuth signup requires a verified email. Names are optional; if the provider omits a name, the full email address is used as first_name.
Response:
json
{
"data": {
"oauth_redirect_url": "https://accounts.google.com/o/oauth2/v2/auth?...",
"oauth_provider": "google"
}
}Return URL parameters:
| Parameter | Meaning |
|---|---|
access_code | Success. Exchange it with auth.oauth_signin |
error=oauth_denied | The user cancelled at the provider |
oauth_error=<CODE> | The flow failed. See the codes below |
oauth_error | Meaning |
|---|---|
OAUTH_VERIFICATION_FAILED | The provider response could not be verified |
VERIFIED_PROVIDER_EMAIL_REQUIRED | The provider did not return a verified email |
SIGN_IN_TO_LINK_PROVIDER | The email belongs to an account; sign in, then link |
PROVIDER_ALREADY_LINKED | The provider identity belongs to another account |
OAUTH_SIGNUP_DISABLED, OAUTH_SIGNIN_DISABLED | The project disabled OAuth signup or signin |
MISSING_OAUTH_CODE | The provider did not return a code |
INVALID_CREDENTIALS, INVALID_TOKEN | The account or linking session is invalid |
REQUIRE_PASSWORD_CHANGE | The account must change its password first |
OAUTH_FAILED | Unexpected failure |
auth.oauth_signin
json
{
"operation": "auth.oauth_signin",
"payload": {
"access_code": "<ACCESS_CODE>",
"nonce": "<NONCE>"
}
}The access code expires after 10 minutes and works once. Returns a session. For intent: "link", the exchange also completes the link.
Example - Browser flow:
javascript
// 1. Start
const { data: { nonce } } = await singlebase('auth.nonce');
sessionStorage.setItem('oauth_nonce', nonce);
const { data } = await singlebase('auth.oauth_connect', {
provider: 'github',
nonce,
intent: 'signin'
});
window.location.href = data.oauth_redirect_url;
// 2. On your return URL
const params = new URLSearchParams(window.location.search);
const accessCode = params.get('access_code');
if (accessCode) {
const session = await singlebase('auth.oauth_signin', {
access_code: accessCode,
nonce: sessionStorage.getItem('oauth_nonce')
});
}Settings
auth.settings
Read the project's public authentication settings to build your signin screens.
json
{
"operation": "auth.settings",
"payload": {}
}Response:
json
{
"data": {
"enabled": true,
"auth_settings": {
"enabled": true,
"allow_signin": true,
"allow_signup": true,
"signin_method": "password",
"second_factor": null,
"signup_verify_email": false,
"password_policy": {}
},
"oauth_settings": {
"enabled": true,
"allow_signin": true,
"allow_signup": true,
"redirect_url": "https://app.example.com/auth/callback"
},
"oauth_providers": {
"google": { "enabled": true, "name": "Google" }
}
}
}Values are illustrative. Provider credentials are never returned.
Current User
Use the Current User API to read and update the account in your bearer token. It cannot select another user.
user.get
See user.get for the request and profile response.
user.update
See user.update for editable profile fields.
Common Errors
| Message | Meaning |
|---|---|
EMAIL_SIGNUP_DISABLED, EMAIL_SIGNIN_DISABLED | The project disabled this method |
INVALID_CREDENTIALS | Wrong email, password, or code, or the account is inactive |
CODE_REQUIRED | The project requires a code with the password |
REQUIRE_PASSWORD_CHANGE | The account must reset its password before signing in |
INVALID_BEARER_TOKEN | The ID token or its session is invalid |
AUTHENTICATION_REQUIRED | The operation needs a bearer token |
AGENT_KEY_EXPIRED | The agent key reached its expiry; returns 401 before scope checks or usage tracking. Extend it or remove its expiry through console.update_agent_key |
CUSTOM_SIGNUP_CLAIMS_NOT_ALLOWED | Signup sent roles or a custom audience |
VERIFICATION_DISABLED | Email codes are disabled for this change |
INVALID_NONCE | The OAuth nonce is invalid, expired, or does not match |
MISSING_PROFILE_DATA | user.update had no supported fields |
See Responses for the error envelope.