Developer SSO Reference
This is the integration index for third-party applications using SSO Timeh as their OpenID Provider. Start with discovery and do not hardcode endpoints beyond the initial bootstrap.
Production issuer: https://api-sso.timeh.my.id
Authoritative discovery: https://api-sso.timeh.my.id/.well-known/openid-configuration
Navigation
| Need | Document |
|---|---|
| Client registration and public versus confidential selection | Client Web App Onboarding |
| OAuth/OIDC endpoint contracts | API Reference |
| Scopes, claims, UserInfo, and token roles | Scopes and Claims |
| OAuth errors and support references | Errors and FAQ |
| Mandatory PKCE, token TTLs, rotation, and JWKS | Security Model |
| Access token validation for APIs | Resource Server Guide |
| Laravel confidential client | Laravel Integration |
| Next.js route-handler BFF | Next.js Integration |
| Vue.js public SPA | Vue.js Integration |
| Express confidential client | Express Integration |
Authorization Code + PKCE Flow
mermaid
sequenceDiagram
participant U as "User"
participant C as "Client App"
participant OP as "SSO Timeh OP"
participant API as "Resource Server"
C->>OP: GET /.well-known/openid-configuration
OP-->>C: issuer, endpoints, jwks_uri, S256
C->>C: Generate state, nonce, code_verifier, S256 code_challenge
U->>C: Click login
C->>OP: GET /authorize?response_type=code&client_id&redirect_uri&scope&state&nonce&code_challenge&code_challenge_method=S256
OP->>U: Login and consent if required
OP-->>C: Redirect redirect_uri?code&state
C->>C: Validate state
C->>OP: POST /token grant_type=authorization_code + code_verifier
OP-->>C: access_token, id_token, expires_in, refresh_token if offline_access
C->>API: Authorization: Bearer access_token
API->>OP: Fetch JWKS when kid is unknown
API->>API: Verify ES256 signature, iss, aud=sso-resource-api, exp, nbfRequired Knowledge
openidis required on every/authorizerequest.- PKCE
S256is mandatory for every client, including confidential clients. stateandnonceare mandatory and must be validated at callback.- Access tokens are ES256 JWTs with
aud = sso-resource-api. Never use an ID token as an API credential. - A refresh token is issued only when
offline_accessis requested and allowed. - Public clients can call the token endpoint without a secret, but PKCE remains mandatory.
The first-party portal and admin panel use the confidential BFF + PKCE S256 pattern. Their secrets and tokens remain in server runtime; browsers receive same-origin session cookies only.
Python Skeleton with Authlib
python
from authlib.integrations.requests_client import OAuth2Session
from authlib.common.security import generate_token
import base64
import hashlib
issuer = "https://api-sso.timeh.my.id"
client_id = "your-client-id"
redirect_uri = "https://app.example.com/auth/callback"
code_verifier = generate_token(64)
challenge = hashlib.sha256(code_verifier.encode("ascii")).digest()
code_challenge = base64.urlsafe_b64encode(challenge).rstrip(b"=").decode("ascii")
state = generate_token(32)
nonce = generate_token(32)
client = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope="openid profile email")
authorization_url, state = client.create_authorization_url(
f"{issuer}/authorize",
state=state,
nonce=nonce,
code_challenge=code_challenge,
code_challenge_method="S256",
)
token = client.fetch_token(
f"{issuer}/token",
code="code-from-callback",
code_verifier=code_verifier,
client_id=client_id,
)OAuth libraries that cannot produce a verifier and code_challenge_method=S256 are incompatible with this provider.