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:
- You initiate the login process by redirecting the user to the
/authorizeendpoint (or the simpler shorthand/url/loginand/url/signupendpoints). - The user is pulled through an authentication process provided by The Bridge with cloud views.
- Once the user is authenticated and has selected the workspace to access, the user is redirected back to your app with a code.
- You make a call to the
/tokenendpoint with this code to exchange it for access tokens and user profile information (OpenID). - You can verify that the tokens are valid and safe to trust by using the public keys available from the
/.well-knownendpoints. - You can refresh the tokens using the
/tokenendpoint to obtain up-to-date access and profile information.
Starting a login
Section titled “Starting a login”Authorize
Section titled “Authorize”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.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/authorize
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
client_id | string | Required | Your app ID |
response_type | string | Required | What kind of tokens will be generated. We support code |
redirect_uri | string | Required | A target URI where the authenticated user will be redirected to together with tokens. Must be a valid URI in your App redirectUris |
scope | string | Required | Any or all of: openid, profile, email, address, phone, onboarding, tenant |
state | string | Optional | Used to resume a state in your app. The state will be available in the response code |
signup | boolean | Optional | Set to true to initiate a signup instead of the default login flow |
signup_plan | string | Optional | The key to an existing plan. Allows the signup to end with the new workspace subscribing to a specific plan |
force_federation | string | Optional | Force a certain federated login flow: ms-azure-ad, google, or saml |
federation_connection | string | Optional | Specify 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-keyheader is required; use your App ID in theclient_idquery 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%20tenantShorthand Authorize (Login)
Section titled “Shorthand Authorize (Login)”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.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/url/login/:YOUR_APP_ID
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
redirectUri | string | Optional | Target URI for redirect after auth. Defaults to your App defaultCallbackUri |
state | string | Optional | Resume a state in your app |
responseType | string | Optional | code or id_token |
forceFederation | string | Optional | ms-azure-ad, google, or saml |
federationConnection | string | Optional | Federation 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-keyheader is required; the App ID is in the URL path.
Try it
GET Try it out
https://api.thebridge.dev/auth/url/login/:YOUR_APP_IDShorthand Signup
Section titled “Shorthand Signup”Use the shorthand endpoint /url/signup to initiate the signup flow.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/url/signup/:YOUR_APP_ID
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
redirectUri | string | Optional | Target URI for redirect after auth. Defaults to your App defaultCallbackUri |
state | string | Optional | Resume a state in your app |
signupPlan | string | Optional | Key to an existing plan for the new workspace to subscribe to |
signupCurrency | string | Optional | Currency matching one of the prices in the plan |
signupRecurrenceInterval | string | Optional | Interval 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-keyheader is required; the App ID is in the URL path.
Try it
GET Try it out
https://api.thebridge.dev/auth/url/signup/:YOUR_APP_IDShorthand Logout
Section titled “Shorthand Logout”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).
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/url/logout/:YOUR_APP_ID
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
redirect_uri | string | Optional | Where the user should land after a subsequent re-login |
Response HTTP 302
Section titled “Response HTTP 302”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'Getting and refreshing tokens
Section titled “Getting and refreshing tokens”Get Tokens
Section titled “Get Tokens”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.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/token
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
client_id | string | Required | Your app ID |
grant_type | string | Required | authorization_code or refresh_token |
code | string | Optional | Required for authorization_code grant. The code received from the authenticated user |
redirect_uri | string | Optional | Required for authorization_code grant. Must be a valid URI in your App redirectUris |
refresh_token | string | Optional | Required for refresh_token grant. The refresh token issued previously |
Response HTTP 200
Section titled “Response HTTP 200”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
https://api.thebridge.dev/auth/tokenShorthand Get Tokens
Section titled “Shorthand Get Tokens”Get new tokens using a simplified endpoint with fewer parameters.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/token/:grantType/:YOUR_APP_ID
grantType is code or refresh.
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Optional | Required for code grant. The authorization code |
refreshToken | string | Optional | Required for refresh grant. The refresh token |
redirectUri | string | Optional | Optional for code grant. Defaults to your App defaultCallbackUri |
Response HTTP 200
Section titled “Response HTTP 200”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": ""
}
}
Refresh with an Access Token
Section titled “Refresh with an Access Token”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.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/refresh-token
Headers: Authorization: Bearer USER_ACCESS_TOKEN
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
scope | string | Optional | Narrow the scope of the new tokens. Defaults to the scope of the presented access token |
Response HTTP 200
Section titled “Response HTTP 200”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"
}
}
Verifying tokens
Section titled “Verifying tokens”OpenID Connect Discovery
Section titled “OpenID Connect Discovery”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.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/.well-known/openid-configuration
Response HTTP 200
Section titled “Response HTTP 200”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"]
}
JSON Web Key Set
Section titled “JSON Web Key Set”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.
HTTP Request
Section titled “HTTP Request”GET https://api.thebridge.dev/auth/.well-known/jwks.json
Response HTTP 200
Section titled “Response HTTP 200”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"
}
]
}
Revoking tokens
Section titled “Revoking tokens”Revoke Refresh Token
Section titled “Revoke Refresh Token”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.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/auth/revoke
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
refreshToken | string | Required | The refresh token to revoke |
Response HTTP 200
Section titled “Response HTTP 200”{ "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
}
Workspace tokens
Section titled “Workspace tokens”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.
List Workspaces
Section titled “List Workspaces”Return all workspaces the authenticated user has access to in your app.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/token/workspace-list
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
accessToken | string | Required | A valid user access token |
Response HTTP 200
Section titled “Response HTTP 200”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": ""
}
}
]
Switch Workspace
Section titled “Switch Workspace”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.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/token/workspace-switch
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
accessToken | string | Required | A valid user access token |
targetTenantUserId | string | Required | The id of the workspace entry to switch to, from the workspace list |
scope | string | Optional | Scope for the new tokens. Defaults to openid profile email onboarding tenant |
Response HTTP 200
Section titled “Response HTTP 200”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"
}
Handover
Section titled “Handover”Handover Code
Section titled “Handover Code”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.
HTTP Request
Section titled “HTTP Request”POST https://api.thebridge.dev/auth/handover/code/:YOUR_APP_ID
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
accessToken | string | Required | A valid user access token |
Response HTTP 200
Section titled “Response HTTP 200”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
https://api.thebridge.dev/auth/handover/code/YOUR_APP_ID