Skip to content

User Authentication

User authentication is done using The Bridge Auth Service that supports OAuth 2.0 / OpenID Connect. Tokens are issued as JWTs, and every access token is scoped to one workspace (the API calls it a tenant). Apps, allowed origins, and workspaces are managed in the Control Center.

This page covers the token lifecycle: starting a login, exchanging codes for tokens, refreshing, verifying, and revoking them. If you want to build your own login UI and drive each step over plain HTTP instead of redirecting to the hosted login, see Auth flows (no SDK).

At a glance, the authentication process looks like this:

  1. You initiate the login process by redirecting the user to the /authorize endpoint (or the simpler shorthand /url/login and /url/signup endpoints).
  2. The user is pulled through an authentication process provided by The Bridge with cloud views.
  3. Once the user is authenticated and has selected the workspace to access, the user is redirected back to your app with a code.
  4. You make a call to the /token endpoint with this code to exchange it for access tokens and user profile information (OpenID).
  5. You can verify that the tokens are valid and safe to trust by using the public keys available from the /.well-known endpoints.
  6. You can refresh the tokens using the /token endpoint to obtain up-to-date access and profile information.

Start the OAuth 2.0 user login flow by redirecting the user to the /authorize endpoint. The user will be able to choose a login method and after the authentication process get back to your app with an auth code that you can exchange for access and OpenID tokens.

GET https://api.thebridge.dev/auth/authorize

Query Parameters

ParameterTypeRequiredDescription
client_idstringRequiredYour app ID
response_typestringRequiredWhat kind of tokens will be generated. We support code
redirect_uristringRequiredA target URI where the authenticated user will be redirected to together with tokens. Must be a valid URI in your App redirectUris
scopestringRequiredAny or all of: openid, profile, email, address, phone, onboarding, tenant
statestringOptionalUsed to resume a state in your app. The state will be available in the response code
signupbooleanOptionalSet to true to initiate a signup instead of the default login flow
signup_planstringOptionalThe key to an existing plan. Allows the signup to end with the new workspace subscribing to a specific plan
force_federationstringOptionalForce a certain federated login flow: ms-azure-ad, google, or saml
federation_connectionstringOptionalSpecify the federation connection ID to use with force_federation

You should redirect the user agent to this endpoint. This is not an API-to-API call. No x-api-key header is required; use your App ID in the client_id query parameter.

Request example

Build the authorize URL with your app ID, redirect URI, and scope, then open it in a browser to start the login flow.

# Build the URL (replace YOUR_APP_ID and open in browser)
# Use URL encoding for redirect_uri and scope if they contain special characters
curl -G 'https://api.thebridge.dev/auth/authorize' \
--data-urlencode 'client_id=YOUR_APP_ID' \
--data-urlencode 'response_type=code' \
--data-urlencode 'redirect_uri=http://localhost:8080/auth/oauth-callback' \
--data-urlencode 'scope=openid profile email tenant' \
--data-urlencode 'state=optional-state'
# Or open: https://api.thebridge.dev/auth/authorize?client_id=YOUR_APP_ID&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Foauth-callback&scope=openid%20profile%20email%20tenant

Use the simpler shorthand endpoint /url/login to initiate the login flow. The Bridge will collect your default config and issue the OAuth 2.0 flow.

GET https://api.thebridge.dev/auth/url/login/:YOUR_APP_ID

Query Parameters

ParameterTypeRequiredDescription
redirectUristringOptionalTarget URI for redirect after auth. Defaults to your App defaultCallbackUri
statestringOptionalResume a state in your app
responseTypestringOptionalcode or id_token
forceFederationstringOptionalms-azure-ad, google, or saml
federationConnectionstringOptionalFederation connection ID for SAML flows

You should redirect the user agent to this endpoint. This is not an API-to-API call. No x-api-key header is required; the App ID is in the URL path.

Try it

GET Try it out
GET https://api.thebridge.dev/auth/url/login/:YOUR_APP_ID
The ID of your app

Use the shorthand endpoint /url/signup to initiate the signup flow.

GET https://api.thebridge.dev/auth/url/signup/:YOUR_APP_ID

Query Parameters

ParameterTypeRequiredDescription
redirectUristringOptionalTarget URI for redirect after auth. Defaults to your App defaultCallbackUri
statestringOptionalResume a state in your app
signupPlanstringOptionalKey to an existing plan for the new workspace to subscribe to
signupCurrencystringOptionalCurrency matching one of the prices in the plan
signupRecurrenceIntervalstringOptionalInterval matching one of the prices in the plan

You should redirect the user agent to this endpoint. This is not an API-to-API call. No x-api-key header is required; the App ID is in the URL path.

Try it

GET Try it out
GET https://api.thebridge.dev/auth/url/signup/:YOUR_APP_ID
The ID of your app

Clear the hosted login session cookies and send the user back to the login screen. Use this to log the user out of The Bridge itself, in addition to deleting the tokens your app holds (and revoking the refresh token, see Revoke Refresh Token).

GET https://api.thebridge.dev/auth/url/logout/:YOUR_APP_ID

Query Parameters

ParameterTypeRequiredDescription
redirect_uristringOptionalWhere the user should land after a subsequent re-login

Redirects the user agent to the login URL after clearing auth cookies.

You should redirect the user agent to this endpoint. This is not an API-to-API call.

Request example

# Open in the user's browser (not an API call)
# https://api.thebridge.dev/auth/url/logout/YOUR_APP_ID
curl -I 'https://api.thebridge.dev/auth/url/logout/YOUR_APP_ID'

Get new tokens using an authorization code from a user who just completed authentication, or by using a refresh token that was issued to a logged-in user before.

POST https://api.thebridge.dev/auth/token

Body Parameters

ParameterTypeRequiredDescription
client_idstringRequiredYour app ID
grant_typestringRequiredauthorization_code or refresh_token
codestringOptionalRequired for authorization_code grant. The code received from the authenticated user
redirect_uristringOptionalRequired for authorization_code grant. Must be a valid URI in your App redirectUris
refresh_tokenstringOptionalRequired for refresh_token grant. The refresh token issued previously

Returns access token, refresh token, and id_token.

Request example

# Exchange authorization code for tokens
curl --request POST 'https://api.thebridge.dev/auth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "client_id": "YOUR_APP_ID",
  "grant_type": "authorization_code",
  "code": "XXXX",
  "redirect_uri": "http://localhost:8080/auth/oauth-callback"
}'

# Refresh tokens
curl --request POST 'https://api.thebridge.dev/auth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "client_id": "YOUR_APP_ID",
  "grant_type": "refresh_token",
  "refresh_token": "XXXX"
}'

Response example:

{
    "access_token": "XXXX",
    "refresh_token": "XXXX",
    "token_type": "Bearer",
    "expires_in": "XXXX",
    "id_token": "XXXX"
}
POST Try it out
POST https://api.thebridge.dev/auth/token

Get new tokens using a simplified endpoint with fewer parameters.

POST https://api.thebridge.dev/auth/token/:grantType/:YOUR_APP_ID

grantType is code or refresh.

Body Parameters

ParameterTypeRequiredDescription
codestringOptionalRequired for code grant. The authorization code
refreshTokenstringOptionalRequired for refresh grant. The refresh token
redirectUristringOptionalOptional for code grant. Defaults to your App defaultCallbackUri

Returns access token, refresh token, id_token, and user_profile.

Request example

# Exchange code for tokens
curl --request POST 'https://api.thebridge.dev/auth/token/code/YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "code": "XXXX" }'

# Refresh tokens
curl --request POST 'https://api.thebridge.dev/auth/token/refresh/YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "refreshToken": "XXXX" }'

Response example:

{
    "access_token": "XXXX",
    "refresh_token": "XXXX",
    "token_type": "Bearer",
    "expires_in": "XXXX",
    "id_token": "XXXX",
    "user_profile": {
        "sub": "63d25a9e0796d40008680f9a",
        "name": "John Doe",
        "family_name": "Doe",
        "given_name": "John",
        "preferred_username": "john@example.com",
        "locale": "en",
        "email": "john@example.com",
        "email_verified": true,
        "onboarded": true,
        "tenant_id": "63d25a9e0796d40008680f96",
        "tenant_name": "Johns Family",
        "tenant_locale": "en",
        "tenant_logo": ""
    }
}

Mint a fresh token set using only the current access token as the credential. Unlike the refresh_token grant, this endpoint does not need a refresh token: it verifies the Bearer access token, re-reads the user’s and workspace’s current state, and issues new tokens carrying the latest claims (role, plan, token version).

This is what the Bridge SDKs call when they receive a realtime user.state_changed signal, so new claims show up before the natural token rotation. Access tokens that expired less than 5 minutes ago are still accepted; beyond that the caller must run the normal login flow or use a refresh token.

POST https://api.thebridge.dev/auth/refresh-token

Headers: Authorization: Bearer USER_ACCESS_TOKEN

Body Parameters

ParameterTypeRequiredDescription
scopestringOptionalNarrow the scope of the new tokens. Defaults to the scope of the presented access token

Returns a fresh token set with user_profile (the decoded id_token). HTTP 401 when the Bearer token is missing, invalid, or expired beyond the grace window.

Request example

curl --request POST 'https://api.thebridge.dev/auth/refresh-token' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{}'

Response example:

{
    "access_token": "XXXX",
    "refresh_token": "XXXX",
    "token_type": "Bearer",
    "expires_in": 3600,
    "id_token": "XXXX",
    "user_profile": {
        "sub": "63d25a9e0796d40008680f9a",
        "preferred_username": "john@example.com",
        "email": "john@example.com",
        "tenant_id": "63d25a9e0796d40008680f96"
    }
}

The standard OpenID Connect discovery document: issuer, endpoint locations, supported response types, scopes, and claims. Point any OIDC-compliant library at this document to configure itself.

GET https://api.thebridge.dev/auth/.well-known/openid-configuration

The discovery document. Tokens are signed with PS256.

Request example

curl 'https://api.thebridge.dev/auth/.well-known/openid-configuration'

Response example:

{
    "issuer": "https://auth.thebridge.dev",
    "authorization_endpoint": "https://auth.thebridge.dev/authorize",
    "token_endpoint": "https://auth.thebridge.dev/token",
    "jwks_uri": "https://api.thebridge.dev/auth/.well-known/jwks.json",
    "response_types_supported": ["code"],
    "id_token_signing_alg_values_supported": ["PS256"],
    "scopes_supported": ["openid", "profile", "email", "address", "phone", "onboarding", "tenant"],
    "token_endpoint_auth_methods_supported": ["none"],
    "claims_supported": ["sub", "name", "family_name", "given_name", "preferred_username", "email", "email_verified", "onboarded", "locale", "tenant_id", "tenant_name", "tenant_locale", "tenant_logo", "tenant_onboarded", "multi_tenant"]
}

The public keys used to sign access tokens and ID tokens. Fetch this once (and cache it, honoring key IDs) and verify JWT signatures locally in your backend on every request, instead of calling The Bridge each time.

GET https://api.thebridge.dev/auth/.well-known/jwks.json

A JWKS document. Verify tokens with the key whose kid matches the token header, algorithm PS256.

Verify locally with any JOSE library: check the signature against these keys, plus the token’s exp. This is the recommended way for a backend to trust an incoming user access token.

Request example

curl 'https://api.thebridge.dev/auth/.well-known/jwks.json'

Response example:

{
    "keys": [
        {
            "kty": "RSA",
            "n": "XXXX",
            "e": "AQAB",
            "kid": "1",
            "use": "sig"
        }
    ]
}

Mark a refresh token as revoked so it can no longer be used to obtain new access tokens. Call this on logout, in addition to deleting the tokens your app holds. No authentication is required: the refresh token itself is verified before being revoked, and an already-invalid token still returns success.

POST https://api.thebridge.dev/auth/auth/revoke

Body Parameters

ParameterTypeRequiredDescription
refreshTokenstringRequiredThe refresh token to revoke

{ "success": true }

Request example

curl --request POST 'https://api.thebridge.dev/auth/auth/revoke' \
--header 'Content-Type: application/json' \
--data-raw '{ "refreshToken": "XXXX" }'

Response example:

{
    "success": true
}

A user can belong to several workspaces, but a token set is always scoped to exactly one. These endpoints let a logged-in user see their workspaces and re-scope their tokens to another one, using only the current access token as the credential.

Return all workspaces the authenticated user has access to in your app.

POST https://api.thebridge.dev/auth/token/workspace-list

Body Parameters

ParameterTypeRequiredDescription
accessTokenstringRequiredA valid user access token

An array of workspaces. Each entry’s id is the user’s membership ID in that workspace (the tenantUserId), usable with workspace switch below.

Request example

curl --request POST 'https://api.thebridge.dev/auth/token/workspace-list' \
--header 'Content-Type: application/json' \
--data-raw '{ "accessToken": "XXXX" }'

Response example:

[
    {
        "id": "63d25a9e0796d40008680f9a",
        "username": "john@example.com",
        "fullName": "John Doe",
        "tenant": {
            "id": "63d25a9e0796d40008680f96",
            "name": "Johns Family",
            "logo": ""
        }
    }
]

Exchange a valid access token and a target tenantUserId for a fresh token set scoped to that workspace. The target must belong to the same user, otherwise the call fails with HTTP 401.

POST https://api.thebridge.dev/auth/token/workspace-switch

Body Parameters

ParameterTypeRequiredDescription
accessTokenstringRequiredA valid user access token
targetTenantUserIdstringRequiredThe id of the workspace entry to switch to, from the workspace list
scopestringOptionalScope for the new tokens. Defaults to openid profile email onboarding tenant

A fresh token set scoped to the target workspace.

Request example

curl --request POST 'https://api.thebridge.dev/auth/token/workspace-switch' \
--header 'Content-Type: application/json' \
--data-raw '{
  "accessToken": "XXXX",
  "targetTenantUserId": "63d25a9e0796d40008680f9b"
}'

Response example:

{
    "access_token": "XXXX",
    "refresh_token": "XXXX",
    "token_type": "Bearer",
    "expires_in": 3600,
    "id_token": "XXXX"
}

Returns a handover code used when redirecting to or displaying The Bridge hosted views and user interactions. The code is short-lived and should be consumed immediately.

POST https://api.thebridge.dev/auth/handover/code/:YOUR_APP_ID

Body Parameters

ParameterTypeRequiredDescription
accessTokenstringRequiredA valid user access token

Returns a short-lived handover code.

Request example

curl --request POST 'https://api.thebridge.dev/auth/handover/code/YOUR_APP_ID' \
--header 'Content-Type: application/json' \
--data-raw '{ "accessToken": "XXXX" }'

Response example:

{
    "code": "XXXX"
}
POST Try it out
POST https://api.thebridge.dev/auth/handover/code/YOUR_APP_ID
Stored in session memory only. Never persisted.
The ID of your app