Skip to content

Contract

Issuer and OIDC profile

The issuer, discovery, Authorization Code with PKCE S256, exact redirects, state and nonce, scope openid profile email, access tokens addressed to the client itself.

Issuer
https://id.institutourupema.com.br/realms/urupema
ItemValue
Issuerpermanent; the iss of every token
Discoveryhttps://id.institutourupema.com.br/realms/urupema/.well-known/openid-configuration; read it at boot; its issuer must equal the configured one
JWKSthe jwks_uri from discovery; no pinned key; follow rotation

Endpoint paths belong to the engine and change with it; discovery is the contract. See Endpoints.

The IdP guarantees

Authorization Code only. The implicit flow (response_type=token), the password grant, client credentials, device and CIBA grants and token exchange are disabled on every managed client.

The IdP guarantees

PKCE S256, mandatory, also for confidential clients (RFC 9700). A request without code_challenge, or with plain, is refused.

The IdP guarantees

Exact redirects. No wildcards; https, or http only on localhost and 127.0.0.1. A redirect_uri that differs from a registered one by one character gets an IdP error page, not a return to the app.

The IdP guarantees

Contract scopes only. openid profile email. Any other scope (phone, roles, offline_access, address…) returns invalid_scope.

The IdP guarantees

Access tokens with a destination. The access token’s aud is the client itself, with no engine roles. An access token serves only the client that requested it.

Your app must

Single-use state, bound to the request. Generate one per login, keep it on the server, compare it on the callback, then discard it.

Your app must

nonce and ID token validation. Send a nonce; on the callback validate the ID token: signature against the JWKS, iss equals the issuer, aud contains your client_id, exp in the future, nonce equals the one sent.

Your app must

The client secret stays on the server. Never in the browser, the repository or the logs. An app that runs only in a browser or device is a public client and has no secret.

Your app must

The ID token is not API authorization. It identifies who signed in. An API validates the access token: signature, iss, exp, aud. See API: validate the token.

Line-broken for reading; the endpoint is authorization_endpoint from discovery.

GET <authorization_endpoint>
?client_id=my-app
&response_type=code
&scope=openid%20profile%20email
&redirect_uri=https%3A%2F%2Fmy-app.institutourupema.com.br%2Fcallback
&state=Q2hhbmdlTWU... ← random, single use
&nonce=bm9uY2UtMTIz... ← random, comes back inside the ID token
&code_challenge=E9Melhoa2Ow... ← BASE64URL(SHA-256(code_verifier))
&code_challenge_method=S256

The callback URL also carries iss (RFC 9207, authorization_response_iss_parameter_supported). openid-client checks it; a library that ignores it is safe if it validates the ID token’s iss.

Code exchange at token_endpoint: grant_type=authorization_code, the code, the same redirect_uri and the code_verifier; a confidential client authenticates with its secret (client_secret_basic or client_secret_post). A code works once.

RequestThe IdP answers
no code_challengeback to the app with error=invalid_request
code_challenge_method=plainback to the app with error=invalid_request
scope=openid phone (or roles, offline_access, address)back to the app with error=invalid_scope
response_type=token (implicit)back to the app with error=unauthorized_client
a redirect_uri not registered exactlyan IdP error page (HTTP 400); never back to the app
grant_type=password at the token endpointHTTP 400, unauthorized_client

Symptoms and fixes: Errors.