Skip to content

OAuth Helpers

The @rakomi/node SDK provides OAuth 2.0 helpers for implementing the Authorization Code flow with PKCE. All functions are available as both standalone imports and Rakomi class methods.

OAuth token operations return a VerifyResult<OAuthTokenResponse> — the SDK never throws:

type VerifyResult<T> =
| { ok: true; data: T }
| { ok: false; error: SdkError };

Always check result.ok before accessing data.


Generate a PKCE code verifier and challenge pair.

function generatePKCE(): PkceChallenge
interface PkceChallenge {
codeVerifier: string; // 43-char base64url (32 random bytes)
codeChallenge: string; // SHA-256 of verifier, base64url
codeChallengeMethod: 'S256';
}
import { generatePKCE } from '@rakomi/node';
const pkce = generatePKCE();
// Store pkce.codeVerifier in a server-side cookie for the callback
// Send pkce.codeChallenge in the authorize URL

Generate a random state parameter for CSRF protection.

function generateState(): string

Returns 32 random bytes, hex-encoded (64 characters).

import { generateState } from '@rakomi/node';
const state = generateState();
// Store in a server-side cookie, compare on callback

Build a full /oauth/authorize URL with all required parameters.

function buildAuthorizeUrl(options: AuthorizeUrlOptions): string
ParameterTypeRequiredDescription
clientIdstringYesOAuth client ID
redirectUristringYesCallback URL
codeChallengestringYesPKCE code challenge
statestringYesCSRF state parameter
scopestring | string[]NoScopes (default: 'openid profile email')
baseUrlstringNoAuth server URL (default: 'https://api.rakomi.com')
import { buildAuthorizeUrl, generatePKCE, generateState } from '@rakomi/node';
const pkce = generatePKCE();
const state = generateState();
const url = buildAuthorizeUrl({
clientId: 'your_client_id',
redirectUri: 'https://app.example.com/callback',
codeChallenge: pkce.codeChallenge,
state,
});
// Redirect user to `url`

Exchange an authorization code for tokens.

async function exchangeCode(
options: OAuthExchangeOptions,
): Promise<VerifyResult<OAuthTokenResponse>>
ParameterTypeRequiredDescription
codestringYesAuthorization code from callback
codeVerifierstringYesPKCE verifier stored during authorize
redirectUristringYesMust match the authorize request
clientIdstringYesOAuth client ID
clientSecretstringYesOAuth client secret
baseUrlstringNoAPI URL (default: 'https://api.rakomi.com')
interface OAuthTokenResponse {
access_token: string;
token_type: string; // "Bearer"
expires_in: number; // seconds (default: 900)
refresh_token?: string;
scope?: string;
}
CodeCauseAction
oauth/invalid_grantCode expired, already used, or bad verifierRequest new authorization
oauth/invalid_clientBad client_id or client_secretCheck credentials
oauth/invalid_requestMissing or malformed parametersCheck request format
oauth/network_errorNetwork failureRetry with backoff
oauth/missing_client_idclientId not providedAdd to config or options
oauth/missing_client_secretclientSecret not providedAdd to config or options
import { exchangeCode } from '@rakomi/node';
const result = await exchangeCode({
code: callbackCode,
codeVerifier: storedVerifier,
redirectUri: 'https://app.example.com/callback',
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
});
if (!result.ok) {
switch (result.error.code) {
case 'oauth/invalid_grant':
// Code expired or already used — redirect to login
break;
case 'oauth/invalid_client':
// Bad credentials — check config
break;
case 'oauth/network_error':
// Transient — retry
break;
}
return;
}
const { access_token, refresh_token } = result.data;

Refresh an OAuth access token. Automatically serializes concurrent calls with the same refresh token to prevent accidental session revocation.

async function refreshToken(
options: OAuthRefreshOptions,
): Promise<VerifyResult<OAuthTokenResponse>>
ParameterTypeRequiredDescription
refreshTokenstringYesRefresh token from previous exchange
clientIdstringYesOAuth client ID
clientSecretstringYesOAuth client secret
baseUrlstringNoAPI URL (default: 'https://api.rakomi.com')

Rakomi implements refresh token rotation with reuse detection. If a refresh token is used twice (e.g., from concurrent requests), all user sessions are revoked (nuclear revocation). The SDK prevents this by deduplicating concurrent calls — only one HTTP request is made, and both callers receive the same result.

import { refreshToken } from '@rakomi/node';
const result = await refreshToken({
refreshToken: storedRefreshToken,
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
});
if (result.ok) {
// Store new tokens — refresh token may have rotated
saveTokens(result.data.access_token, result.data.refresh_token);
}

All OAuth functions are also available as class methods with auto-filled config:

import { Rakomi } from '@rakomi/node';
const ca = new RakomiClient({
apiKey: 'akm_live_xxx',
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
});
// PKCE and state — no config needed
const pkce = ca.generatePKCE();
const state = ca.generateState();
// URL — auto-fills clientId and baseUrl from config
const url = ca.buildAuthorizeUrl({
redirectUri: 'https://app.example.com/callback',
codeChallenge: pkce.codeChallenge,
state,
});
// Exchange — auto-fills clientId, clientSecret, baseUrl
const result = await ca.exchangeCode({
code,
codeVerifier: pkce.codeVerifier,
redirectUri: 'https://app.example.com/callback',
});