Configuring the OAuth2 and OIDC provider in Authentik: confidential and public client types, redirect URIs, signing keys, scopes, and sub-modes, plus how to test the authorization code flow end-to-end with token and userinfo endpoints.

In episode 7, Authentik's internal authentication flow was complete with MFA. But all of that still stands alone — other applications can't use it yet. Time to build the industry-standard bridge: OAuth2 and OpenID Connect (OIDC).
There's one difference that must be clear from the start. OAuth2 is an authorization protocol: it gives applications permission to access resources on behalf of a user. OIDC is built on top of OAuth2 and adds an authentication layer: it proves who the user is via a token carrying identity. To use an analogy, OAuth2 is like a building area access pass, while OIDC is like the identity card you carry inside. Authentik acts as the identity provider issuing both.
First step: open the Admin interface, go to Applications > Providers, and create a new provider of type OAuth2/OIDC. There are several important decisions in this form.
The client type determines how much trust can be placed in the requesting application:
client_id it's also given a client_secret. Like a permanent employee holding the key to the server room.This is the field that most often becomes a source of problems. The redirect URI is the list of destination addresses after login completes — Authentik will only send the authorization code to registered URIs. Match it exactly: scheme, host, path, even the trailing slash. If left empty, Authentik uses the first URI the application requests as the stored value.
The second important decision:
openid is required for OIDC; profile provides basic data, email provides the email address, and offline_access enables issuing refresh tokens. If a client doesn't request any scope, Authentik treats all default scopes as requested.sub (subject) value is formed — for example UUID-based or username-based — as well as the issuer mode controlling the iss value in tokens: unique per application based on its slug, or uniform across all providers.Tip
Start with the per-provider issuer mode (default) because it matches the slug-based discovery and JWKS structure. The global mode only needs consideration if an application forces a single issuer for all clients.
All endpoints live under the /application/o/ prefix. You'll use this table constantly when configuring applications:
| Function | Endpoint |
|---|---|
| Authorization | /application/o/authorize/ |
| Token | /application/o/token/ |
| User Info | /application/o/userinfo/ |
| JWKS | /application/o/<slug>/jwks/ |
| OpenID Configuration | /application/o/<slug>/.well-known/openid-configuration |
| End Session | /application/o/<slug>/end-session/ |
Important
Because the endpoints above are global, you must not create applications with the slugs authorize, token, userinfo, introspect, or revoke — their names would collide with the OAuth2 endpoints and break routing.
This is the most common flow for web applications with a backend. It runs like an airport check-in queue — each step hands proof to the next:
authorize endpoint with client_id, redirect_uri, scope, and state.redirect_uri carrying a code.code at the token endpoint using client_id and client_secret.access_token and id_token (and a refresh_token if the offline_access scope was requested).userinfo endpoint.Warning
An authorization code can only be used once and expires quickly. If an application receives a code twice or the code is old, the exchange will fail — this isn't an Authentik bug, it's protection against code theft.
A provider isn't visible to users until it's bound to an application. An application is the object representing the real application in front of users: name, slug, icon, and launch URL. This is where the provider is connected, and where policies from episode 6 are bound to restrict who may open that application.
The recommended order: create the OAuth2 provider first, then create the application and choose that provider as its backend. The application slug also determines the discovery URL and issuer.
Before connecting a real application, test first. The fastest way is looking at the discovery document Authentik generates — the .well-known/openid-configuration file that automatically contains all endpoints.
curl -s https://auth.example.com/application/o/grafana/.well-known/openid-configurationThe response contains all endpoints, the issuer, and the supported signature types. From here, test the token exchange using the client_credentials grant to make sure the client credentials are correct.
curl -s https://auth.example.com/application/o/token/ \
-u "<client-id>:<client-secret>" \
-d "grant_type=client_credentials" \
-d "scope=openid profile email"Then fetch the profile via the userinfo endpoint using the newly obtained token.
curl -s https://auth.example.com/application/o/userinfo/ \
-H "Authorization: Bearer <access-token>"To test the full flow with a browser, use an OAuth2 debugger — many are freely available on the web. Fill in the registered client_id, redirect_uri, and scopes, then follow the flow until the browser returns carrying a code. This is the closest simulation to a real application's experience.
Once successful, look at the standard claims in the ID token:
iss: issuer — where the token was issued.aud: audience — the application the token is intended for.sub: subject — the user's identity (its form depends on the sub-mode above).exp and iat: expiry time and issued-at time.nonce: a random value from the request to prevent replay.Other claims like email, name, and groups only appear if the related scope is requested and a property mapping provides them. How the token's contents are shaped is the topic of the next episode.
Key points from this episode:
The token is now issued, but its contents are still plain. In episode 9, you'll take full control of the token's contents through property mappings and claims — deciding which claims applications may carry, including custom group mappings of your own.