Skip to content

Client Web App Onboarding Guide — SSO Timeh

Audience: Application owners and developers registering a web application for the first time. Estimated time: 30 minutes. Official deliverable: FR-065.

1. Choose the Client Type

TypeUse whenSecret
PublicBrowser-only SPA or mobile application that cannot protect credentials.None; PKCE is mandatory.
ConfidentialApplication with a backend or BFF that can protect credentials.Stored server-side only.

Rule of thumb: server-side token calls mean confidential; browser-only logic means public.

The first-party portal and admin panel use confidential BFF + PKCE S256. Their browsers receive same-origin cookies and never receive OAuth tokens or secrets.

2. Authorization Code + PKCE

Every client must use Authorization Code with PKCE:

text
Browser -> /authorize with state, nonce, and S256 challenge
SSO -> registered callback with code and state
Client -> validate state and POST code + verifier to /token
Client -> validate tokens and create its local session

Discovery: https://api-sso.timeh.my.id/.well-known/openid-configuration

3. Registration Requirements

  • Redirect URIs use exact matching; wildcards are rejected.
  • Production redirect URIs require HTTPS.
  • http:// is accepted only for localhost development.
  • Post-logout URIs must share the redirect URI origin.
  • Request openid; add profile, email, and approved optional scopes.
  • Use separate development and production clients.

In the admin panel at https://admin-sso.timeh.my.id, create a client with a unique lowercase ID, owner email, type, environment, exact redirect URIs, and optional logout channels.

For confidential clients, the secret is displayed once. Store it in a vault or server environment and never commit it. Public clients do not have a secret.

4. Integration Selection

Use the framework guide matching the application architecture:

Use the API Reference rather than duplicating or hardcoding endpoint metadata.

5. One-Line Header Account Bar

For applications that want an app launcher plus account menu in their header, add a mount point:

html
<div id="sso-account"></div>

Then add the hosted widget:

html
<script src="https://sso.timeh.my.id/widget/account.js" data-mount="#sso-account"></script>

The script is a thin loader: it injects an <iframe> from sso.timeh.my.id that renders the real account bar (pixel-identical to the portal and console) and grows/shrinks automatically as its panels open and close. All data calls (/widget/session, /widget/apps, /widget/accounts, /widget/switch, /widget/logout) happen inside the iframe on the SSO origin — the host page never calls the widget endpoints and the browser never receives OAuth tokens. The multi-account chooser is limited to accounts that have signed in on this browser through an httpOnly device cookie and a server-side registry, not IP address or User-Agent.

If the data-mount attribute is not set, the loader looks for an element marked with data-sso-account. Other attributes on the script tag are ignored.

The embedding origin must be explicitly approved: either a first-party SSO origin or a client marked as trusted for widget CORS. The same allow-list is enforced as the embed page's frame-ancestors CSP, so an unapproved origin cannot frame the account bar at all (the browser refuses, rather than a mere 401). app_base_url alone is not sufficient, and the allow-list does not use redirect URIs.

If the application uses strict Content Security Policy, allow the SSO origin in script-src (for the account.js loader) and in frame-src (or child-src) for the https://sso.timeh.my.id/embed/account-bar iframe. connect-src and style-src entries are no longer needed — the host page makes no widget fetches and loads no widget stylesheet. Ensure the client app_base_url uses a safe web URL; non-web links such as javascript: and data: are not rendered by the widget. Do not call native /api/auth/*, /api/profile/*, or /api/mfa/* endpoints from external apps; use OIDC and /widget/*.

6. Integration Test Checklist

  • Login redirects to SSO and returns to the exact callback.
  • State and nonce are validated.
  • PKCE uses code_challenge_method=S256.
  • Authorization code exchange succeeds once.
  • ID token signature, issuer, audience, expiry, and nonce are validated.
  • UserInfo returns expected scoped claims.
  • Refresh rotates the refresh token.
  • Logout removes the local session and ends the SSO session.
  • Back-channel logout removes the local session when configured.
  • The account bar, if used, shows only allowed apps and accounts bound to this browser.
  • No secret or token is present in a browser bundle, URL, or log.

7. Go Live and Rollback

  1. Validate with a development client.
  2. Register or activate the production client with HTTPS URIs.
  3. Install production secrets in the server vault before deployment.
  4. Deploy and run login, refresh, and logout smoke tests.
  5. Rotate confidential secrets according to policy.

To roll back access, disable the client from its lifecycle controls. Disabling revokes active tokens. Decommission only after dependent applications have been removed.

8. Troubleshooting

ErrorCheck
invalid_clientActive client, correct type, and confidential credentials.
invalid_grantCode TTL/reuse, exact redirect URI, and original verifier.
Redirect mismatchScheme, host, port, path, case, and trailing slash.
PKCE requiredBoth challenge and verifier use S256.
Scope deniedClient allow-list and mandatory openid.
Session expiredRefresh rotation or a new interactive login.

Retain request_id and error_ref when reporting a problem. Never include tokens or secrets in a support ticket.

Released under the MIT License.