Integrate Login into Your App
This guide takes a subscriber's application from "nothing" to "users log in through Curupira".
It assumes you already have a tenant, an application (client_id + API key), and at
least one redirect URI registered. If not, see Getting Started.
The mechanics of the flow — every parameter, request, and response — are in Authentication. This page is the pragmatic checklist and shows two common integration styles.
Prerequisites checklist
- A tenant provisioned for your organization.
- An application with its
client_id(UUID) and API key (secret). - Your callback URL registered as a redirect URI on the application (exact match, including scheme, host, port, and path).
- Decided your client type:
- Public (SPA, mobile, desktop) → PKCE only, no API key in the client.
- Confidential (server-rendered web app, backend) → PKCE and the API key, kept server-side.
Recommended: use an off-the-shelf OIDC library
Because Curupira ships a standard discovery document, you rarely need to hand-code the flow. Point any OIDC client library at the issuer and it configures itself:
issuer: https://auth.curupira.api.br
discovery URL: https://auth.curupira.api.br/.well-known/openid-configuration
client_id: <YOUR_CLIENT_ID>
redirect_uri: https://app.example.com/callback
scope: openid email profile
response_type: code
PKCE: S256 (required)
Well-tested options: oidc-client-ts / oauth4webapi (browser & Node), openid-client
(Node), AppAuth (iOS/Android), Authlib (Python), coreos/go-oidc (Go). Enable PKCE
(most libraries do by default) and, for confidential clients, configure the token-endpoint
call to send the X-API-Key header.
Confidential (server-side web app) — the flow
- Login route (
GET /login): generatestate+ PKCEcode_verifier/code_challenge, store both in the user's session, and redirect tohttps://auth.curupira.api.br/oauth2/authorize?...(see Authentication §3). - Callback route (
GET /callback): validatestate, thenPOSTtohttps://auth.curupira.api.br/oauth2/tokenwithgrant_type=authorization_code, thecode, the storedcode_verifier, yourclient_id, and theX-API-Keyheader. - Verify the
id_tokensignature against the JWKS; checkiss,aud,exp. - Establish your app's own session from the verified claims (store
sub,email,roles,permissions, and therefresh_tokenserver-side). - Refresh with
grant_type=refresh_tokenwhen the access token nears expiry — and persist the rotated refresh token returned each time.
# Step 2, concretely (server-side, API key stays on the server):
curl -s -X POST https://auth.curupira.api.br/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-API-Key: $CURUPIRA_API_KEY" \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "redirect_uri=https://app.example.com/callback" \
-d "client_id=$CURUPIRA_CLIENT_ID" \
-d "code_verifier=$CODE_VERIFIER"
Public (SPA / mobile) — the flow
Identical, except the client never holds the API key. Security comes from PKCE plus the
exact-match redirect URI. Use a library that keeps tokens in memory (SPAs) or the platform
secure storage (mobile), and send the token request without X-API-Key.
Using roles & permissions in your app
After you verify the id_token, read:
roles— the role names the user holds in this application (e.g.["editor"]).permissions— the aggregated permission strings from those roles.tenant— the tenant slug the user belongs to.
Gate features on these claims. Create the roles/role-groups your app expects in the admin dashboard and assign them to users — see Product Concepts.
Common pitfalls
redirect_urimismatch → the authorize request is rejected. It must match a registered URI character-for-character.- Dropping the rotated refresh token → the next refresh fails with
invalid_grant. Always save the newest one. - Shipping the API key to the browser → treat public clients as PKCE-only; keep the key server-side for confidential clients.
- Trusting an unverified JWT → always validate the signature against the JWKS and check
iss/aud/exp.
Next steps
- How to Get Your API Token — obtaining and using tokens, customer-facing.
- Admin API Guide — automate tenant/app/role management.