Skip to main content

Overview

The authorization code flow with PKCE (Proof Key for Code Exchange) is the recommended method for authenticating users with Ave. This flow ensures secure authentication even for public clients like single-page applications and mobile apps.

Flow Diagram

1

Generate PKCE parameters

Create a code verifier and code challenge to secure the authorization flow.
import { generateCodeVerifier, generateCodeChallenge } from 'ave-sdk';

const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

// Store codeVerifier securely (sessionStorage, localStorage, etc.)
sessionStorage.setItem('pkce_verifier', codeVerifier);
2

Build authorization URL

Construct the authorization URL with your app’s configuration and PKCE parameters.
import { buildAuthorizeUrl } from 'ave-sdk';

const config = {
  clientId: 'your_client_id',
  redirectUri: 'https://yourapp.com/callback'
};

const authUrl = buildAuthorizeUrl(config, {
  scope: ['openid', 'profile', 'email', 'offline_access'],
  codeChallenge,
  codeChallengeMethod: 'S256',
  state: crypto.randomUUID(), // CSRF protection
  nonce: crypto.randomUUID()  // Replay protection
});
3

Redirect user to Ave

Redirect the user to the authorization URL. They will see the Ave authorization screen.
window.location.href = authUrl;
The user will be redirected to:
https://aveid.net/signin?client_id=...&redirect_uri=...&scope=...&code_challenge=...&code_challenge_method=S256&state=...&nonce=...
4

User authorizes your app

The user sees your app’s information and selects which Ave identity to use for authorization. After granting permission, Ave redirects back to your redirect_uri with an authorization code.
5

Handle the callback

Extract the authorization code and state from the callback URL.
// On your callback page (e.g., /callback)
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');

// Verify state matches what you sent (CSRF protection)
if (state !== expectedState) {
  throw new Error('State mismatch - possible CSRF attack');
}

// Retrieve the code verifier
const codeVerifier = sessionStorage.getItem('pkce_verifier');
if (!codeVerifier) {
  throw new Error('Code verifier not found');
}
6

Exchange code for tokens

Exchange the authorization code for access tokens using the code verifier.
import { exchangeCode } from 'ave-sdk';

const tokens = await exchangeCode(config, {
  code,
  codeVerifier
});

// Clean up
sessionStorage.removeItem('pkce_verifier');

// Store tokens securely
console.log('Access token:', tokens.access_token);
console.log('ID token:', tokens.id_token);
console.log('Refresh token:', tokens.refresh_token);
console.log('User:', tokens.user);

Authorization Endpoint

POST /api/oauth/authorize

This endpoint requires user authentication and creates an authorization code. Headers:
Authorization: Bearer {session_token}
Content-Type: application/json
Request Body:
{
  clientId: string;                                    // Your OAuth app client ID
  redirectUri: string;                                 // Must match registered URI
  scope?: string;                                      // Space-separated scopes (default: "profile")
  state?: string;                                      // CSRF protection token
  identityId: string;                                  // UUID of user's identity to use
  codeChallenge?: string;                              // PKCE code challenge
  codeChallengeMethod?: "S256" | "plain";              // Challenge method (use S256)
  encryptedAppKey?: string;                            // E2EE: encrypted app key
  nonce?: string;                                      // Replay protection
  connector?: boolean;                                 // Enable delegation flow
  requestedResource?: string;                          // Resource for delegation
  requestedScope?: string;                             // Scope for delegation
  communicationMode?: "user_present" | "background";   // Delegation mode
}
Response:
{
  "redirectUrl": "https://yourapp.com/callback?code=abc123&state=xyz789"
}
Error Responses:
  • 400 - Invalid client_id, redirect_uri, or scope
  • 400 - Identity doesn’t belong to user
  • 400 - E2EE app requires encryptedAppKey

SDK Functions

generateCodeVerifier()

Generates a cryptographically random code verifier for PKCE.
function generateCodeVerifier(): string
Example:
const verifier = generateCodeVerifier();
// Returns: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

generateCodeChallenge()

Generates a code challenge from a code verifier using SHA-256.
function generateCodeChallenge(verifier: string): Promise<string>
Example:
const challenge = await generateCodeChallenge(verifier);
// Returns: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

generateNonce()

Generates a random nonce for replay protection.
function generateNonce(): string
Example:
const nonce = generateNonce();

buildAuthorizeUrl()

Constructs the authorization URL for redirecting users.
function buildAuthorizeUrl(
  config: AveConfig,
  params?: {
    scope?: Scope[];
    state?: string;
    nonce?: string;
    codeChallenge?: string;
    codeChallengeMethod?: "S256" | "plain";
    extraParams?: Record<string, string>;
  }
): string
Example:
const url = buildAuthorizeUrl(
  { clientId: 'app123', redirectUri: 'https://app.com/callback' },
  {
    scope: ['openid', 'profile', 'email'],
    codeChallenge: challenge,
    codeChallengeMethod: 'S256',
    state: 'random_state'
  }
);

exchangeCode()

Exchanges an authorization code for access tokens.
function exchangeCode(
  config: AveConfig,
  payload: {
    code: string;
    codeVerifier?: string;
  }
): Promise<TokenResponse>
Example:
const tokens = await exchangeCode(
  { clientId: 'app123', redirectUri: 'https://app.com/callback' },
  { code: 'auth_code_here', codeVerifier: verifier }
);

Security Best Practices

Even for confidential clients, PKCE adds an extra layer of security. Always use S256 as the code challenge method, never plain.
The state parameter protects against CSRF attacks. Generate a random value, store it securely, and verify it matches when handling the callback.
When requesting the openid scope, include a nonce parameter to prevent replay attacks. Verify the nonce claim in the ID token matches your original value.
The code verifier is sensitive. Store it in sessionStorage or a secure cookie, never in localStorage or URL parameters.
Only use redirect URIs that are registered with your OAuth app. Ave will reject authorization requests with unregistered URIs.

Common Errors

Error CodeDescriptionSolution
invalid_clientClient ID not foundVerify your clientId is correct
invalid_scopeRequested scope not allowedCheck your app’s allowed scopes
invalid_grantAuthorization code invalid/expiredCodes expire after 10 minutes
invalid_requestMissing required parameterCheck all required fields are provided

Next Steps

Token Exchange

Learn how to refresh tokens and handle token expiration

Build docs developers (and LLMs) love