Genyleap/Docs
OpenProof / Develop

Connect any application through standard OAuth/OIDC.

OpenProof is language-agnostic at the protocol boundary. Use the shipped SDKs where they help, or integrate from any language that can do HTTPS and OAuth 2.0/OpenID Connect.

The integration model

flowAuthorization Code + PKCE
Your app
   ↓ redirect
OpenProof /oauth/authorize
   ↓
authentication / provider / passkey / wallet
   ↓
your callback?code=...&state=...&iss=...
   ↓ code + verifier
OpenProof /oauth/token
   ↓
access_token + id_token + optional refresh_token

Use OAuth/OIDC tokens for product integration. The secure OpenProof session cookie belongs to the OpenProof account/admin origin and should not be copied into your product domain.

Start with Discovery

URLsreplace auth.example.com
https://auth.example.com/.well-known/openid-configuration
https://auth.example.com/.well-known/jwks.json

Discovery is the preferred way for reusable clients to learn the authorization, token, UserInfo, introspection and related protocol endpoints.

Register your product

An active owner with IAL2 can use /admin/console or the administration API.

Create an application

curlowner session required
curl --fail-with-body -b owner.cookies \
  https://auth.example.com/admin/applications \
  -H 'Content-Type: application/json' \
  -d '{
    "identifier":"example-web",
    "name":"Example Web",
    "environment":"production"
  }'

Create a client

curlweb client
curl --fail-with-body -b owner.cookies \
  https://auth.example.com/admin/clients \
  -H 'Content-Type: application/json' \
  -d '{
    "application_id":"APPLICATION_ID",
    "name":"Example Web",
    "kind":"web",
    "redirect_uris":["https://app.example.com/oauth/callback"],
    "scopes":["openid","profile","offline_access"]
  }'
KindTypical clientSecret
browserSPA / browser-onlyNone
nativeiOS / Android / desktopNone
webServer-rendered/backend web appReturned once; backend only
serviceMachine-to-machineReturned once

Authorization Code + PKCE S256

Generate a random verifier, derive its S256 challenge, and generate random state and nonce. Store verifier/state/nonce only for the lifetime of that login transaction.

authorization requestconceptual
GET /oauth/authorize?
  response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://app.example.com/oauth/callback
  &scope=openid profile offline_access
  &code_challenge=PKCE_CHALLENGE
  &code_challenge_method=S256
  &state=STATE
  &nonce=NONCE

At callback, require the returned state and iss to match the transaction/configured issuer before token exchange.

Token exchange

curlapplication/x-www-form-urlencoded
curl --fail-with-body https://auth.example.com/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode 'client_id=CLIENT_ID' \
  --data-urlencode 'code=AUTHORIZATION_CODE' \
  --data-urlencode 'redirect_uri=https://app.example.com/oauth/callback' \
  --data-urlencode 'code_verifier=PKCE_VERIFIER'

ID token validation

Verify the RS256 signature with issuer JWKS. Validate at least iss, aud, exp, iat and the original nonce; honor nbf, azp and at_hash when present.

JavaScript / browser

The repository ships a browser-focused SDK package named @openproof/identity. It generates PKCE/state/nonce, validates callback state/issuer and verifies the RS256 ID token against JWKS.

javascriptpublic browser client
import { OpenProofIdentity } from "@openproof/identity";

const identity = new OpenProofIdentity({
  issuer: "https://auth.example.com",
  clientId: "CLIENT_ID",
  redirectUri: "https://app.example.com/oauth/callback",
  scopes: ["openid", "profile", "offline_access"]
});

await identity.login();
javascriptcallback
const tokens = await identity.handleCallback();

console.log(tokens.id_token_claims.sub);

const profile =
  await identity.userInfo(tokens.access_token);
Browser clients have no client secret.

Never embed a confidential web or service client secret in JavaScript shipped to a user.

Node.js

Use a mature OIDC client for your framework, or use native fetch() for the documented protocol. Keep confidential credentials in your server environment/secret store.

javascriptserver-side token exchange
const body = new URLSearchParams({
  grant_type: "authorization_code",
  client_id: process.env.OPENPROOF_CLIENT_ID,
  code,
  redirect_uri: "https://app.example.com/oauth/callback",
  code_verifier: verifier
});

const response = await fetch(
  "https://auth.example.com/oauth/token",
  {
    method: "POST",
    headers: {
      "content-type":
        "application/x-www-form-urlencoded"
    },
    body
  }
);

if (!response.ok) {
  throw new Error(
    `OpenProof token exchange failed: ${response.status}`
  );
}

const tokens = await response.json();

Use an OIDC/JWT library for JWKS caching, signature verification and claim validation rather than writing cryptographic verification yourself.

PHP

Any maintained PHP OAuth/OIDC library can use OpenProof as its issuer. The direct protocol is normal HTTPS and form-encoded OAuth.

phptoken exchange
<?php

$payload = http_build_query([
    'grant_type' => 'authorization_code',
    'client_id' => getenv('OPENPROOF_CLIENT_ID'),
    'code' => $_GET['code'],
    'redirect_uri' =>
        'https://app.example.com/oauth/callback',
    'code_verifier' =>
        $_SESSION['openproof_pkce_verifier'],
]);

$curl = curl_init(
    'https://auth.example.com/oauth/token'
);

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/x-www-form-urlencoded'
    ],
]);

$body = curl_exec($curl);

if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}

Validate ID tokens with a maintained JWT/OIDC package and issuer JWKS. Do not implement RSA/JWK verification ad hoc.

C++

The repository C++26 SDK is exported as openproof.sdk. It is transport-neutral so you can keep your existing Boost.Beast, Qt Network or libcurl stack.

c++SDK configuration
import openproof.sdk;

auto config =
    openproof::sdk::ClientConfig::create(
        "https://auth.example.com",
        "CLIENT_ID",
        "http://127.0.0.1:49152/callback",
        {"openid", "profile", "offline_access"});

openproof::sdk::IdentityClient client{
    std::move(config).value()
};

auto login = client.beginLogin();

std::cout <<
    login->authorizationUrl()
    << '\n';

The SDK provides helpers for login transaction creation, authorization-code request construction, refresh and UserInfo. A minimal relying-party example lives in examples/reference-client.

Python, Go, Rust, Java, C# and other languages

You do not need an OpenProof-specific SDK. Configure a standards-compliant OAuth/OIDC library with the OpenProof issuer. The minimum safe implementation is Discovery → PKCE/state/nonce → authorization redirect → callback checks → token exchange → JWKS signature/claim validation → access-token authorization.

UserInfo and refresh

curlUserInfo
curl --fail-with-body \
  https://auth.example.com/oauth/userinfo \
  -H 'Authorization: Bearer ACCESS_TOKEN'
curlrefresh token
curl --fail-with-body \
  https://auth.example.com/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=refresh_token' \
  --data-urlencode 'client_id=CLIENT_ID' \
  --data-urlencode 'refresh_token=REFRESH_TOKEN'

Refresh tokens rotate. Persist the new token atomically and discard the old value. Replay protection can revoke the entire family.

Protect your API

Register a resource with an exact audience and narrow scopes.

curlregister resource
curl --fail-with-body -b owner.cookies \
  https://auth.example.com/admin/resources \
  -H 'Content-Type: application/json' \
  -d '{
    "audience":"https://api.example.com/rides",
    "name":"Ride API",
    "scopes":["rides:read","rides:request"]
  }'

Your backend should authorize token validity, exact audience, required scope and product business rules. A valid token is not by itself sufficient authorization.

Introspection

curlconfidential backend
curl --fail-with-body \
  https://auth.example.com/oauth/introspect \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'client_id=CONFIDENTIAL_CLIENT_ID' \
  --data-urlencode 'client_secret=CLIENT_SECRET' \
  --data-urlencode 'token=ACCESS_TOKEN'

OpenProof gateway

Alternatively, place product routes behind OpenProof and enforce role, assurance, scope and audience in static route policy before proxying to your backend.

Service-to-service

Use a service client and provision allowed audiences/scopes, then request an access token with client_credentials.

curlmachine grant
curl --fail-with-body \
  https://auth.example.com/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=CLIENT_ID' \
  --data-urlencode 'client_secret=CLIENT_SECRET' \
  --data-urlencode 'scope=rides:dispatch' \
  --data-urlencode 'resource=https://api.example.com/dispatch'

The result is an access token only. There is no refresh token or human session.

Account APIs

When account self-service and verification delivery are enabled, OpenProof exposes signup, verified email/phone ownership, password reset, profiles, TOTP, recovery codes and passkeys.

PurposeEndpoint
SignupPOST /account/signup
Email verificationPOST /account/email/verify
Password reset startPOST /account/password/forgot
Password reset completePOST /account/password/reset
ProfileGET/PATCH /account/profile
TOTP enrollmentPOST /account/totp/start/complete
Passkey loginPOST /auth/passkey/options/verify
Linked methodsGET /account/connections
One canonical identity.

Do not key your product user by Google/GitHub email. A user may link multiple sign-in methods while retaining the same stable OpenProof subject.

Error handling and secret hygiene

Errors include a stable code, client-safe message and request ID. Branch on code/status, not prose. Treat 401 as missing/invalid authentication, 403 as insufficient authority/assurance, 409 as state conflict and 429 as a backoff signal.

Never log passwords, TOTP/recovery codes, verification secrets, authorization codes, access/refresh tokens, confidential client secrets, cookies, DPoP proofs or private keys.