Skip to content

verifyToken()

async verifyToken<T extends TokenPayload = TokenPayload>(
token: string,
): Promise<VerifyResult<T>>
ParameterTypeDescription
tokenstringJWT access token from Authorization: Bearer <token> header
type VerifyResult<T> =
| { ok: true; data: T }
| { ok: false; error: SdkError };

The method never throws. All errors are returned via the Result type.

const result = await ca.verifyToken(token);
if (result.ok) {
const { userId, email, tenantId, sessionId } = result.data;
// User is authenticated
} else {
const { code, message, suggestion, docs_url } = result.error;
// Handle error — see Error Codes reference
}

Pass a custom type to extend the default payload:

interface MyPayload extends TokenPayload {
role: string;
permissions: string[];
}
const result = await ca.verifyToken<MyPayload>(token);
if (result.ok) {
console.log(result.data.role); // Type-safe access
}

The default decoded token payload:

interface TokenPayload {
userId: string; // from JWT 'sub' claim
email: string;
tenantId: string; // from JWT 'tenant_id' claim
sessionId: string; // from JWT 'sid' claim
iss: string; // Issuer: 'rakomi.com'
aud: string; // Audience
exp: number; // Expiration (Unix timestamp)
iat: number; // Issued at (Unix timestamp)
jti: string; // Unique token ID
session?: SessionMetadata; // Session expiry metadata (always present for user tokens)
token?: TokenMetadata; // Token TTL metadata
}
interface SessionMetadata {
expiresAt: string; // ISO 8601 — from JWT exp claim
maxLifetimeExpiresAt?: string; // ISO 8601 — present only for tenants with custom max lifetime policy
isExpiringSoon: boolean; // true when min(token TTL, max lifetime remaining) < 300 seconds
}
interface TokenMetadata {
expiresIn: number; // Remaining seconds until token expiry (computed at verify time, clamped to 0)
}

session.isExpiringSoon is true when the effective session lifetime (the smaller of token TTL and tenant max lifetime) is under 5 minutes. Use this to build server-side “session expiring soon” warnings without additional clock checks:

const result = await ca.verifyToken(token);
if (result.ok) {
if (result.data.session?.isExpiringSoon) {
// Add warning header for the frontend
res.setHeader('X-Session-Expiring-Soon', '1');
}
}

session.maxLifetimeExpiresAt is only present for tenants that have configured a custom session max lifetime (shorter than the platform default). Unlike token expiry, this limit cannot be extended by refresh — the user will be signed out at this absolute time.

  • Lazy initialization — JWKS is fetched on the first verifyToken() call, not at construction
  • Single-entry cache — exactly 1 JWKS entry is cached, replaced atomically on refresh
  • Cache-Control respected — the SDK honors the Cache-Control header from the JWKS response
  • Automatic refresh — when a token’s kid doesn’t match the cached key, the SDK refetches JWKS once before returning an error

The clockTolerance config option (default: 30 seconds, max: 120 seconds) allows for small clock differences between servers.

const ca = new RakomiClient({
apiKey: 'akm_live_xxx',
clockTolerance: 60, // Allow 60 seconds of clock drift
});
CodeWhen
token/expiredToken’s exp claim is in the past
token/invalid_signatureSignature doesn’t match any JWKS key
token/malformedNot a valid JWT format
token/invalid_algorithmAlgorithm is not RS256
token/missing_claimsRequired claims (sub, tenant_id, etc.) missing
token/invalid_issuerIssuer doesn’t match rakomi.com
token/not_yet_validToken’s nbf claim is in the future

See Error Codes for full details with suggestions and fix commands.