Application integration
When Awwwth uses the Keycloak backend, applications continue to use the Awwwth issuer, client credentials, signed tokens, gateway and hosted widget described here. Inline native password/MFA and Awwwth embedded passkey APIs are unavailable in this mode; use hosted sign-in.
For automatic project setup, start with the CLI guide.
Awwwth supports confidential clients using authorization-code flow with S256 PKCE and HTTP Basic client authentication. Token exchange belongs on your application's backend because it requires the client secret. The browser SDK only obtains an authorization code.
Register an application
Use the account interface or the management API to register a platform application as an administrator, or a company application as its owner. Registration accepts:
{
"name": "Example application",
"redirectUri": "https://app.example.com/auth/callback",
"logoutUri": "https://app.example.com/auth/backchannel-logout"
}
logoutUri is optional. Registration returns {id, secret}; store the secret securely on the backend. Later application listings do not return it. Each client has one exact registered callback URL. Production URLs require HTTPS, no embedded credentials, and no fragments. Development also permits HTTP on localhost and 127.0.0.1. Logout URLs cannot contain a query string.
Company clients receive a company-scoped profile and OAuth subject. Shared passwords, passkeys and MFA belong to the main Awwwth identity. Existing independent same-email company accounts require explicit proof of both accounts before linking; email matching alone never merges them.
Authorization flow
- Load
/.well-known/openid-configurationfrom the issuer. Use its advertised endpoints, which may use a separate API hostname. - Generate random
state,nonce, and a PKCE verifier. Retain them for this login attempt. State and nonce must each be 16–512 characters; the verifier must be 43–128 characters using letters, digits,.,_,~, or-. - Redirect the browser to
/oauth/authorizewith the following query fields. - The user signs in, verifies their email if needed, and approves access. Check callback
stateand issuer, and handle returned OAuth errors. - Exchange the one-time code from your backend with the same callback URL and PKCE verifier.
- Validate the ID token before establishing your application's own session.
| Field | Value |
|---|---|
response_type | code |
client_id | Registered application ID |
redirect_uri | Exact registered callback URL |
scope | Space-separated scopes; openid required |
state, nonce | Random per-attempt values |
code_challenge | Base64url SHA-256 digest of the verifier, without padding |
code_challenge_method | S256 |
prompt | Optional login, consent, or none; none cannot be combined |
max_age | Optional nonnegative integer, in seconds |
screen_hint | Optional signup or forgot to show a hosted account form |
Supported scopes are openid, profile, email, and offline_access. Authorization requires a verified email. By default, an existing session automatically authorizes registered apps in the same account directory. prompt=none issues a code when possible and otherwise returns login_required or interaction_required; prompt=consent forces an explicit consent screen. prompt=login or max_age=0 requires authentication after the authorization request began.
The screen_hint extension opens account creation or password recovery at Awwwth instead of asking an application to implement those forms. It is intended for interactive requests and cannot be combined with prompt=none.
Token exchange and validation
Send a backend POST to the discovered token endpoint with HTTP Basic authentication using the client ID and secret, and Content-Type: application/x-www-form-urlencoded:
grant_type=authorization_code
code=<callback code>
redirect_uri=<registered callback URL>
code_verifier=<original verifier>
These are logical fields; use a form encoder such as URLSearchParams to construct the actual request body. A successful response contains access_token, token_type: "Bearer", expires_in: 300, scope, and id_token. It includes refresh_token when offline_access was requested.
Access tokens are opaque. Use /oauth/userinfo with Authorization: Bearer <access-token> for claims, or client-authenticated /oauth/introspect to check activity. Introspection only reports active access tokens belonging to the requesting client; refresh tokens are not introspected as active.
ID tokens are EdDSA-signed JWTs. Your backend must verify the signature using the discovered JWKS and check the issuer, audience, expiry, and original nonce. Relevant claims include sub, sid, auth_time, and, for company identities, tenant_id. Name and email claims depend on scopes. Merely decoding a token is not verification. Use the stable subject identifier for account linkage.
| Credential | Lifetime / behavior |
|---|---|
| Authorization request token | 10 minutes |
| Authorization code | One minute; one-time exchange |
| Access and ID token | Five minutes |
| Refresh token | 30 days per issued token; validity also depends on the underlying session/grant |
| Identity session | 30 days unless revoked |
To refresh, send grant_type=refresh_token and refresh_token to the token endpoint with the same client authentication. Save the replacement refresh token atomically. Reuse of an already consumed refresh token revokes the grant, so serialize refresh operations per application session. Tokens remain subject to session revocation and account status.
To disconnect, send token to /oauth/revoke with client authentication. This revokes the associated grant and returns {}, including when no matching token exists.
Browser popup SDK
The ESM module at https://YOUR_ISSUER/awwwth.js exports signIn and AwwwthAuthError. Call signIn directly from a user interaction so the browser can open its popup. For example, inside your application's JavaScript module:
import { signIn, AwwwthAuthError } from "https://auth.example.com/awwwth.js";
export async function startSignIn() {
try {
const authorization = await signIn({
issuer: "https://auth.example.com",
clientId: "app_REPLACE_WITH_REGISTERED_ID",
redirectUri: `${location.origin}/auth/callback`,
scope: "openid profile email",
timeoutMs: 300_000,
});
return authorization; // { code, codeVerifier, nonce }
} catch (error) {
if (error instanceof AwwwthAuthError) {
// Let the caller render an appropriate retry or cancellation message.
throw new Error(`Sign-in failed: ${error.code}`, { cause: error });
}
throw error;
}
}
The callback must be on your application's origin and must serve a page that keeps the popup open until the SDK observes it. The SDK polls the popup URL, validates state, closes the popup, and returns the code, verifier, and nonce. It does not exchange tokens, validate ID tokens, create your app session, or supply a backend callback route. Pass the result to a backend login-completion endpoint protected and bound to the initiating browser login attempt. Keep client credentials and resulting tokens on the backend.
Options also accept an AbortSignal. Defined SDK errors are invalid_config, popup_blocked, popup_closed, request_cancelled, request_timeout, and authorization_failed; callers should also handle unexpected errors. The default timeout is five minutes. Popup isolation policies in the consuming application must preserve the opener relationship needed by this flow.
Back-channel logout
If a logoutUri is registered, grant revocation queues a server-side POST with the form-encoded field logout_token. The signed token has type logout+jwt, a two-minute expiry, and claims including iss, aud, sub, sid, iat, exp, jti, and the back-channel logout event.
Your endpoint should validate the signature, issuer, audience, expiry, event, and replay identifier, then invalidate local sessions linked to the grant's sid. Accept retries idempotently. Delivery is asynchronous and retries are bounded; applications should still enforce token expiry and handle inactive tokens. See Architecture for queue behavior.
Passkeys without app navigation
The unpublished inline package supports a visible Awwwth-origin passkey panel inside the app's page. See the module README for prepareInlinePasskey and the same-origin initiation route. The backend authenticates the registered client, keeps secrets server-side, binds a five-minute browser attempt with state/nonce/S256 PKCE and validates the normal OIDC callback. The provider permits only the exact registered callback origin to embed this panel and verifies the signed top origin. Credentials are shared through the platform identity; company profiles/permissions remain separate. Users still confirm the passkey on their device. Browser incompatibility can be recovered with the explicit hosted passkey action.