Integrating real applications like Grafana, Nextcloud, Portainer, and Gitea with Authentik OIDC, a login flow walkthrough, token verification with jwt.io, and troubleshooting common errors like redirect mismatches and scope issues.

In episode 9, the token contents were fully in your hands through property mappings. Now it's time for the real test: connecting real applications. Grafana, Nextcloud, Portainer, Gitea — all support OIDC, and all can use Authentik as a single login gateway.
The trick is recognizing the pattern. Each application has a different configuration point, but the requested parameters are always the same: the discovery URL or endpoint, client ID, client secret, redirect URI, and scopes. You already have all those ingredients from episodes 8 and 9. All that's left is translating them into each application's configuration language.
Before touching any application, make sure these four things are ready:
openid profile email, add offline_access if the application needs refresh tokens.The redirect URI is a contract between the application and Authentik: both sides must write the exact same value.
Grafana supports OIDC through the generic OAuth module. First, create an OAuth2 provider in Authentik with the redirect URI https://grafana.example.com/login/generic_oauth. Then add the configuration in the grafana.ini file.
[auth.generic_oauth]
enabled = true
name = Authentik
allow_sign_up = true
client_id = <client-id>
client_secret = <client-secret>
scopes = openid profile email
auth_url = https://auth.example.com/application/o/authorize/
token_url = https://auth.example.com/application/o/token/
api_url = https://auth.example.com/application/o/userinfo/allow_sign_up makes new users auto-created on first login — convenient for a homelab. For role control, you can map groups from the groups claim (built in episode 9) to Grafana roles, for example Viewer and Admin.
Nextcloud uses the official user_oidc app. After installing it, go to Settings > OpenID Connect clients and register Authentik with:
https://auth.example.com/application/o/nextcloud/.well-known/openid-configurationhttps://nextcloud.example.com/apps/user_oidc/codeAuto-discovery lets Nextcloud read all endpoints and JWKS keys directly from Authentik — a big advantage over manual configuration, because signing key changes on the provider don't need to be translated again.
Portainer is configured through its UI: Settings > Authentication, then enable OAuth. It only asks for endpoint URLs and client credentials — there's no configuration file to edit:
https://auth.example.com/application/o/authorize/https://auth.example.com/application/o/token/https://auth.example.com/application/o/userinfo/https://portainer.example.com (shown by Portainer during configuration).Enable Autoprovisioning so users who log in via Authentik automatically get an account. Without it, users must be created manually first.
Gitea provides OAuth2 sources via Site Administration > Authentication Sources. Choose the OpenID Connect provider, then fill in:
https://auth.example.com/application/o/gitea/.well-known/openid-configurationhttps://gitea.example.com/user/oauth2/<source-name>/callbackFor reference, the same configuration can be represented in app.ini:
[oauth2_client.1]
NAME = Authentik
PROVIDER = openidConnect
CLIENT_ID = <client-id>
CLIENT_SECRET = <client-secret>
AUTO_DISCOVERY_URL = https://auth.example.com/application/o/gitea/.well-known/openid-configurationTip
Always start with the discovery URL rather than copying endpoints manually. It automatically loads the authorize, token, userinfo, and JWKS endpoints. If Authentik changes or the issuer mode is reset, applications with auto-discovery will stay in sync.
When all applications are configured, the SSO flow you see will look exactly like this:
authorize endpoint.code.code at the token endpoint using its client credentials.From the user's perspective, login is the same single door for all applications. From the admin's perspective, one account and one set of policies controls everything.
After the first successful login, verify what the application actually receives. Paste the ID token into jwt.io — this tool decodes the header, payload, and signature. What to check:
iss: the issuer matches the application.aud: contains the application's client ID.exp: a sensible validity period.email, groups, or the custom claims you created.Without opening a browser, you can decode a token from the command line:
echo "<id-token>" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq .Important
jq and base64 here are only for peeking at the token's contents — not authenticity verification. To verify the signature, use the public key from the JWKS endpoint. Accepting a token just because its payload is readable is a dangerous habit.
Even with correct configuration, some errors keep recurring. Learn to recognize the patterns:
signing key (not HMAC) is selected on the provider.email_verified mapping as in episode 9, or verify the user's email.Warning
When checking integration issues, open the Authentik logs and the application logs side by side. Errors usually appear twice: once from the application side (the parameters it sent), once from the Authentik side (the parameters it rejected). Matching both speeds up diagnosis dramatically.
Key points from this episode:
grafana.ini, Nextcloud via the user_oidc app, Portainer via the UI, Gitea via authentication sources.base64/jq for peeking at token contents; JWKS for verifying signatures.All modern applications now log in through a single door. But what about applications that don't support OIDC at all — old tools that only accept a plain reverse proxy? In episode 11, you'll get to know the proxy provider and outpost, Authentik's weapon for wrapping any application with authentication, without changing a single line of code.