Skip to content

Any OIDC client

Integrate Authio with any OpenID Connect library: discovery, the authorization code flow with PKCE, SPAs and native apps, token validation, refresh and sign-out.

Every realm is a standard OpenID Connect provider, so any certified OIDC or OAuth 2.0 library works without an Authio SDK. Give the library the realm’s issuer, a client ID and, for a confidential client, its secret; it reads everything else from discovery. This page shows the requests on the wire, so you know what the library does and can check it with curl.

Discovery

The issuer is https://authio.api.hyperio.tech/realms/{realm}. Its discovery document lists the endpoints, the scopes and grants the realm’s clients use, and the supported algorithms. It is served at the OpenID Connect address and at both forms of RFC 8414, with CORS open to any origin.

TerminalBash
curl https://authio.api.hyperio.tech/realms/acme/.well-known/openid-configuration
Discovery fieldPath under the issuer
authorization_endpoint/protocol/openid-connect/auth
token_endpoint/protocol/openid-connect/token
userinfo_endpoint/protocol/openid-connect/userinfo
jwks_uri/protocol/openid-connect/certs
introspection_endpoint/protocol/openid-connect/introspect
revocation_endpoint/protocol/openid-connect/revoke
end_session_endpoint/protocol/openid-connect/logout
device_authorization_endpoint/protocol/openid-connect/device/auth

Configure your library with the issuer rather than individual endpoints. The full list, with every parameter, is in OIDC endpoints.

Register the client

SettingWhat to choose
Client authenticationConfidential for anything with a server that can keep a secret. Public (method none) for SPAs, mobile and desktop apps.
Token endpoint authentication methodclient_secret_basic (the default) or client_secret_post. The token endpoint accepts only the registered method.
Grant typesauthorization_code, plus refresh_token if you renew sessions.
PKCERequired by default. Public clients must keep it on and allow S256.
Redirect URIsEvery address the sign-in may come back to, compared in full. Only http and https, without wildcards or fragments.
Post-logout redirect URIsWhere sign-out may send people back. Empty accepts the redirect URIs.
ScopesEvery scope the app will ask for, such as openid profile email offline_access.

Authorization code with PKCE

  1. Create a verifier and its challenge

    The verifier is a random string the app keeps; the challenge is its SHA-256 hash in base64url. Also create a random state and nonce for this sign-in.

    TerminalBash
    CODE_VERIFIER=$(openssl rand -hex 32)
    CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
  2. Send the browser to the authorization endpoint

    Authorization requestPlain text
    https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/auth
      ?response_type=code
      &client_id=acme-web
      &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
      &scope=openid%20profile%20email%20offline_access
      &state=STATE
      &nonce=NONCE
      &code_challenge=CODE_CHALLENGE
      &code_challenge_method=S256

    Optional parameters: prompt (none, login, select_account, consent), max_age, login_hint, ui_locales, id_token_hint, organization and resource — one resource server identifier, to get an access token for that API. display=popup with response_mode=web_message runs the sign-in in a popup.

  3. Receive the code

    After the hosted login, the browser comes back with code, state and iss (RFC 9207). Check that state is the one you sent and iss is the realm’s issuer. The code is single-use and expires after 30 seconds, so exchange it right away. A refusal comes back with error instead, also with state and iss.

    CallbackPlain text
    https://app.example.com/callback?code=AUTHORIZATION_CODE&state=STATE&iss=https%3A%2F%2Fauthio.api.hyperio.tech%2Frealms%2Facme
  4. Exchange the code for tokens

    Confidential clientBash
    curl -X POST https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/token \
      -u 'acme-web:YOUR_CLIENT_SECRET' \
      -d grant_type=authorization_code \
      -d code=AUTHORIZATION_CODE \
      --data-urlencode redirect_uri=https://app.example.com/callback \
      -d code_verifier=$CODE_VERIFIER
    ResponseJSON
    {
      "access_token": "eyJhbGciOiJSUzI1NiIs...",
      "token_type": "Bearer",
      "expires_in": 1800,
      "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
      "refresh_expires_in": 3600,
      "id_token": "eyJhbGciOiJSUzI1NiIs...",
      "scope": "openid profile email offline_access"
    }

    Validate the ID token as OpenID Connect Core asks: signature, iss, aud equal to your client ID, exp, and nonce equal to the one you sent. Presenting the same code twice revokes the session it was issued in.

SPAs, mobile and native apps

Apps that run on the user’s device cannot keep a secret, so they use a public client with PKCE and S256. Use a library that does the flow above for you — an OpenID-certified one for your platform.

  • SPAs redirect back to an https page of the app. The discovery document, the JWKS and the token endpoint answer any origin, so the code can be redeemed from the browser. To keep tokens out of the browser altogether, put a Backend-for-Frontend in front of the SPA.
  • Desktop apps and CLIs listen on the loopback interface. Register http://127.0.0.1/callback and turn on Loopback on any port: Authio then accepts any port on 127.0.0.1, [::1] and localhost (RFC 8252).
  • Mobile apps come back through an https link the app claims (universal links or app links). Redirect URIs must be http or https, so custom URI schemes are not accepted.
  • Devices without a browser, such as TVs, use the device flow instead.

Validate access tokens

An API checks each access token locally against the realm’s keys; a valid token needs no call to Authio. Use your platform’s JWT library with these rules:

CheckRule
SignatureA key from jwks_uri, matched by kid. Accept asymmetric algorithms only: RS, PS and ES 256/384/512. Never none.
Issueriss equals the realm’s issuer exactly.
Audienceaud is your API’s resource server identifier. Without resource, tokens carry the client’s audience, Authio by default.
Lifetimeexp in the future, nbf in the past, with a small clock skew.
Token typeThe typ claim is Bearer. ID and refresh tokens are signed by the same keys and must be refused.
Permissionsscope holds the granted scopes; roles are in the *_resource_access claims when the roles scope was granted.

A JWT stays valid until it expires, even after the session ends. To catch revocations and sign-outs sooner, or to accept opaque or encrypted tokens, ask the introspection endpoint (RFC 7662) as a confidential client allowed to introspect the API’s tokens. It always answers 200, with active: false and nothing else for any token that is not valid.

TerminalBash
curl -X POST https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/introspect \
  -u 'orders-api:YOUR_CLIENT_SECRET' \
  -d token=ACCESS_TOKEN \
  -d token_type_hint=access_token

Refresh tokens

A refresh token is issued only when offline_access is granted. Present it with the same client authentication as the code exchange:

TerminalBash
curl -X POST https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/token \
  -u 'acme-web:YOUR_CLIENT_SECRET' \
  -d grant_type=refresh_token \
  -d refresh_token=REFRESH_TOKEN
  • Refresh tokens rotate by default: store the new refresh_token from every response.
  • Within the reuse window (10 seconds by default), a repeated request gets the same new refresh token. A spent token presented after that ends the whole session and is reported as possible theft.
  • The renewal keeps auth_time, amr and the original resource. It returns an id_token only if the client turns on ID Token on Refresh.
  • A refusal is invalid_grant: sign the person in again.

UserInfo

The UserInfo endpoint returns the person’s current claims, read fresh on every call, as far as the token’s scopes allow. It needs a token granted the openid scope. It answers JSON unless the client asked for a signed or encrypted JWT: see Signed and encrypted UserInfo.

TerminalBash
curl https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/userinfo \
  -H 'Authorization: Bearer ACCESS_TOKEN'

Sign out

To end the person’s session, send the browser to the end-session endpoint with the ID token you received. Only a valid id_token_hint ends the session — an expired one is accepted. Without it, Authio shows a confirmation screen instead.

Sign-out requestPlain text
https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/logout
  ?id_token_hint=ID_TOKEN
  &post_logout_redirect_uri=https%3A%2F%2Fapp.example.com%2F
  &state=STATE

post_logout_redirect_uri is followed only if it is registered on the client, compared like a redirect URI; state comes back unchanged. To drop tokens without signing the person out of the realm’s other apps, revoke the refresh token instead (RFC 7009): this ends the client’s session and keeps single sign-on.

TerminalBash
curl -X POST https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/revoke \
  -u 'acme-web:YOUR_CLIENT_SECRET' \
  -d token=REFRESH_TOKEN \
  -d token_type_hint=refresh_token

Apps that must learn when a session ends elsewhere — an administrator, a suspension, another tab — can register a back-channel or front-channel logout URI. See Sessions and SSO.

Next steps