HonkIO Auth

Concepts

Apps, clients, endpoints, tokens and claims: how HonkIO Auth fits the OpenID Connect your library already speaks.

Apps

An app is one sign-in page with its own hostname, issuer, client ID, signing keys, branding, sign-in methods, users and plan. Use one app per product and environment: a test app and a live app are separate apps with separate users.

Confidential and public clients

Apps are confidential by default: creating one returns a client secret, shown once, which your back end sends to the token endpoint (client_secret_basic or client_secret_post). Replace it with POST /v1/auth-apps/:id/rotate-secret; the old secret stops working at once.

Single-page and mobile apps that can’t keep a secret are public clients (confidential: false): they have no secret and rely on PKCE alone. This is set when the app is created.

Redirect URIs

Redirect URIs are registered per app and must match exactly: HTTPS, with no wildcards, fragments or credentials. http://localhost is allowed on test apps only. An app has at most 10 redirect URIs and 10 post-logout redirect URIs, and a PATCH replaces each list as a whole. A redirect URI that breaks these rules answers AUTH_APP_REDIRECT_INVALID.

Endpoints

Every endpoint is on your app’s hostname, and the issuer is https:// plus that hostname. Discovery lists them:

bash
curl https://login.acme.ca/.well-known/openid-configuration
{
  "issuer": "https://login.acme.ca",
  "authorization_endpoint": "https://login.acme.ca/authorize",
  "token_endpoint": "https://login.acme.ca/token",
  "userinfo_endpoint": "https://login.acme.ca/userinfo",
  "jwks_uri": "https://login.acme.ca/.well-known/jwks.json",
  "end_session_endpoint": "https://login.acme.ca/logout",
  "revocation_endpoint": "https://login.acme.ca/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "id_token_signing_alg_values_supported": ["ES256"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
  "ui_locales_supported": ["en", "fr"]
}

Only the authorization code flow is supported. PKCE with S256 is required for every client, and so are state and nonce. There is no implicit or hybrid flow, and tokens never appear in URLs. /authorize also accepts prompt (login or none), max_age, login_hint and ui_locales (en or fr); without it, the page follows the browser’s language, and its EN/FR toggle overrides both.

Tokens and keys

ID and access tokens are JWTs signed with ES256 by the app’s own keys, published at /.well-known/jwks.json. Their aud is your client ID. Access tokens last 10 minutes; a HonkIO session lasts up to 30 days, and no token outlives it. Refresh tokens are opaque, rotate on every use, and reusing an old one revokes the whole session.

To accept an access token in your own API, check its signature against the JWKS, its iss, its aud and its expiry. Keys rotate with an overlap: an old key stays in the JWKS until every token it signed has expired, so caching the JWKS, as JOSE libraries do, is safe and lets you check tokens while HonkIO is down.

Scopes and claims

Ask for scopes in the authorize request. Each adds claims to the ID token and userinfo:

ScopeClaimsWhat it adds
openidsub, amr, auth_time, sidRequired. The user’s ID, how and when they signed in, and the HonkIO session.
emailemail, email_verifiedThe user’s email address and whether it’s verified.
phonephone_number, phone_number_verifiedThe user’s mobile number and whether it’s verified.
profilenameThe user’s name, when you’ve set one.

amr says how the user signed in: otp for an email code or link, sms for a text code, hwk and user for a passkey, plus mfa once two steps are done, when acr is urn:honkio:acr:2fa. When the user signed in to an organization, the ID and access tokens also carry org_id, org_slug and org_role, as they were when the token was issued.

ID token
{
  "iss": "https://login.acme.ca",
  "aud": "hka_xxxxxxxxxxxx",
  "sub": "usr_clxxxuserxxxxxxxxxxxxxx",
  "email": "ada@example.com",
  "email_verified": true,
  "amr": ["hwk", "user"],
  "auth_time": 1793632445,
  "sid": "ses_xxxxxxxxxxxxxxxxxxxxxx",
  "nonce": "…",
  "org_id": "org_clxxxorgxxxxxxxxxxxxxxx",
  "org_slug": "northwind",
  "org_role": "admin",
  "iat": 1793632445,
  "exp": 1793633045
}

Sessions and outages

An outage must never sign out people who are already signed in. HonkIO has no standby region, so your app keeps sessions alive on its own side, under one rule:

  • Keep the session when HonkIO can’t be reached: a network error, a timeout or a 5xx on refresh. Keep it only until 24 hours past the access token’s expiry, and retry now and then.
  • End the session immediately when HonkIO answers and refuses the refresh (invalid_grant): the session was revoked or reused, or the user was blocked, deleted or removed from the organization.

Every quickstart follows this rule. New sign-ins need HonkIO to be up. To learn about a revocation sooner than the next refresh, subscribe to session.revoked and user.blocked (webhook events).

Sign-out

Send the browser to the end-session endpoint, /logout, with id_token_hint (or client_id) and a registered post_logout_redirect_uri; state comes back unchanged. Use a top-level navigation (a GET), as the quickstarts do with a redirect: a cross-site POST doesn’t carry HonkIO’s session cookie, so it can’t end the HonkIO session. To revoke a refresh token from your back end, POST it to /revoke.