Tokens & Claims
SingleSign issues three types of tokens during the OAuth 2.0 / OpenID Connect flow. Understanding what each token contains and how to use it is essential for building a secure integration.
Token Types Overview
| Token | Format | Purpose | Lifetime |
|---|---|---|---|
| Access Token | JWT (signed with HS512) | Authorize API requests (e.g., call /oauth/userinfo) | Short-lived (typically 1 hour) |
| ID Token | JWT (signed with HS512) | Assert the user's identity to your application | Short-lived (typically 1 hour) |
| Refresh Token | Opaque string | Obtain new access tokens without re-authentication | Long-lived (days to weeks) |
Access Token
The access token is a JSON Web Token (JWT) that your application sends as a Bearer token to access protected resources.
Structure
A JWT consists of three base64url-encoded parts separated by dots:
HEADER.PAYLOAD.SIGNATURE
Header:
{
"alg": "HS512",
"typ": "JWT"
}
Payload (claims):
{
"iss": "https://api.singlesign.com",
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"human_guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"aud": "your-client-id",
"iat": 1739996400,
"exp": 1740000000,
"scope": "openid profile email",
"client_id": "your-client-id",
"platform_guid": "your-platform-guid"
}
Key Claims
| Claim | Type | Description |
|---|---|---|
iss | string | Issuer — always https://api.singlesign.com. |
sub | string | Subject — the unique user identifier. Same value as human_guid. |
human_guid | string | SingleSign's primary user identifier. Use this as the foreign key in your database. |
aud | string | Audience — your application's client_id. |
iat | number | Issued-at time (Unix timestamp). |
exp | number | Expiration time (Unix timestamp). Reject the token if exp < now. |
scope | string | Space-separated list of granted scopes. |
client_id | string | The client ID the token was issued to. |
platform_guid | string | The platform this token belongs to. |
Using the Access Token
Send it as a Bearer token in the Authorization header:
curl -H "Authorization: Bearer eyJhbGciOiJIUzUxMiIs..." \
"https://api.singlesign.com/oauth/userinfo"
const response = await fetch("https://api.singlesign.com/oauth/userinfo", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
ID Token
The ID token is a JWT that asserts who the user is. It is intended for your application only and should not be sent to APIs.
When to Use the ID Token
- Extract user identity (
human_guid,name,email) immediately after login. - Verify the user's identity without an additional network call to
/oauth/userinfo. - Populate a session with the user's profile data.
ID Token Claims
The ID token includes all standard OIDC claims based on the requested scopes:
{
"iss": "https://api.singlesign.com",
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"human_guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"aud": "your-client-id",
"iat": 1739996400,
"exp": 1740000000,
"nonce": "random-nonce-value",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"email": "jane@example.com",
"email_verified": true,
"picture": "https://api.singlesign.com/avatars/jane.jpg"
}
Validating the ID Token
Always validate the ID token before trusting its claims:
- Decode the JWT and verify the header's
algisHS512. - Verify the signature using the JWKS endpoint or your client secret.
- Check
issequalshttps://api.singlesign.com. - Check
audequals yourclient_id. - Check
expis in the future. - Check
iatis not unreasonably far in the past.
import { jwtVerify } from "jose";
async function validateIdToken(idToken: string, clientSecret: string): Promise<Record<string, unknown>> {
const secret = new TextEncoder().encode(clientSecret);
const { payload } = await jwtVerify(idToken, secret, {
issuer: "https://api.singlesign.com",
audience: "your-client-id",
algorithms: ["HS512"],
});
return payload;
}
Refresh Token
The refresh token is an opaque string (not a JWT) that your application uses to obtain a new access token when the current one expires, without requiring the user to log in again.
Requesting a Refresh Token
Include the offline_access scope in your authorization request:
scope=openid+profile+email+offline_access
Using the Refresh Token
const response = await fetch("https://api.singlesign.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: storedRefreshToken,
client_id: "your-client-id",
platform_guid: "your-platform-guid",
}),
});
const tokens = await response.json();
// tokens.access_token — new access token
// tokens.refresh_token — new refresh token (rotate it)
Refresh Token Rotation
SingleSign may issue a new refresh token with each refresh request. Always store the latest refresh token and discard the old one. This is called refresh token rotation and limits the window of opportunity for a stolen token.
Refresh Token Security
| Do | Don't |
|---|---|
| Store in the platform keychain (mobile) or server-side session. | Store in localStorage or sessionStorage. |
| Rotate on every use. | Reuse an old refresh token after receiving a new one. |
| Revoke on logout. | Leave refresh tokens active when the user logs out. |
Token Introspection
Server-side applications can validate tokens by calling the introspection endpoint. This is useful when your API receives an access token and needs to verify its validity without decoding the JWT locally.
curl -X POST "https://api.singlesign.com/oauth/introspect" \
-u "your-client-id:your-client-secret" \
-d "token=eyJhbGciOiJIUzUxMiIs...&platform_guid=YOUR_PLATFORM_GUID"
Active token response:
{
"active": true,
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"human_guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_id": "your-client-id",
"scope": "openid profile email",
"token_type": "Bearer",
"exp": 1740000000,
"iat": 1739996400
}
Expired or revoked token response:
{
"active": false
}
Token Revocation
Revoke tokens when the user logs out or when you detect suspicious activity:
curl -X POST "https://api.singlesign.com/oauth/revoke" \
-d "token=eyJhbGciOiJIUzUxMiIs...&platform_guid=YOUR_PLATFORM_GUID"
Revocation works for both access tokens and refresh tokens. Always revoke the refresh token on logout to prevent it from being used to obtain new access tokens.
Claims Reference
Standard OIDC Claims
| Claim | Scope Required | Type | Description |
|---|---|---|---|
sub | openid | string | Unique subject identifier |
human_guid | openid | string | SingleSign unique user ID (same as sub) |
name | profile | string | Full name |
given_name | profile | string | First name |
family_name | profile | string | Last name |
middle_name | profile | string | Middle name |
nickname | profile | string | Casual name |
preferred_username | profile | string | Preferred username |
picture | profile | string | Profile picture URL |
updated_at | profile | number | Last profile update (Unix timestamp) |
email | email | string | Email address |
email_verified | email | boolean | Whether the email is verified |
phone_number | phone | string | Phone number |
phone_number_verified | phone | boolean | Whether the phone is verified |
address | address | object | Mailing address |
JWT Header & Registered Claims
| Claim | Location | Description |
|---|---|---|
alg | Header | Signing algorithm — always HS512 |
typ | Header | Token type — always JWT |
iss | Payload | Issuer — https://api.singlesign.com |
aud | Payload | Audience — your client_id |
iat | Payload | Issued-at timestamp |
exp | Payload | Expiration timestamp |
scope | Payload | Granted scopes |
Next Steps
- PKCE — Understand the proof key mechanism.
- Scopes Reference — Full scope-to-claims mapping.
- Endpoint Reference — Token, UserInfo, Introspect, and Revoke endpoints.
- Troubleshooting — Fix token-related errors.
Last updated: 2026-02-16
Related docs
- SingleSign Developer DocumentationLearn how to integrate SingleSign identity and authentication into your applications using OAuth 2.0 and OpenID Connect.
- alternative to Firebase AuthFirebase Authentication alternatives for teams leaving Google Cloud or wanting a standalone OIDC provider. Comparison table, then the real migration path.
- SaaS single sign-onSSO for SaaS applications using OAuth 2.0 and OpenID Connect: the flow to pick, the scopes to request, and a working integration path with SingleSign.
- free identity providerSingleSign pricing in one line: sign-in is free, with no monthly-active-user meter. See exactly what is included, and what SingleSign Mail costs for businesses.
- Identity provider alternativesIdentity provider alternatives, by the provider you are leaving: what actually has to change in your code, and what does not.
- Auth0 comparisonSingleSign vs Auth0 compared on the difference that matters: who owns the account. A side-by-side table, then the three cases where each one is the right call.
- SingleSign vs OktaSingleSign vs Okta: Okta is built for workforce identity inside a company, SingleSign for consumer sign-in across applications.
- Firebase Auth comparisonSingleSign vs Firebase Authentication on lock-in, portability and consent, plus the cases where staying on Firebase is right.
- Google Sign-In alternativeSingleSign vs Google Sign-In: the same one-tap convenience, without an advertising business behind the identity.
- Auth0 alternativesAuth0 alternatives compared, plus the part most listicles skip: what migrating off Auth0 actually involves, which code changes, and which does not.
- alternative to OktaOkta alternatives for teams that need customer sign-in rather than workforce identity. The table first, then what changes when you move consumer auth off Okta.
- Authentication for startupsAuthentication for startups without a monthly-active-user meter: standards-based OAuth 2.0 sign-in you will not have to replace when the product finds traction.