Connect your first app
Register an OIDC application and run the full authorization code flow with PKCE: authorize, token exchange, id_token, userinfo.
This quickstart connects an application to Obexal with OpenID Connect, end to end: you register a client, then run the authorization code flow with PKCE using nothing but curl and a browser. If your application only speaks SAML, Obexal also acts as a SAML 2.0 identity provider.
The discovery document lists every endpoint used below:
curl https://accounts.obexal.com/.well-known/openid-configurationEvery example uses accounts.obexal.com, the default domain. If your organization uses a custom domain, replace it accordingly.
Register the application
In the admin console, open Applications and create a client:
- Name: whatever your team will recognize.
- Type:
publicfor a browser or mobile app (no secret, PKCE only),confidentialfor a server-side app (receives a client secret). - Redirect URIs: the exact absolute
http(s)URLs the user returns to after signing in. Matching is strict string equality, no wildcards.
For a well-known SaaS (Slack, Jira and others), start from the integration catalog instead: it pre-fills the configuration and creates a ready-to-use client.
Copy the clientId (for example app_a1b2c3). For a confidential client, the clientSecret is displayed once at creation: store it immediately, it is never shown again.
Prepare PKCE
PKCE is mandatory for every client, public or confidential, and only the S256 method is accepted:
VERIFIER=$(openssl rand -base64 60 | tr '+/' '-_' | tr -d '=\n')
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -binary -sha256 | openssl base64 | tr '+/' '-_' | tr -d '=\n')Get an authorization code
Send the user's browser to /oauth/authorize (shown here on several lines for readability):
https://accounts.obexal.com/oauth/authorize
?response_type=code
&client_id=app_a1b2c3
&redirect_uri=https%3A%2F%2Fapp.example.eu%2Fcallback
&scope=openid%20profile%20email
&state=xyz
&nonce=n0abc123
&code_challenge=<CHALLENGE>
&code_challenge_method=S256- Without a session, the user is first redirected to the hosted sign-in page, signs in, then returns to the same authorize URL.
- On success, the browser is redirected to
https://app.example.eu/callback?code=<CODE>&state=xyz. scopemust includeopenid; the supported scopes areopenid,profileandemail.- If the application is restricted to groups, a user outside those groups receives
error=access_denied.
Exchange the code for tokens
The code is single use and short-lived. Exchange it at /oauth/token (form encoded, no cookie, no CSRF):
curl -sS -X POST https://accounts.obexal.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=<CODE>' \
-d 'redirect_uri=https://app.example.eu/callback' \
-d 'client_id=app_a1b2c3' \
-d "code_verifier=$VERIFIER"{
"access_token": "<JWT>",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "<opaque>",
"id_token": "<JWT>",
"scope": "openid profile email"
}A confidential client authenticates in addition with its secret:
curl -sS -u "app_conf:$CLIENT_SECRET" -X POST https://accounts.obexal.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' -d 'code=<CODE>' \
-d 'redirect_uri=https://app.example.eu/callback' -d "code_verifier=$VERIFIER"Replaying the code, or presenting a wrong code_verifier, returns 400 {"error":"invalid_grant"}.
Read the id_token
The id_token is a JWT signed with RS256 (the only algorithm). Verify it against the keys published at /.well-known/jwks.json, then read the claims:
| Claim | Content |
|---|---|
iss, aud, exp, iat | issuer, your client_id, expiry, issue time |
sub | stable user identifier |
auth_time | time of the interactive sign-in |
nonce | echoed if you sent one (replay protection) |
email, email_verified | with the email scope |
given_name, family_name, name, locale | with the profile scope, when set in the directory |
groups | names of the user's groups, omitted when empty |
Full verification rules: Validate tokens.
Test the userinfo endpoint
curl -sS https://accounts.obexal.com/oauth/userinfo \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"sub": "8b1e2c4a-7f3d-4e5b-9a6c-1d2e3f4a5b6c",
"email": "alice@example.eu",
"email_verified": true,
"given_name": "Alice",
"family_name": "Martin",
"name": "Alice Martin",
"locale": "fr"
}The claims follow the scopes carried by the access token; a missing, invalid or expired token returns 401.
Limits and next steps
response_type=codeonly: no implicit or hybrid flow.- PKCE with
S256is required;plainis refused. - Tokens are signed with RS256 only.
- Refresh tokens rotate on every use; reusing an old one revokes the whole chain.
Continue with OIDC and OAuth 2.1 in depth, sessions and logout and, for hardened clients, PAR, DPoP and private_key_jwt.