Docs · Quickstart
Add sign‑in in ten lines
Host one JSON file, point any OpenID Connect library at happauth.com, and place the button. There is no form, no secret and no approval.
The ten lines
With openid-client 6. Lines 2–3 find hApp Auth, 4–7 send the person to it, 8–10 turn the code into the person. callbackUrl is the URL hApp Auth sent the person back to, and the verifier lives in their session between the two requests.
import * as oidc from "openid-client";
const config = await oidc.discovery(new URL("https://happauth.com"),
"https://yoursite.example/oauth/client.json", undefined, oidc.None());
const verifier = oidc.randomPKCECodeVerifier();
const signInUrl = oidc.buildAuthorizationUrl(config, { scope: "openid profile",
redirect_uri: "https://yoursite.example/callback", code_challenge_method: "S256",
code_challenge: await oidc.calculatePKCECodeChallenge(verifier) });
const tokens = await oidc.authorizationCodeGrant(config, callbackUrl,
{ pkceCodeVerifier: verifier });
const { sub } = tokens.claims()!;
Host your client metadata
Your client id is the HTTPS URL of a small JSON document on your own site (a client ID metadata document). hApp Auth fetches it when a person first signs in; there is nothing to register.
https://yoursite.example/oauth/client.json { "client_id": "https://yoursite.example/oauth/client.json", "client_name": "Your site", "client_uri": "https://yoursite.example", "redirect_uris": ["https://yoursite.example/callback"], "grant_types": ["authorization_code"], "response_types": ["code"], "token_endpoint_auth_method": "none" }client_idmust be the document’s own URL. Keep redirect URIs on the same origin; a redirect tolocalhostworks for development and shows the person a warning line. The document is fetched with a 5 KB cap, no redirects and a short timeout, and cached by its HTTP headers.Point the library at the issuer
The issuer is
https://happauth.com. Discovery, keys and endpoints come from its metadata:Terminal curl https://happauth.com/.well-known/openid-configurationThe same flow, spread over the two routes a server needs:
server.ts import * as oidc from "openid-client"; const SITE = "https://yoursite.example"; const config = await oidc.discovery( new URL("https://happauth.com"), // the issuer `${SITE}/oauth/client.json`, // your client id: a URL you host undefined, oidc.None(), // a public client: no secret ); // GET /sign-in — send the person to hApp Auth. export async function signIn(session: Session) { const verifier = oidc.randomPKCECodeVerifier(); session.set("pkce", verifier); // keep it until the callback return Response.redirect(oidc.buildAuthorizationUrl(config, { redirect_uri: `${SITE}/callback`, scope: "openid profile email", code_challenge: await oidc.calculatePKCECodeChallenge(verifier), code_challenge_method: "S256", })); } // GET /callback — the code becomes a verified person. export async function callback(request: Request, session: Session) { const tokens = await oidc.authorizationCodeGrant(config, new URL(request.url), { pkceCodeVerifier: session.take("pkce"), }); const claims = tokens.claims()!; // sub, acr, amr, auth_time, and what you asked for session.set("user", claims.sub); return Response.redirect(SITE); }Place the button
Link it to your
/sign-inroute. Use it as it is: recognition is what lets a person notice a fake.Two variants (Emerald on light grounds; White on dark grounds and beside other providers), one height (44 px), English and Turkish. Coming: the 2 KB web component and the SVG set
What comes back
The ID token always carries sub and auth_time; acr and amr, which say how the person signed in, are coming. Ask for more with scopes; the person sees one line for each and can turn the optional ones off. Refusing an optional claim still signs them in, so read what you received, not what you asked for.
| Scope | Claims | The person sees |
|---|---|---|
openid | sub, auth_time | Your ID for this site (required) |
profile | name, picture | Your name and picture |
email | email, email_verified | Your email address |
github Coming | github: { id, login, created_at } | Your GitHub account |
offline_access | A refresh token | Stay signed in while you’re away |
More scopes, the assurance values and the subject rules are on OpenID Connect for Holochain.
Rules the provider keeps
- Authorization code with PKCE (S256) for every client. No implicit grant, no password grant.
- Codes work once and expire in ten minutes (60 seconds is coming). Every authorization response carries
iss; openid-client checks it for you. - Access tokens are JWTs that live 5 minutes and name one audience. Refresh tokens rotate, and reusing an old one revokes the family.
- A third-party site gets a pairwise
sub: the same person has a different id on every site.