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
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
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
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
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"]
}'| Kind | Typical client | Secret |
|---|---|---|
browser | SPA / browser-only | None |
native | iOS / Android / desktop | None |
web | Server-rendered/backend web app | Returned once; backend only |
service | Machine-to-machine | Returned 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.
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
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.
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();const tokens = await identity.handleCallback(); console.log(tokens.id_token_claims.sub); const profile = await identity.userInfo(tokens.access_token);
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.
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.
<?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.
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
curl --fail-with-body \ https://auth.example.com/oauth/userinfo \ -H 'Authorization: Bearer ACCESS_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.
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
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.
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.
| Purpose | Endpoint |
|---|---|
| Signup | POST /account/signup |
| Email verification | POST /account/email/verify |
| Password reset start | POST /account/password/forgot |
| Password reset complete | POST /account/password/reset |
| Profile | GET/PATCH /account/profile |
| TOTP enrollment | POST /account/totp/start → /complete |
| Passkey login | POST /auth/passkey/options → /verify |
| Linked methods | GET /account/connections |
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.