Connect Authentik
Authentik connects as a generic OpenID Connect provider. This page covers the Authentik side and the defaults that block sign-in. The protocol settings, the warnings and the sign-in refusals are on Connect an OpenID Connect provider.
Authentik ships three defaults that stop a connection working, so read signing key, issuer mode and email verified before you test a sign-in. If you provision Authentik with Terraform rather than the console, read provisioning as code as well: it has a fourth trap the console never shows you.
Create the provider and application
Section titled “Create the provider and application”In Authentik, a provider holds the OpenID settings and an application makes it visible. Create both.
- Applications → Providers → Create, type OAuth2/OpenID Provider.
- Client type: Confidential. Copy the generated Client ID and Client Secret.
- Redirect URIs: the callback deploybase shows you. Open People and access → Single sign-on, click Set up single sign-on, and it is the first thing on the page, before you type anything. It is the sign-in service’s callback, not a deploybase address, and it is the same for every connection and every team. Add it as a strict match rather than a regular expression.
- Signing Key: pick a certificate. See signing key below; this is the field that is empty by default and breaks sign-in.
- Advanced protocol settings → Scopes: leave the default
openid,profileandemailmappings in place. - Advanced protocol settings → Subject mode: Based on the User’s hashed ID. This is what ties a member to their account, so it has to be a value that never changes. Pick the username or the email and a person who later changes either one arrives as a stranger: their link is dropped and they are refused as though they had never signed in.
- Save, then Applications → Applications → Create, give it a slug such as
deploybase, and select the provider you just made.
The application slug appears in the issuer URL, so pick it before you copy the issuer.
Find the issuer URL
Section titled “Find the issuer URL”Authentik gives each provider its own issuer, built from the application slug, and it ends in a trailing slash:
https://id.example.com/application/o/deploybase/Take the value from the provider page rather than typing it: open the provider and follow the OpenID Configuration URL. The issuer field of that document is the exact value.
The trailing slash is real. It is what Authentik puts in the token’s iss claim, and sign-in compares the two strings exactly. Entering the URL without the slash is the single most common mistake here, so it is not treated as an error: the provider’s spelling is stored and the connection tells you the value was adjusted.
Issuer mode
Section titled “Issuer mode”Authentik has an Issuer mode setting on the provider, under Advanced protocol settings. It has two values:
- Each provider has a different issuer (
per-provider). Use this one. - Same identifier for all providers (
global). This reports one shared issuer for the whole installation, so every application claims to be the same provider.
The global mode cannot be brokered. The discovery document still lives at the per-application path, while the iss claim carries the installation-wide value, so the two never agree and sign-in fails at token verification. The connection is refused at save time with a message naming this setting, rather than left to fail at someone’s first sign-in.
Open the provider, set Issuer mode to “Each provider has a different issuer”, save, and try the connection again. If other systems depend on the global issuer, give deploybase its own provider with its own application slug instead of changing the shared one.
Signing key
Section titled “Signing key”A new Authentik provider has no Signing Key selected. With the field empty, Authentik signs ID tokens with the client secret using HS256, and the sign-in service verifies asymmetric signatures only: it fetches your public keys from the JWKS endpoint, and there is no public key for an HMAC signature. No sign-in can ever succeed, so the connection is refused rather than saved with a warning.
The fix is one field. Open Applications → Providers → your provider, set Signing Key to a certificate (the built-in self-signed authentik Self-signed Certificate is fine, since only Authentik’s own public key is used to verify), and save. Authentik then signs with RS256.
Re-test the connection in deploybase afterwards. The discovery document changes as soon as the key is set.
Email verified
Section titled “Email verified”Authentik 2025.10 and later report email_verified: false for every user by default, whatever the account’s real state. The default email scope mapping does not emit a verified flag, and the missing flag reads as unverified.
Sign-in matches people by a verified address, so on a stock Authentik every member is refused. The warning is raised on every Authentik connection for this reason: it is not something we can see from the discovery document, and it costs an afternoon to find.
Two ways forward:
-
Customise the email scope mapping in Authentik. Under Customisation → Property Mappings, create a scope mapping with the scope name
emailand this expression, then select it on your provider in place of the built-inemailmapping:return {"email": request.user.email,"email_verified": True,}Written that way it asserts every address in that Authentik is verified, which has the same reach as option 2 and is worth the same thought. To scope it, return the flag from something you actually trust, for example whether the account came from a directory you sync:
return {"email": request.user.email,"email_verified": request.user.attributes.get("email_verified", False),} -
Turn on Trust unverified email addresses on the connection. Addresses from this Authentik are then accepted whether or not it calls them verified. Only the team Owner can turn it on, because anyone who can set an address in that Authentik can then sign in as the holder of that address in your team.
Pick the second option only where you control who can register and change addresses in that Authentik installation.
Provisioning as code
Section titled “Provisioning as code”Everything above can be committed rather than clicked, with the goauthentik/authentik Terraform provider or an Authentik blueprint. Nothing about the connection needs a person in the console.
The two values that look like they have to come from deploybase first do not:
- The callback is one value per environment, the same for every connection and every team, so you can write it before you open deploybase at all. Read it once from People and access → Single sign-on → Set up single sign-on.
- The issuer is built from the application slug, which you choose yourself in your own configuration:
https://id.example.com/application/o/<your slug>/, trailing slash included.
So the whole connection is knowable before anything is applied.
Grant types
Section titled “Grant types”This is the trap that only exists for code. authentik_provider_oauth2 defaults grant_types to an empty list, and an empty list rejects every authorization request. The console never produces it, because creating a provider through the UI fills the field in.
Nothing catches it for you. Authentik still advertises authorization_code in its discovery document whatever the provider resource says, so the connection test passes every check and the first person to try signing in is the one who finds out. Set it explicitly:
grant_types = ["authorization_code", "refresh_token"]A provider that works
Section titled “A provider that works”locals { app_slug = "deploybase" issuer = "https://id.example.com/application/o/${local.app_slug}/"
# From Settings -> Single sign-on -> Set up single sign-on. One value per # environment, identical for every team, so it is safe to hardcode. callback = "https://auth.deploybase.eu/idps/callback"}
resource "authentik_provider_oauth2" "deploybase" { name = "deploybase" client_type = "confidential" client_id = var.deploybase_client_id client_secret = var.deploybase_client_secret
# Empty by default, and empty rejects every sign-in. grant_types = ["authorization_code", "refresh_token"]
# Empty by default, which makes Authentik sign with HS256 and the connection # is refused. Any certificate works; only the public half is ever used. signing_key = data.authentik_certificate_key_pair.generated.id
# "global" reports one issuer for the whole installation and is refused. issuer_mode = "per_provider"
# A subject that survives a username or email change. sub_mode = "hashed_user_id"
redirect_uris = [{ matching_mode = "strict" url = local.callback }]
property_mappings = [ data.authentik_property_mapping_provider_scope.openid.id, data.authentik_property_mapping_provider_scope.profile.id, authentik_property_mapping_provider_scope.email_verified.id, ]}
# Replaces the built-in email mapping, which omits the verified flag.# See "Email verified" above before using this as written.resource "authentik_property_mapping_provider_scope" "email_verified" { name = "deploybase-email-verified" scope_name = "email" expression = <<-EOT return { "email": request.user.email, "email_verified": True, } EOT}
resource "authentik_application" "deploybase" { name = "deploybase" slug = local.app_slug protocol_provider = authentik_provider_oauth2.deploybase.id}With that applied, create the connection in deploybase with the wizard or with the API. Both are described under connect without the wizard.
Live 022265e7 built