Overview

This guide shows you how to integrate Hopae Connect with the standard OpenID Connect (OIDC) Authorization Code flow. You will redirect users to the Hopae OpenID Provider (issuer: https://connect.hopae.com), receive an authorization code on your redirect URI, and exchange it for tokens using the OIDC token endpoint. For a shorter walkthrough, start with the Quickstart Guide.

How It Works

Dashboard Configuration

Redirect URI Management

Configure your redirect URIs in the Hopae Developer Dashboard:
  1. Open your application in the dashboard.
  2. Navigate to Developer settings and add each redirect URI you expect to use (production, staging, mobile deep links, etc.).

Authorization Request Construction

Use the /auth endpoint on the issuer domain (https://sandbox.connect.hopae.com/auth for sandbox, https://connect.hopae.com/auth for production):
const authorizationEndpoint = "https://sandbox.connect.hopae.com/auth"; // Sandbox
const redirectUri = "https://localhost:3000/callback";

const params = new URLSearchParams({
  client_id: "YOUR_CLIENT_ID",
  redirect_uri: redirectUri,
  response_type: "code",
  scope: "openid idv",
});

if (codeChallenge) {
  params.set("code_challenge", codeChallenge);
  params.set("code_challenge_method", "S256");
}

const authorizationUrl = `${authorizationEndpoint}?${params.toString()}`;

Authorization Request Parameters

ParameterRequiredDescriptionExample
client_idYesYour Hopae Connect client identifier5SZdu0fn
response_typeYesMust be codecode
redirect_uriYesWhitelisted callback URIhttps://localhost:3000/callback
scopeYesInclude openid; add profile to receive normalized identity dataopenid profile
nonceRecommendedReplay protection for ID tokens (especially browser-based clients)4d9961bd-12a9-46d0-803f-aafef1bf814d
code_challengeConditionalPKCE code challenge (required for public clients)E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
code_challenge_methodConditionalMust be S256 when code_challenge is providedS256
promptOptionalForce a specific UX path (login, consent, select_account)login

PKCE Support

PKCE (Proof Key for Code Exchange) is strongly recommended for native and SPA clients.
function base64UrlEncode(buffer) {
  return btoa(String.fromCharCode(...buffer))
    .replace(/=+/g, "")
    .replace(/\+/g, "-")
    .replace(/\//g, "_");
}

export function generateVerifier() {
  const randomBuffer = crypto.getRandomValues(new Uint8Array(32));
  return base64UrlEncode(randomBuffer);
}

export async function generateChallenge(verifier) {
  const data = new TextEncoder().encode(verifier);
  const digest = await crypto.subtle.digest("SHA-256", data);
  return base64UrlEncode(new Uint8Array(digest));
}

const codeVerifier = generateVerifier();
const codeChallenge = await generateChallenge(codeVerifier);
sessionStorage.setItem("code_verifier", codeVerifier);
Store the code_verifier securely (session storage, encrypted cookie, etc.) so you can supply it during the token exchange.

Callback Handling

After the user completes verification, Hopae redirects to your redirect_uri with either an authorization code or an error. Success Response:
https://localhost:3000/callback?code=auth_abc123&state=b2a6c120-8d07-4f8f-a54f-ff832c3772b3
Error Response:
https://localhost:3000/callback?error=access_denied&error_description=User%20cancelled&state=b2a6c120-8d07-4f8f-a54f-ff832c3772b3
Validate the state value before proceeding.

Token Exchange

Exchange the authorization code for tokens using the OIDC /token endpoint.
curl -X POST https://sandbox.connect.hopae.com/token \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=auth_abc123&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2Fcallback&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

ID Token Claims

Wondering what data you’ll get back? See the Return Data Model for normalized claims, assurance, issuers, presentation, and evidence. Explore the Return Data Model →

Mobile Integration

You can initiate the OIDC flow from native apps using platform browser sessions (ASWebAuthenticationSession, Custom Tabs, etc.). The Expo example below demonstrates the pattern:
import * as AuthSession from "expo-auth-session";

const issuer = "https://sandbox.connect.hopae.com";

export async function startVerification() {
  const discovery = await AuthSession.fetchDiscoveryAsync(issuer);
  const redirectUri = AuthSession.makeRedirectUri({ useProxy: false });

  const authRequest = new AuthSession.AuthRequest({
    clientId: CLIENT_ID,
    redirectUri,
    responseType: AuthSession.ResponseType.Code,
    scopes: ["openid", "profile"],
    usePKCE: true,
  });

  const result = await authRequest.promptAsync(discovery);

  if (result.type === "success" && authRequest.code) {
    // Exchange authRequest.code on your secure backend with /token
  }
}
On iOS, AuthSession uses ASWebAuthenticationSession; on Android it launches a Custom Tab. This keeps user credentials within trusted system components while preserving the OIDC redirect semantics.

Common Issues and Solutions

Next Steps