Integration Checklist
Use this checklist before taking an SSO client integration live. It consolidates the requirements spread across onboarding, API reference, scopes/claims, and framework guides.
1. Client Registration
- [ ] Choose the correct client type: public for SPA/mobile, confidential for applications with a backend/BFF.
- [ ] Register an exact-match
redirect_uri: scheme, host, port, path, and trailing slash must match exactly. - [ ] Use separate development and production clients.
- [ ] Store
client_secretonly for confidential clients; it is shown once and must go into a vault/server env. Never commit it. Details → Onboarding. - [ ] Request the minimum scopes you actually need:
openidis required; addprofile,email,offline_access,roles, andpermissionsonly when needed.
2. Discovery and Endpoints
- [ ] Read metadata from
/.well-known/openid-configurationat boot or deploy time. - [ ] Cache
authorization_endpoint,token_endpoint,jwks_uri,userinfo_endpoint,revocation_endpoint, andend_session_endpoint. - [ ] Do not guess or hardcode endpoint paths when discovery can provide them. Details → API Reference.
3. Authorize Request
- [ ] Use Authorization Code Flow.
- [ ] Enable PKCE
S256for all clients, including confidential clients. - [ ] Generate a fresh random
stateandnoncefor every login. - [ ] Store the
code_verifiersafely until the callback. - [ ] Include
openidin the requested scopes.
4. Token Exchange
- [ ] Exchange the authorization code at the
token_endpoint. - [ ] Confidential clients send
client_secretfrom the server only. - [ ] Send the same
code_verifierused for the authorize request. - [ ] Use the exact same
redirect_uriduring exchange. - [ ] Treat authorization codes as short-lived and single-use.
5. id_token Validation
- [ ] Validate the signature using JWKS and the provider signing algorithm (ES256).
- [ ] Validate
issagainst the discovery issuer. - [ ] Validate
aud=client_idfor theid_token. - [ ] Validate
exp/nbfand use a small clock-skew leeway. - [ ] Validate
nonceagainst the stored authorize value. - [ ] Do not confuse token roles:
id_tokenis for local login,access_tokenis for resource server calls.
6. Identity and RBAC
- [ ] Use
subas the stable user identifier, not email. - [ ] If RBAC is required, request the
rolesand/orpermissionsscopes. - [ ] Read
roles[]/permissions[]claims (arrays), not a singularroleclaim. - [ ] Ensure optional scopes are allow-listed for the client in the admin panel. Details → Scopes and Claims.
7. Session, Refresh, Logout
- [ ] Store refresh tokens encrypted server-side; avoid browser storage unless this is truly a public SPA and you have completed a threat review.
- [ ] Rotate refresh tokens atomically and prevent parallel replay.
- [ ] Use RP-initiated logout via
end_session_endpoint//connect/logoutwith a registeredid_token_hintandpost_logout_redirect_uri. - [ ] If back-channel logout is enabled, verify that the application actually clears its local session.
8. Optional Header Account Bar
- [ ] For external web apps, provide a header mount point such as
<div id="sso-account"></div>. - [ ] Add the one-line widget (an iframe loader — other attributes on the script tag are ignored; without
data-mount, the loader looks for an element markeddata-sso-account):
html
<script src="https://sso.timeh.my.id/widget/account.js" data-mount="#sso-account"></script>- [ ] The loader injects an
<iframe>fromsso.timeh.my.id/embed/account-barthat renders the real account bar and grows/shrinks automatically as its panels open. All/widget/*calls happen inside the iframe — the host page never calls the widget endpoints. - [ ] Ensure the client
app_base_urlis a web URL (https://or localhost development);javascript:anddata:links are not rendered. - [ ] Ensure the embedding origin is explicitly approved (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-ancestorsCSP — an unapproved origin is refused by the browser when framing.app_base_urlalone is not sufficient, and the allow-list does not use redirect URIs. - [ ] If the application uses strict CSP, allow the SSO origin in
script-src(theaccount.jsloader) and inframe-src/child-src(the/embed/account-bariframe).connect-srcandstyle-srcentries are no longer needed. - [ ] Do not read widget cookies from JavaScript. The multi-account chooser uses an httpOnly device cookie and a server-side registry.
- [ ] Do not call native
/api/auth/*,/api/profile/*, or/api/mfa/*endpoints from external apps. Those mutation endpoints are first-party browser APIs and require a trustedOrigin/RefererplusX-Requested-With: XMLHttpRequest; external apps use OIDC and/widget/*.
9. Development vs Production
- [ ]
http://localhostis for development only. - [ ] HTTPS is required for live environments.
- [ ] Register production redirect URIs, post-logout URIs, and logout hooks separately from development.
- [ ] Logs and troubleshooting output never print tokens or secrets.
10. Pre-Go-Live Smoke Test
- [ ] Login succeeds and returns to the exact callback.
- [ ] State/nonce mismatches are rejected.
- [ ] A second code exchange fails as expected.
- [ ] The
id_tokenis fully validated before the local session is created. - [ ] Refresh succeeds and the old token cannot be replayed.
- [ ] Logout clears both the local session and the SSO session.
- [ ] The account bar widget, if used, shows only applications the user can access and accounts bound to this browser.
- [ ] No token or secret appears in URLs, frontend bundles, prohibited browser storage, or support tickets.