Company login with authentik¶
This page connects authentik in front of MEHO's Keycloak. When you are done:
- People log in to MEHO with their authentik account and pass authentik's second login step (MFA).
- They come back with a MEHO token from Keycloak.
- Their authentik group decides their MEHO group, and with it their tenant and their role.
- You manage people only in authentik. Nobody needs the Keycloak admin console for daily work.
MEHO still trusts only Keycloak. Keycloak works here as a broker: a go-between for two login systems. It sends people on to authentik, and then it writes the MEHO token. Service accounts (agents, check runners, add-ons) stay in Keycloak and do not use authentik.
person (meho CLI, web console, Claude Code)
| 1. "log me in to MEHO"
v
Keycloak (realm meho) ------ 2. sends the person on -----> authentik
(no passwords, no MFA here) <-- 3. "this is Jane, groups: MEHO-Operators"
| 4. MEHO token: tenant_id, tenant_role, approver
v
MEHO
There are two ways to run Keycloak, and this page covers both:
- The built-in Keycloak that the MEHO Helm chart installs
(
coreKeycloak.enabled: true). You set up the broker with a few Helm values. Follow Part 1 and Part 2. - Your own Keycloak, set up with Keycloak realm setup. You set up the broker by hand. Follow Part 1 and Your own Keycloak.
Other company login systems that speak OpenID Connect (Entra ID, Okta, ADFS) work the same way. Only the clicks in Part 1 differ.
Before you start¶
- A MEHO install (or a planned one) with a DNS name, for example
meho.example.com. This page writes it as<host>. - An authentik that people already use, for example
authentik.example.com. The menu names below come from authentik 2026.8. - Admin access to authentik.
- If a private CA signs authentik's certificate: that CA as a PEM file. Keycloak calls authentik directly to get the login result, so it must trust this certificate.
- Every person who will use MEHO has an email address, a first name and a last name in authentik. If one is missing, Keycloak shows the person a short form to fill it in at their first login.
How the groups work¶
MEHO has five role groups. Each group gives a role:
| MEHO group | Role in MEHO (tenant_role) |
What the person can do |
|---|---|---|
MEHO-Readers |
read_only |
Read only. |
MEHO-Operators |
operator |
Run actions. Risky changes wait until a second person approves. |
MEHO-SA-Operators |
operator |
Same as Operators for now. |
MEHO-Admins |
tenant_admin |
Manage the tenant. |
MEHO-Approvers |
read_only, plus approver: true |
Approve other people's changes. Cannot run actions. |
The rule: put each person in exactly one of these groups.
- A person in no group gets no tenant. MEHO refuses them with
401 missing_tenant_claim. - A person in two groups is refused with
401 multiple_role_groups. - Operators and admins can already approve other people's changes, so nobody needs two groups.
The easiest setup is to give the authentik groups the same five names.
You can also use groups you already have and map them (see groupMap
in Part 2).
Part 1: authentik¶
1. Create the groups¶
In authentik, open Directory > Groups > New Group. Create the five groups from the table above, or pick five groups you already have. Put each person in exactly one of them.
2. Create the application and the provider¶
Open Applications > Applications > New Application, and choose with New Provider... from the arrow menu.
- Application Name:
MEHO. Slug:meho. The slug is part of the addresses below. This page usesmeho. - Provider type: OAuth2/OpenID Provider.
- Authorization Flow:
default-provider-authorization-implicit-consent. People then see no extra "allow this app?" page. - Client Type: Confidential (a client that has a secret). Copy the Client ID and the Client Secret. You need both in Part 2.
-
Redirect URIs/Origins (RegEx): Add entry, mode Strict, URL:
- built-in Keycloak:
https://<host>/auth/realms/meho/broker/company/endpoint - your own Keycloak:
https://<keycloak-host>/realms/<realm>/broker/company/endpoint
This is the address authentik sends people back to.
companyis the short name of the login in Keycloak. With the built-in Keycloak, the install notes print the exact address. - built-in Keycloak:
-
Signing Key: pick an RSA certificate, for example the self-signed certificate that authentik makes. This is required. Without a signing key, authentik signs with HS256 and the client secret, and it publishes no public key. Keycloak then cannot check the token, and the login fails.
- Grant Types: Authorization Code must be on.
- Under Advanced protocol settings:
- Scopes:
openid,profileandemail. Theprofilescope carries thegroupslist (the names of the person's groups). - Include claims in id_token: keep it on (the default). Keycloak reads the groups from the ID token.
- Issuer mode: keep the default, "Each provider has a different issuer, based on the application slug".
- Scopes:
- Bindings (recommended): Bind existing policy/group/user, and
bind the five groups. Then authentik refuses people who are in none
of them. Without bindings, such a person reaches Keycloak, and MEHO
refuses them with
401 missing_tenant_claim.
3. Note the discovery address¶
The discovery address of the application is:
https://authentik.example.com/application/o/meho/.well-known/openid-configuration
Check that it works and that it has signing keys:
curl -fsS https://authentik.example.com/application/o/meho/.well-known/openid-configuration \
| jq '{issuer, jwks_uri, id_token_signing_alg_values_supported}'
jwks_uri must be set, and the list of signing methods must hold
RS256.
Part 2: MEHO (built-in Keycloak)¶
This part is for an install with coreKeycloak.enabled: true. The keys
of the built-in Keycloak are described in the chart's
values.yaml
under coreKeycloak.
1. Store the client secret¶
Put the authentik client secret into a Kubernetes Secret in MEHO's namespace. The chart reads it by name and never prints it.
read -rs CLIENT_SECRET # paste the authentik client secret; it is not shown
kubectl create secret generic meho-company-login -n meho \
--from-literal=client_secret="$CLIENT_SECRET"
unset CLIENT_SECRET
If a private CA signs authentik's certificate, also store the CA in a ConfigMap:
kubectl create configmap company-login-ca -n meho --from-file=ca.crt=<authentik-ca.pem>
2. Add the Helm values¶
Add this to your values file, next to your other coreKeycloak
values:
coreKeycloak:
enabled: true
hostname: meho.example.com # the same as ingress.host
firstTenantId: "<your tenant UUID>"
companyLogin:
enabled: true
alias: company # part of the redirect address
displayName: authentik # the button name on the Keycloak login page
discoveryUrl: https://authentik.example.com/application/o/meho/.well-known/openid-configuration
clientId: "<the authentik Client ID>"
clientSecret:
secretName: meho-company-login
secretKey: client_secret
scopes: openid profile email # keep profile: it carries the groups
groupsClaim: groups
groupMap: # authentik group -> MEHO group
MEHO-Readers: MEHO-Readers
MEHO-Operators: MEHO-Operators
MEHO-SA-Operators: MEHO-SA-Operators
MEHO-Admins: MEHO-Admins
MEHO-Approvers: MEHO-Approvers
autoRedirect: true # go straight to authentik
caBundle: # only for a private CA; else leave the name empty
configMapName: company-login-ca
key: ca.crt
What the keys mean:
groupMap: one line per group. The left side is the authentik group name. The right side is the MEHO group. Only the groups you list here give access. With an emptygroupMap, nobody gets a MEHO group. If your authentik groups have other names, write those names on the left, for exampleInfra-Team: MEHO-Operators.autoRedirect: true: Keycloak never shows its own login page. It sends everyone straight to authentik.caBundle: Keycloak trusts this CA for its calls to authentik, and still trusts the usual public CAs.
3. Install or upgrade¶
helm upgrade --install meho oci://ghcr.io/evoila/meho-chart \
--namespace meho --create-namespace \
-f values.yaml --wait --wait-for-jobs
After every install and every upgrade, the bootstrap Job sets up the broker in Keycloak. A second run with the same values changes nothing. The Job:
- reads authentik's discovery address and creates or updates the login. It refuses a login system that publishes no signing keys;
- makes one group rule per
groupMapline, and removes the rules of lines you deleted; - turns the redirect to authentik on or off, as
autoRedirectsays.
The install notes then print the redirect address. Check that it is the same as the one in authentik:
Company login (built-in Keycloak):
In the company login system, set this redirect address on the client <client id>:
https://meho.example.com/auth/realms/meho/broker/company/endpoint
People in a company group listed in coreKeycloak.companyLogin.groupMap get that MEHO group at every company login.
Keycloak sends everyone straight to the company login. To use the local accounts above
(for example when the company login is down), set coreKeycloak.companyLogin.autoRedirect=false and run helm upgrade.
To change a group rule later, edit groupMap and run the same
helm upgrade. People who came in through authentik lose the MEHO
group of a removed or changed line.
You turn on the company login on an existing install
The Job needs two Keycloak rights to set up the broker:
manage-identity-providers and manage-realm. The chart gives them
only when the company login is on at the first install, because
Keycloak reads its realm file only at its first start. If you turn
the company login on later, the Job stops with a message that names
the two rights. Then add them once by hand: in the Keycloak admin
console, realm meho, open Clients > meho-admin, tab
Service accounts roles, assign the two realm-management roles, and run
helm upgrade again. For a Keycloak admin login, see
Reach the Keycloak admin console.
4. Check that it works¶
- Command line. Run
meho login https://meho.example.com. Open the link it prints. Keycloak sends you to authentik. Log in with your password and MFA. Keycloak may ask once "Grant Access to …": click Yes. Then runmeho status. It must show your tenant and role. - Web console. Open
https://meho.example.com/ui/. You land on authentik, then on the MEHO dashboard with your name and role. - Claude Code. Log in with the MEHO plugin
(Claude Code). The browser goes to
authentik. Then
meho_statusreturns yourtenant_idandtenant_role. - Token fields. Read your raw token as shown in
Keycloak realm setup, Verify. Expect:
issishttps://meho.example.com/auth/realms/meho;audholdsmeho-backplaneandhttps://meho.example.com/mcp;tenant_idis your tenant UUID, andtenant_roleis the role of your group;groupslists your MEHO group;approver: trueappears only forMEHO-Approvers.
Your own Keycloak¶
Use this section if your site runs its own Keycloak instead of the built-in one. You set up the same broker by hand. The result is the same as with the Helm values above.
First: set up the realm with Keycloak realm setup,
including the five groups and the mappers that read them. Check that a
local user in a group can log in. This page writes your Keycloak as
<keycloak-host> and your realm as <realm>.
1. Let Keycloak trust authentik¶
Skip this step if a public CA signs authentik's certificate.
Otherwise, give Keycloak the authentik CA as a PEM file, and point the
Keycloak setting truststore-paths at it. In a container, that is the
environment variable KC_TRUSTSTORE_PATHS, for example
/opt/keycloak/conf/tls/authentik-ca.pem
(Keycloak truststore guide).
Restart Keycloak.
Without it, the login fails after authentik with "Unexpected error when
authenticating with identity provider", and the Keycloak log says
PKIX path building failed.
2. Add authentik as a login in Keycloak¶
You can click it in the admin console or run it with kcadm.sh, the
Keycloak admin command line. Both give the same result.
In the admin console (realm <realm>):
- Identity providers > OpenID Connect v1.0.
- Alias
company, Display nameauthentik. - Use discovery endpoint on. Discovery endpoint: the address from Part 1, step 3.
- Client authentication: "Client secret sent in the request body". Client ID and Client Secret from authentik. Click Add.
- Alias
- On the provider page, check or set:
- Validate Signatures on, Use JWKS URL on.
- Under Advanced: Scopes
openid profile email. Use PKCE on, PKCE MethodS256. - Trust Email off. First login flow override
first broker login. Sync modeforce. Click Save.
-
Mappers tab > Add mapper, once for each of the five groups:
- Name
meho-group-MEHO-Operators, Sync mode override Force, Mapper type Advanced Claim to Group. - Claims: key
groups, valueMEHO-Operators(the authentik group name). Regex Claim Values off. - Group:
MEHO-Operators(the MEHO group).
With Force, Keycloak checks the
groupsfield at every authentik login. It adds the person to the group, or takes them out when the field no longer matches. 4. Authentication > browser, then the gear icon on the Identity Provider Redirector row. Set an Alias (for exampleredirect-to-company) and Default Identity Providercompany. People now never see the Keycloak login page. - Name
The same with kcadm.sh. Log in first, for example with
kcadm.sh config credentials --server https://<keycloak-host> --realm master --user <admin>.
Save this as company-idp.json, with your authentik address and Client
ID:
{
"alias": "company", "displayName": "authentik", "providerId": "oidc", "enabled": true,
"trustEmail": false, "hideOnLogin": false, "firstBrokerLoginFlowAlias": "first broker login",
"config": {
"issuer": "https://authentik.example.com/application/o/meho/",
"authorizationUrl": "https://authentik.example.com/application/o/authorize/",
"tokenUrl": "https://authentik.example.com/application/o/token/",
"userInfoUrl": "https://authentik.example.com/application/o/userinfo/",
"jwksUrl": "https://authentik.example.com/application/o/meho/jwks/",
"useJwksUrl": "true", "validateSignature": "true",
"clientId": "<the authentik Client ID>", "clientAuthMethod": "client_secret_post",
"defaultScope": "openid profile email",
"pkceEnabled": "true", "pkceMethod": "S256", "syncMode": "FORCE"
}
}
The authentik issuer ends with a /. Keep it. Then run:
REALM=<realm>
read -rs CLIENT_SECRET # paste the authentik client secret; it is not shown
jq --arg s "$CLIENT_SECRET" '.config.clientSecret = $s' company-idp.json \
| kcadm.sh create identity-provider/instances -r "$REALM" -f -
unset CLIENT_SECRET
for G in MEHO-Readers MEHO-Operators MEHO-SA-Operators MEHO-Admins MEHO-Approvers; do
jq -n --arg g "$G" '{name: ("meho-group-" + $g), identityProviderAlias: "company",
identityProviderMapper: "oidc-advanced-group-idp-mapper",
config: {syncMode: "FORCE", "are.claim.values.regex": "false", group: ("/" + $g),
claims: ([{key: "groups", value: $g}] | tojson)}}' \
| kcadm.sh create identity-provider/instances/company/mappers -r "$REALM" -f -
done
EID=$(kcadm.sh get authentication/flows/browser/executions -r "$REALM" \
--fields id,providerId --format csv --noquotes \
| awk -F, '$2=="identity-provider-redirector" {print $1}')
kcadm.sh create "authentication/executions/$EID/config" -r "$REALM" \
-b '{"alias":"redirect-to-company","config":{"defaultProvider":"company"}}'
If your authentik groups have other names, change value in claims
(the authentik name) and keep group (the MEHO group). Run the block
once. A second run fails, because the objects exist already.
3. Point MEHO at your Keycloak¶
Nothing changes in MEHO. MEHO still trusts your Keycloak, with the values from the install trail, Step 6.
4. Check that it works¶
Run the checks of Part 2, step 4. The only
difference: iss is https://<keycloak-host>/realms/<realm>.
What admins must know¶
- Removal takes effect at the next company login. Keycloak checks the groups only when the person logs in through authentik. While the person's Keycloak session lasts, a new MEHO login reuses it and does not ask authentik. A login that is still running keeps working, and so does a long login for Claude Code (an offline login, up to 48 hours without use). To cut access now, also sign the person out in Keycloak, or disable the user there: open Users > the person > Sessions, click Logout all sessions, then Revoke on any row of type Offline. A MEHO token already issued stays valid until it expires, which takes a few minutes.
- MFA is authentik's job. Keycloak does not check it again. Turn on MFA in authentik for everyone who uses MEHO.
- The emergency way in when authentik is down. The local Keycloak
accounts still work. With the built-in Keycloak, these are the two
people the install made (their passwords are in the Secret
<release>-core-first-login). Two ways:- Set
coreKeycloak.companyLogin.autoRedirect: falseand runhelm upgrade. Keycloak shows its own login form again. Set it back totruewhen authentik works again. With your own Keycloak, clear Default Identity Provider on the Identity Provider Redirector instead (Part "Your own Keycloak", step 2.4). - For one login only: open the Keycloak login address with an
empty
kc_idp_hint=added at the end. Keycloak then shows its login form instead of sending you to authentik. MEHO's own login screens do not add this, so do it by hand: start the web console login, copy the Keycloak address (it starts withhttps://<host>/auth/realms/meho/protocol/openid-connect/auth?) from the browser's developer tools, add&kc_idp_hint=, and open it in the same browser.
- Set
- A company user with the same name as a local account. If an
authentik user has the user name or email of a local Keycloak account
(for example
admin), Keycloak does not link the two by itself. It asks the person to prove that they own the local account first, for example with its password. - Group changes: to move a person to another role, move them to another group in authentik. The next company login gives the new role.
- Turning the company login off. Setting
coreKeycloak.companyLogin.enabled: falsedoes not remove the login from Keycloak. To show the local login form again, keepenabledon and setautoRedirect: false.
Reach the Keycloak admin console¶
The steps above that sign a person out, or add the two rights, need the Keycloak admin console. With the built-in Keycloak:
- The console is not on the ingress by default. Set
coreKeycloak.ingress.exposeAdmin: trueto reach it athttps://<host>/auth/admin/. - The install makes no admin account for people. Make one with Keycloak's bootstrap admin recovery command, and remove it when you are done.
If something fails¶
| What you see | What it means | Fix |
|---|---|---|
| authentik shows a redirect URI error | The address in authentik does not match. | Copy the redirect address from the install notes into authentik, mode Strict. |
| The bootstrap Job stops: "publishes no signing keys" | authentik has no signing key. | Pick an RSA Signing Key in the authentik provider (Part 1, step 2). |
The bootstrap Job stops and names manage-identity-providers and manage-realm |
The company login was off at the first install. | See "You turn on the company login on an existing install" above. |
| The bootstrap Job stops: "groupMap names the group …, which the realm does not have" | A right side in groupMap is not a MEHO group. |
Use one of the five MEHO group names. |
| Keycloak shows "Unexpected error when authenticating with identity provider" | Keycloak does not trust authentik's certificate. | Set caBundle (built-in) or KC_TRUSTSTORE_PATHS (your own Keycloak). |
401 missing_tenant_claim |
The person is in no MEHO group. | Check the person's authentik group, the groupMap line, and that profile is in the scopes. Log in again. |
401 multiple_role_groups |
The person is in two MEHO groups. | Keep one group per person in authentik. |
| Keycloak asks for a name or an email | authentik did not send that field. | Give the person a first name, a last name and an email in authentik. |