Obexal Docs

Docs/Get started/Connect your first app

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-configuration
Note

Every 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: public for a browser or mobile app (no secret, PKCE only), confidential for 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.
  • scope must include openid; the supported scopes are openid, profile and email.
  • 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:

ClaimContent
iss, aud, exp, iatissuer, your client_id, expiry, issue time
substable user identifier
auth_timetime of the interactive sign-in
nonceechoed if you sent one (replay protection)
email, email_verifiedwith the email scope
given_name, family_name, name, localewith the profile scope, when set in the directory
groupsnames 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=code only: no implicit or hybrid flow.
  • PKCE with S256 is required; plain is 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.