Skip to content

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

TokenFormatPurposeLifetime
Access TokenJWT (signed with HS512)Authorize API requests (e.g., call /oauth/userinfo)Short-lived (typically 1 hour)
ID TokenJWT (signed with HS512)Assert the user's identity to your applicationShort-lived (typically 1 hour)
Refresh TokenOpaque stringObtain new access tokens without re-authenticationLong-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

ClaimTypeDescription
issstringIssuer — always https://api.singlesign.com.
substringSubject — the unique user identifier. Same value as human_guid.
human_guidstringSingleSign's primary user identifier. Use this as the foreign key in your database.
audstringAudience — your application's client_id.
iatnumberIssued-at time (Unix timestamp).
expnumberExpiration time (Unix timestamp). Reject the token if exp < now.
scopestringSpace-separated list of granted scopes.
client_idstringThe client ID the token was issued to.
platform_guidstringThe 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:

  1. Decode the JWT and verify the header's alg is HS512.
  2. Verify the signature using the JWKS endpoint or your client secret.
  3. Check iss equals https://api.singlesign.com.
  4. Check aud equals your client_id.
  5. Check exp is in the future.
  6. Check iat is 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

DoDon'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

ClaimScope RequiredTypeDescription
subopenidstringUnique subject identifier
human_guidopenidstringSingleSign unique user ID (same as sub)
nameprofilestringFull name
given_nameprofilestringFirst name
family_nameprofilestringLast name
middle_nameprofilestringMiddle name
nicknameprofilestringCasual name
preferred_usernameprofilestringPreferred username
pictureprofilestringProfile picture URL
updated_atprofilenumberLast profile update (Unix timestamp)
emailemailstringEmail address
email_verifiedemailbooleanWhether the email is verified
phone_numberphonestringPhone number
phone_number_verifiedphonebooleanWhether the phone is verified
addressaddressobjectMailing address

JWT Header & Registered Claims

ClaimLocationDescription
algHeaderSigning algorithm — always HS512
typHeaderToken type — always JWT
issPayloadIssuer — https://api.singlesign.com
audPayloadAudience — your client_id
iatPayloadIssued-at timestamp
expPayloadExpiration timestamp
scopePayloadGranted scopes

Next Steps

Last updated: 2026-02-16

Related docs