IdP Urupema
For AI coding agents
The contract as plain text for an assistant, and a ready prompt for an IdP Urupema integration.
The contract as text, generated on every build from the same pages:
| File | Holds | Use |
|---|---|---|
/llms.txt | an index in the llms.txt format, opening with the contract in ten lines | the assistant decides what to read |
/llms-full.txt | every page in full, with the guides’ code | to integrate an app: use this one |
/llms-small.txt | every page without asides and collapsed details | a short context window |
The prompt
Section titled “The prompt”Paste it into the assistant, fill in the app details, keep the rules as they are.
You are integrating this app with IdP Urupema (OpenID Connect).Before writing any code, read https://docs.id.institutourupema.com.br/llms-full.txt and follow itexactly; where it disagrees with a library default or a tutorial,it wins.
App details (fill in):- stack: Node.js/Express | other: ...- client_id: my-app-dev- redirect_uri: http://localhost:3000/callback- post_logout_redirect_uri: http://localhost:3000/- has an API of its own: yes | no- who gets access, and how it is granted today: ...
Rules that cannot be broken (issuer: https://id.institutourupema.com.br/realms/urupema):1. Discover everything from https://id.institutourupema.com.br/realms/urupema/.well-known/openid-configuration at boot and check its issuer field. Never hard-code endpoints or keys.2. Authorization Code + PKCE S256 only, with a single-use state and a nonce. Scope exactly openid profile email; any other scope fails with invalid_scope. No implicit, password, client-credentials or offline_access.3. Validate the ID token: signature against the discovered JWKS, iss, aud contains your client_id, exp, nonce. Make sure your library really verifies the signature.4. Key people by the pair (iss, sub). Never by email, and never link accounts because emails match.5. Confidential client (has a server, the recommended setup): tokens stay on the server, the browser gets only an HttpOnly session cookie, the client secret never leaves the server (not in the browser, the repository or logs).6. The local session is derived: when the 300 s access token expires, refresh. If the refresh is refused or the IdP is unreachable, destroy the local session and answer 401. Local session ceiling: 10 h.7. Logout = destroy the local session and redirect to end_session_endpoint with id_token_hint and a registered post_logout_redirect_uri.8. An API validates the access token: signature, iss, exp, aud containing the app's client_id, and typ Bearer (rejects ID tokens). Name and email are not in the access token: call userinfo_endpoint.9. Authentication is not authorization: a new account has zero permissions in your app until your app grants them. To grant access before someone's first login, invite by email and bind the invitation to (iss, sub) at first login only when email_verified is true.10. Never call the Keycloak admin API or database; there is no admin API for apps. An app is registered by a reviewed pull request (clients/<clientId>.json in urupema/idp), one client per environment; without access to that repository, request it at suporte@institutourupema.com.br.
For Node.js, start from the server in "Node.js server (BFF)" and themiddleware in "API: validate the token"; change only what this app needs.
Deliver:1. the code;2. for each rule above, the file and line where it is met;3. tests proving: a new account has zero permissions; an invitation binds only with email_verified = true; a refused refresh answers 401 and destroys the session; logout redirects to end_session_endpoint with id_token_hint; the API refuses an ID token and another client's token;4. the descriptor clients/<client_id>.json for the pull request.Recurring misses
Section titled “Recurring misses”Check the result against Before production.
- ID token signature not verified. Several libraries skip it by default (openid-client needs
enableNonRepudiationChecks). - Email as the key, or accounts merged on matching emails. The key is
(iss, sub); the email binds only an invitation, once, when verified. - Permissions on first login. Generic code gives every authenticated user a default role. Here a new account gets nothing.
- A refused refresh does not end the session: a retry, a grace period or a cached user. It must destroy the session and answer 401; a block then takes effect within 5 minutes.
- Logout only clears the cookie. It must also redirect to
end_session_endpointwithid_token_hint. - Extra scopes (
offline_access,roles) copied from tutorials: the IdP refuses them withinvalid_scope. - An invented admin API to list or create users. There is none; the app keeps its own records.
Complete, tested code: site/examples/ in the IdP repository.