This episode integrates real clients with Authelia as an OIDC provider: Grafana, Gitea or Forgejo, Nextcloud, and Portainer. From client registration with redirect URI, scope, and grant type, the OIDC login flow on the application side, up to how to verify tokens.

In episode 18 you understood scopes and claims — the language clients use to request identity data. Now it's time to talk to real applications. Episode 19 guides you through integrating four real clients — Grafana, Gitea or Forgejo, Nextcloud, and Portainer — with Authelia as the identity provider.
The flow we use is always the same, regardless of the application: the client redirects the user to Authelia's authorization endpoint, the user logs in and approves consent, Authelia returns a code, the client exchanges it for a token, then uses that token to fetch the user's identity.
All integrations start from one place: registering the client in identity_providers.oidc.clients. Four things are the most common sources of problems:
redirect_uris — must match exactly what the application requests, including scheme, host, path, and trailing slash.scopes — the registered scopes must cover everything the application requests.grant_types — for interactive web applications, authorization_code is the standard; add refresh_token if the app needs long sessions.secret — using a hash instead of plaintext is recommended; the $plaintext$ prefix makes development easier.Grafana supports OAuth via auth.generic_oauth. Register the client in Authelia, then set the configuration in grafana.ini:
[auth.generic_oauth]
enabled = true
name = Authelia
client_id = grafana
client_secret = client-secret
scopes = openid profile email groups
auth_url = https://auth.example.com/api/oidc/authorization
token_url = https://auth.example.com/api/oidc/token
api_url = https://auth.example.com/api/oidc/userinfo
allowed_groups = grafana-admin grafana-editorallowed_groups matches the groups claim from the token. With this, only users in the grafana-admin or grafana-editor groups may log in — access control centralized in Authelia, not scattered across each application.
Gitea and Forgejo use the same pattern: enable OAuth2 support, then register Authelia as an authentication source through the admin panel, not a config file.
[oauth2_client]
ENABLE = trueIn the Gitea or Forgejo admin panel, create a new Authentication Source of type OAuth2 with provider OpenID Connect. Fill in the OpenID Connect Auto Discovery URL with https://auth.example.com/.well-known/openid-configuration, then enter the client_id, client_secret, and scopes openid profile email groups. Both applications use discovery to find all Authelia endpoints automatically, so there's no need to fill in the authorization and token URLs manually.
Tip
The OpenID Connect Auto Discovery field uses Authelia's discovery endpoint. If that field is filled correctly, everything flows automatically — this is why OIDC is more convenient than plain OAuth 2.0.
Nextcloud uses the oidc_login app. Its configuration goes in config/config.php:
'oidc_login_provider_url' => 'https://auth.example.com/.well-known/openid-configuration',
'oidc_login_client_id' => 'nextcloud',
'oidc_login_client_secret' => 'client-secret',
'oidc_login_auto_redirect' => false,
'oidc_login_disable_registration' => true,
'oidc_login_scope' => 'openid profile email',
'oidc_login_default_claim' => 'preferred_username',oidc_login_default_claim determines which claim is used to create the local account. Remember the lesson from episode 18: preferred_username and email are only for account provisioning, while the stable account relation still references sub on the Authelia side.
Portainer is configured via the Settings → Authentication → OAuth menu. Since it has no automatic discovery, all endpoints are filled manually. The client in Authelia:
identity_providers:
oidc:
clients:
- id: portainer
description: Portainer management
secret: '$plaintext$client-secret'
redirect_uris:
- https://portainer.example.com
scopes:
- openid
- profile
- email
- groups
grant_types:
- authorization_code
response_types:
- codeOn the Portainer side, fill in the following fields:
portainer.https://auth.example.com/api/oidc/authorization.https://auth.example.com/api/oidc/token.https://auth.example.com/api/oidc/userinfo.https://portainer.example.com — must match redirect_uris exactly.sub.openid profile email groups.Note that Portainer's redirect URL is just the host without a path. A single character mismatch will fail the flow at the callback step.
When everything is installed, the behind-the-scenes flow works like this:
https://auth.example.com/api/oidc/authorization along with client_id, redirect_uri, scope, and state.redirect_uri.https://auth.example.com/api/oidc/token.All these steps are automatic — you just need to make sure the configuration matches on both sides.
Basic verification you can do:
curl -s https://auth.example.com/.well-known/openid-configuration | jq .
curl -s https://auth.example.com/jwks.json | jq .keys[0].algThe first endpoint should return complete OIDC metadata; the second carries the public RSA key for validating token signatures. After the first successful login, decode the token produced — for example via the browser's developer panel — and check the sub, name, and groups claims.
Warning
If login always fails halfway, the diagnostic order starts with the most common mistakes: a mismatched redirect URI, a typo in the client secret, a requested scope not yet registered, or X-Forwarded-Proto not being forwarded so Authelia forces HTTPS.
In this episode you integrated four real clients:
auth.generic_oauth with Authelia's authorization, token, and userinfo endpoints..well-known/openid-configuration.sub claim is the stable account relation key.One component we haven't fully covered is the consent screen that appears at login. In episode 20, we discuss Consent Management — how to set the consent mode per client, pre-configured consent, up to revoking permissions already granted. See you there!