Skip to main content
These functions are for registered Ave apps (apps with a clientId from the developer portal). If you haven’t registered yet, start with Quick Ave or the quickstart.
bun add @ave-id/sdk

Import paths

ImportContentsRuntime
@ave-id/sdkCore OAuth, PKCE helpers, verifyJwt, fetchJwksBrowser or Node
@ave-id/sdk/clientstartPkceLogin, finishPkceLogin, signIn, signInWithFedCm — browser redirect + FedCM helpersBrowser only
@ave-id/sdk/expo-sessionconfigureAveSdkForExpo, completeExpoOAuthCallback, AveSession, SecureStore adapter, wireAveSessionToConvex — React Native / Expo (needs expo-crypto for PKCE)Expo
@ave-id/sdk/nextAveSessionProvider, useAveSession, AveConvexBridge — Next.js App Router (peer: react)Browser
@ave-id/sdk/serverexchangeCodeServer, refreshTokenServer, verifyJwt — uses clientSecretServer only
Never import from @ave-id/sdk/server in browser code. Those functions require your clientSecret, which must never be exposed to the client.

Config object

Most functions take a config object as the first argument:
interface AveConfig {
  clientId: string;     // Your app's client ID
  redirectUri: string;  // Registered redirect URI
  issuer?: string;      // Base URL for Ave user-facing pages (default: "https://aveid.net"). Do not set this to the API host "https://api.aveid.net".
}

PKCE helpers

generateCodeVerifier()

Generates a cryptographically random PKCE code verifier.
function generateCodeVerifier(): string
Returns a 43-character base64url-encoded random string. Store this in sessionStorage before redirecting and send it as codeVerifier when exchanging the code.
const verifier = generateCodeVerifier();
sessionStorage.setItem("ave_verifier", verifier);

generateCodeChallenge(verifier)

Generates the PKCE code challenge from a verifier using SHA-256.
function generateCodeChallenge(verifier: string): Promise<string>
verifier
string
required
The code verifier from generateCodeVerifier().
Returns Promise<string> — the base64url-encoded SHA-256 hash. Pass this as codeChallenge to buildAuthorizeUrl.
const challenge = await generateCodeChallenge(verifier);

generateNonce()

Generates a random nonce for ID token replay protection.
function generateNonce(): string
Returns a 32-character base64url-encoded random string. Pass it as nonce to buildAuthorizeUrl and verify it matches the nonce claim in the id_token after exchange.

Authorization functions

buildAuthorizeUrl(config, params)

Builds the URL to redirect users to Ave for sign-in.
function buildAuthorizeUrl(
  config: AveConfig,
  params?: {
    scope?: Scope[];
    state?: string;
    nonce?: string;
    codeChallenge?: string;
    codeChallengeMethod?: "S256" | "plain";
    organizationId?: string;
    extraParams?: Record<string, string>;
  }
): string
config.clientId
string
required
Your app’s client ID.
config.redirectUri
string
required
The registered redirect URI the user will be sent to after authorization.
params.scope
Scope[]
default:"[\"openid\", \"profile\", \"email\"]"
Scopes to request. Available values: "openid", "profile", "email", "offline_access", "user_id".
params.state
string
CSRF protection token. Generate with crypto.randomUUID(). Store and verify on the callback.
params.nonce
string
ID token replay protection. Generate with generateNonce(). Verify against the nonce claim in id_token.
params.codeChallenge
string
PKCE challenge from generateCodeChallenge(). Required for public clients.
params.organizationId
string
Optional Ave Business organization ID. When present, Ave verifies that the selected identity belongs to the organization and includes org_id, org_role, org_scopes, org_signing_authority, org_encryption_mode, org_key_custody, and SSO auth metadata in issued tokens. If the organization requires SSO, the current Ave session must come from that organization’s active SSO connection.
params.extraParams
object
Extra query parameters on the Ave sign-in URL. For encrypted invites, set wrapped_key to the value from encodeWrappedPayloadParam() — see Identity keys & wrapped payloads.
const url = buildAuthorizeUrl(
  { clientId: "YOUR_CLIENT_ID", redirectUri: "https://yourapp.com/callback" },
  {
    scope: ["openid", "profile", "email", "offline_access"],
    state: crypto.randomUUID(),
    nonce: generateNonce(),
    codeChallenge: challenge,
    codeChallengeMethod: "S256",
  }
);
window.location.href = url;

exchangeCode(config, payload)

Exchanges an authorization code for tokens. Use for PKCE (public client) flows.
function exchangeCode(
  config: AveConfig,
  payload: { code: string; codeVerifier?: string }
): Promise<TokenResponse>
payload.code
string
required
The authorization code from the callback URL.
payload.codeVerifier
string
The PKCE code verifier from sessionStorage. Required if you used PKCE.
const tokens = await exchangeCode(
  { clientId: "YOUR_CLIENT_ID", redirectUri: "https://yourapp.com/callback" },
  { code, codeVerifier }
);

refreshToken(config, payload)

Exchanges a refresh token for a new set of tokens.
function refreshToken(
  config: AveConfig,
  payload: { refreshToken: string }
): Promise<TokenResponse>
payload.refreshToken
string
required
The refresh token from a previous token response.
Refresh tokens rotate on every use — each successful refresh returns a new refresh token and invalidates the old one. Store the new token immediately after every refresh. Sending an old token returns invalid_grant and may revoke the entire token family.

fetchUserInfo(config, accessToken)

Fetches live identity claims from the userinfo endpoint.
function fetchUserInfo(config: AveConfig, accessToken: string): Promise<UserInfo>
accessToken
string
required
The opaque access_token (not the JWT) from the token response.
Returns UserInfo with sub, name?, preferred_username?, email?, picture?, and organization? when the token was issued for an Ave Business workspace.

listAveWorkspaceOrganizations(config, accessToken)

Lists Ave Business organizations that the token identity can use. Use it to build a workspace picker before requesting organization context.
function listAveWorkspaceOrganizations(
  config: { issuer?: string; clientId?: string; fetcher?: typeof fetch },
  accessToken: string
): Promise<AveWorkspaceOrganization[]>
import { listAveWorkspaceOrganizations } from "@ave-id/sdk";

const organizations = await listAveWorkspaceOrganizations(
  { clientId: "YOUR_CLIENT_ID" },
  tokens.access_token
);
Pass the selected organization’s id as organizationId to startPkceLogin() or buildAuthorizeUrl().

createAveWorkspaceOrganization(config, accessToken, params)

Creates an Ave Business workspace for the token identity. Call this only after your UI tells the user that it will create an Ave-managed workspace.
function createAveWorkspaceOrganization(
  config: { issuer?: string; clientId?: string; fetcher?: typeof fetch },
  accessToken: string,
  params: {
    name: string;
    clientId?: string;
    userConfirmedAveWorkspaceCreation: true;
  }
): Promise<AveWorkspaceOrganization>
import { createAveWorkspaceOrganization } from "@ave-id/sdk";

const organization = await createAveWorkspaceOrganization(
  { clientId: "YOUR_CLIENT_ID" },
  tokens.access_token,
  {
    name: "Example Co",
    userConfirmedAveWorkspaceCreation: true,
  }
);
The returned organization.id is the organizationId for the follow-up workspace sign-in.

Client helpers

Import from @ave-id/sdk/client. Browser-only.

supportsFedCm()

Checks whether the current browser environment is eligible to attempt FedCM.
function supportsFedCm(): boolean

signInWithFedCm(params)

Attempts a browser-native FedCM sign-in and returns tokens immediately without a redirect callback.
async function signInWithFedCm(params: {
  clientId: string;
  redirectUri: string;
  scope?: string;
  issuer?: string;
  state?: string;
  nonce?: string;
  mediation?: CredentialMediationRequirement;
}): Promise<FedCmTokenResponse>
Ave returns the normal token payload. For E2EE apps, Ave continues in a FedCM dialog so the existing Ave authorization UI can unlock the user’s master key and prepare the plaintext app_key handoff before resuming automatically. FedCM does not claim Ave Business organization-context support. Use signIn() or startPkceLogin() with organizationId when signing into a business workspace.

signIn(params)

Smart sign-in helper for browser apps. It tries FedCM first when available, and falls back to the standard PKCE redirect flow when it isn’t.
async function signIn(params: {
  clientId: string;
  redirectUri: string;
  scope?: string;
  issuer?: string;
  state?: string;
  nonce?: string;
  organizationId?: string;
  mediation?: CredentialMediationRequirement;
  preferFedCm?: boolean;
}): Promise<FedCmTokenResponse | null>
Returns:
  • FedCmTokenResponse when FedCM completes in-page
  • null when the helper falls back to startPkceLogin() and redirects the browser
When organizationId is present, signIn() uses the PKCE redirect flow so Ave can verify organization membership and SSO policy before issuing tokens.
import { finishPkceLogin, signIn } from "@ave-id/sdk/client";

const result = await signIn({
  clientId: "YOUR_CLIENT_ID",
  redirectUri: "https://yourapp.com/callback",
  scope: "openid profile email offline_access",
});

if (result) {
  // FedCM completed immediately
} else {
  // Browser redirected for PKCE fallback
}

// On your callback page:
const tokens = await finishPkceLogin({
  clientId: "YOUR_CLIENT_ID",
  redirectUri: "https://yourapp.com/callback",
});

startPkceLogin(params)

Generates PKCE params, saves them to sessionStorage, and redirects the user to Ave. The browser-friendly alternative to calling generateCodeVerifier + buildAuthorizeUrl + window.location.href manually.
async function startPkceLogin(params: {
  clientId: string;
  redirectUri: string;
  scope?: string;
  issuer?: string;
  state?: string;   // optional — auto-generated if omitted
  nonce?: string;   // optional — auto-generated if omitted
  organizationId?: string;
}): Promise<void>
Automatically generates a PKCE verifier/challenge, a nonce for ID token replay protection, and a state value for CSRF protection — unless you supply your own. All three are stored in sessionStorage and retrieved automatically by finishPkceLogin. Set organizationId to sign into an Ave Business organization as an app workspace. Ave verifies membership before issuing tokens and includes organization claims in the returned JWTs. See Business workspaces.

finishPkceLogin(options)

Handles the OAuth callback: reads stored PKCE state, verifies the returned state against the stored value, exchanges the authorization code, verifies the returned tokens, and cleans up sessionStorage. Returns null when no code parameter is present — safe to call on every page load.
async function finishPkceLogin(options: {
  clientId: string;
  redirectUri: string;
  issuer?: string;
  url?: string;        // override the callback URL to parse (default: window.location.href)
  cleanUrl?: boolean;  // remove code/state from the URL after exchange (default: true)
}): Promise<TokenResponse | null>
import { startPkceLogin, finishPkceLogin } from "@ave-id/sdk/client";

// On your login page:
await startPkceLogin({
  clientId: "YOUR_CLIENT_ID",
  redirectUri: "https://yourapp.com/callback",
  scope: "openid profile email offline_access",
});

// On your /callback page:
const tokens = await finishPkceLogin({
  clientId: "YOUR_CLIENT_ID",
  redirectUri: "https://yourapp.com/callback",
});

if (tokens) {
  // User is signed in — tokens.id_token, tokens.access_token, etc.
}

JWT verification

These functions are available in @ave-id/sdk, @ave-id/sdk/client, and @ave-id/sdk/server.

verifyJwt(token, options?)

Verifies an Ave-issued JWT: parses the token, fetches the OIDC discovery document, validates the RS256 signature against the JWKS, and checks issuer, expiry, audience, and nonce claims. Returns the decoded payload on success or null on any validation failure.
async function verifyJwt<T extends JwtPayload = AveJwtClaims>(
  token: string,
  options?: VerifyJwtOptions,
): Promise<T | null>
interface VerifyJwtOptions {
  issuer?: string;             // Base issuer URL (default: "https://aveid.net")
  expectedIssuer?: string;     // Exact issuer string to validate iss claim against
  audience?: string | string[]; // Expected aud claim (e.g. your clientId)
  nonce?: string;              // Expected nonce claim (replay protection)
  jwksUrl?: string;            // Override JWKS endpoint URL
  jwks?: JwksResponse;         // Pre-fetched JWKS (skips network call)
  discoveryUrl?: string;       // Override OIDC discovery endpoint
  clockSkewSeconds?: number;   // Clock tolerance for exp/iat checks (default: 30)
  fetcher?: typeof fetch;      // Custom fetch implementation
}
token
string
required
The raw JWT string to verify.
options.audience
string | string[]
Expected aud claim. Pass your clientId when verifying id_token. Pass "https://aveid.net" when verifying access_token_jwt.
options.nonce
string
Expected nonce claim. Pass the nonce you sent in the authorization request to prevent replay attacks.
options.clockSkewSeconds
number
default:"30"
Tolerance in seconds applied to exp and nbf checks to handle clock drift between servers.
Returns Promise<T | null> — the decoded payload typed as T, or null if any check fails (invalid signature, expired, issuer mismatch, audience mismatch, nonce mismatch, or malformed token).
import { verifyJwt } from "@ave-id/sdk";

// Verify an id_token on your server after code exchange:
const claims = await verifyJwt(tokens.id_token, {
  audience: "YOUR_CLIENT_ID",
  nonce: savedNonce,
});

if (!claims) {
  throw new Error("Invalid id_token");
}

// claims.sub — identity UUID
// claims.email, claims.name, etc.
verifyJwt fetches the OIDC discovery document and JWKS automatically and caches both for 5 minutes. For server environments with no global fetch, pass a fetcher option.

fetchJwks(options?)

Fetches and caches the Ave JWKS (JSON Web Key Set). Used internally by verifyJwt. Useful if you need direct access to the signing keys.
async function fetchJwks(options?: {
  issuer?: string;
  jwksUrl?: string;
  fetcher?: typeof fetch;
}): Promise<JwksResponse>
import { fetchJwks } from "@ave-id/sdk";

const { keys } = await fetchJwks();
// keys: JwkKey[] — RSA public keys used to verify Ave JWTs

Server helpers

Import from @ave-id/sdk/server. Server runtime only — requires your clientSecret.

exchangeCodeServer(config, payload)

Same as exchangeCode but uses clientSecret instead of codeVerifier. Use this for confidential clients (server-side token exchange).
async function exchangeCodeServer(
  config: { clientId: string; clientSecret: string; redirectUri: string; issuer?: string },
  payload: { code: string }
): Promise<TokenResponse>
import { exchangeCodeServer } from "@ave-id/sdk/server";

const tokens = await exchangeCodeServer(
  {
    clientId: process.env.AVE_CLIENT_ID!,
    clientSecret: process.env.AVE_CLIENT_SECRET!,
    redirectUri: "https://yourapp.com/callback",
  },
  { code }
);

refreshTokenServer(config, payload)

Refreshes tokens using client secret (confidential client).
async function refreshTokenServer(
  config: { clientId: string; clientSecret: string; redirectUri: string; issuer?: string },
  payload: { refreshToken: string }
): Promise<TokenResponse>

TokenResponse

The shape returned by exchangeCode, exchangeCodeServer, refreshToken, and refreshTokenServer:
interface TokenResponse {
  access_token: string;      // Opaque bearer token for Ave API endpoints
  access_token_jwt: string;  // Signed Ave API JWT (aud: https://aveid.net)
  id_token?: string;         // OIDC JWT (aud: your clientId) — requires openid scope
  refresh_token?: string;    // Rotation token — requires offline_access scope
  token_type: "Bearer";
  expires_in: number;        // Seconds until access_token expires
  scope: string;             // Space-separated granted scopes
  user_id?: string;          // Present when user_id scope is granted
  user?: {                   // May be absent depending on scopes/config
    id: string;
    handle: string;
    displayName: string;
    email?: string;
    avatarUrl?: string;
  } | null;
}

Error handling

All SDK functions throw an Error when the server returns a non-2xx response. The error message is the error field from the response body (e.g. "invalid_grant", "invalid_client").
try {
  const tokens = await exchangeCode(config, { code, codeVerifier });
} catch (err) {
  if (err.message === "invalid_grant") {
    // Code expired or PKCE verifier mismatch
  }
}

See also

Last modified on May 23, 2026