Microsoft Entra ID SSO for AlmaForge
AlmaForge authenticates users against Microsoft Entra ID (formerly Azure Active Directory) through the standard OIDC flow. Map Entra security groups to AlmaForge roles. Membership changes take effect on the user's next sign-in. Manage group membership in Entra and revoke active access in AlmaForge when the change must take effect immediately.
The integration is built into almad. Configure it with an
OIDCConnector
resource whose spec.provider is set to microsoft. See the
generic OIDC guide for the full field list and the
shared verification, high-availability, and break-glass content.
This page covers only the Entra-specific clicks.
The OIDC integration supports group-based role mapping, tenant isolation, and reauthentication.
TL;DR
- Register a new app in Entra ID with the OIDC callback URL (Step 1).
- Create a client secret (Step 2).
- Add the
groupsclaim to the ID token (Step 3). - Apply the
OIDCConnectorfrom Step 4. - Verify with a real login (Step 5).
The AlmaForge side of this integration takes 2 to 3 minutes. You
write a single
OIDCConnector
resource and apply it.
The Entra ID side is the slow part. Expect to spend up to half an
hour clicking through the Entra admin center, finding the right
blades, and collecting the tenant ID, the client ID, the secret,
and the object ID of each group you want to map. Start the Entra
work first and save the alma apply for the end.
Limitations
Test recovery before changing SSO. AlmaForge has no local
accounts. If this is the only connector, a failure prevents new logins. Review
SSO-first behavior on the OIDC page,
and confirm your break-glass procedure works
before running apply on a production cluster.
Client secrets expire. Entra ID caps client secrets at 24 months and does not let you create a non-expiring secret. When the secret expires, token exchange fails for every user. See Client secret rotation under Operations for the rotation procedure and the observable symptom.
Group overage is not followed. Entra does not emit the groups
claim inline for users who are members of more than approximately
200 security groups. Instead, it emits a _claim_names hint that
points back to Microsoft Graph. AlmaForge does not currently
follow the overage pointer. If users hit the limit, configure Entra
to emit only groups assigned to this application, then assign and
map the required groups. Users must be direct members of those assigned
groups, because this option excludes nested membership. Reducing the
connector's mapping does not reduce the groups Entra puts in the token. See
Microsoft's group-claim guidance.
Match groups by object ID, never by display name.
AlmaForge matches claimsToRoles against the groups claim
exactly as the token carries it, and resolves nothing. The
directory assigns object IDs. Display names are chosen by whoever
creates the group, Entra does not require them to be unique, and
creating a group is a default permission for an ordinary tenant
user, so a name is not something a login decision can rest on.
Which of the two lands in the token is a tenant setting.
Token configuration → Add groups claim offers Group ID as
well as Cloud-only group display names and sAMAccountName
(Step 3 selects Group ID). Pick
Group ID, and copy each group's Object ID from its
Overview page in Entra. If your tenant emits names instead,
claimsToRoles will match on a value your users can choose.
Keep credentials outside the manifest. clientSecret can load
its value from a file or environment variable. See
Secret storage.
Prerequisites
Before you start, make sure you have:
- A running AlmaForge cluster with an Auth Service that you
can reach over the network and a working
almaCLI pointed at it. - An Entra ID tenant and an account with permission to create App registrations.
- One or more Entra security groups that you will map to AlmaForge roles, and the Object ID of each. A fresh group dedicated to AlmaForge access is cleanest. Reusing an all-employees group generally is not what you want.
- The public address of your AlmaForge Proxy Service, for
example
almaforge.example.com. - Outbound HTTPS egress from
almadtologin.microsoftonline.comfrom every auth host. - A break-glass procedure in place and tested.
Step 1. Register in Entra ID
Sign in to the Microsoft Entra admin center and open Identity → Applications → App registrations → New registration.
Fill in the form:
- Name: something recognizable, for example
AlmaForge. - Supported account types: Accounts in this organizational directory only (single tenant). Multi-tenant and personal-account options work too, but single-tenant is the default shape for an internal access platform.
- Redirect URI: select Web and enter
https://almaforge.example.com/api/v1/oidc/callback, substituting your own proxy host.
Click Register.
On the application Overview page, copy these two values. You will need them in the connector YAML:
- Application (client) ID.
- Directory (tenant) ID.
Step 2. Create a client secret
In the application sidebar, open Certificates & secrets → Client secrets → New client secret. Give it a description and an expiration period that matches your rotation policy, then click Add.
Entra masks the secret as soon as you leave the page and never
shows it again. The Secret ID column is not the secret. Do not
use it.
Store the client secret in the same secret manager you use for production credentials. Do not commit it to Git.
Step 3. Add the groups claim
AlmaForge maps Entra security groups to roles by reading the
groups claim on the ID token. By default Entra does not include
this claim. You have to opt in.
In the application sidebar, open Token configuration → Add groups claim. Choose:
- Security groups (sufficient for most deployments).
- Under ID, select Group ID. This tells Entra to emit each group as its immutable object ID.
Save the change.
Step 4. Apply the OIDCConnector
Write a YAML file with the values you collected:
# Microsoft Entra ID connector using immutable group object IDs.
apiVersion: almaforge.com/v1
kind: OIDCConnector
metadata:
name: microsoft
spec:
display: Microsoft
provider: microsoft
issuerURL: https://login.microsoftonline.com/<tenant-id>/v2.0
clientID: <application-client-id>
clientSecret: <client-secret-value>
usernameClaim: preferred_username
claimsToRoles:
# AlmaForge-Admins
- claim: groups
value: 44d56e07-85fc-4f1e-9a3b-0c2d6e5b7a19
roles: [admin, access]
# AlmaForge-Developers
- claim: groups
value: 7907db2a-2e6b-42c8-b5d0-1f8e3a94c6b2
roles: [access]
Replace:
<tenant-id>with the Directory (tenant) ID from Step 1.<application-client-id>with the Application (client) ID from Step 1.<client-secret-value>with the secret Value from Step 2.- The
claimsToRolesentries with your own groups and AlmaForge roles. Eachvalueis a group Object ID, copied from the group's Overview page in Entra. A display name will not match. Keep the name in a comment above the entry so the mapping stays readable.
A few things to notice about this example:
issuerURLincludes/v2.0at the end. Entra's v1 endpoint is deprecated and emits a slightly differentissclaim that does not match the discovery document. Use v2.0.usernameClaim: preferred_usernamematches the Entra user principal name (for examplealice@example.com) rather than the immutable object ID. This is almost always what you want.- The Redirect URI you registered in
Step 1 must match the URL
AlmaForge actually sends:
https://<proxy_public_addr>/api/v1/oidc/callbackwith port443omitted. Including or omitting the port (:443) counts as a mismatch.
AlmaForge has no local accounts. If this is the only connector, a wrong claim mapping or expired client secret prevents new logins. Confirm your break-glass procedure works before applying the connector to a production cluster.
Apply the connector:
alma apply -f microsoft.yamlStep 5. Verify the connector
Open the cluster's audit log for your test account. See SSO verification for event fields and the distinction from service logs.
Sign out and log in through Microsoft:
alma logoutalma login --proxy=almaforge.example.comA browser opens the cluster's sign-in page. Choose Microsoft
Entra ID. On the first sign-in, Entra may prompt the user to
consent to the application's permissions. Admins can pre-consent
for the whole tenant from the app's API permissions page.
After authentication,
alma prints the roles, logins, and expiration assigned
to the session.
A successful first-time login emits a UserLogin event and the
CertificateCreate event for the certificate it mints. Both carry the
spec.type that names them and the payload beside it:
{
"kind": "AuditEvent",
"apiVersion": "almaforge.com/v1",
"spec": {
"id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9",
"index": 0,
"timestamp": "2026-03-30T15:58:58.999000Z",
"clusterName": "almaforge.example.com",
"code": "T1001I",
"type": "UserLogin",
"verb": "login",
"actor": {
"name": "alice@example.com",
"type": "user"
},
"status": {
"success": true
},
"userLogin": {
"method": "oidc",
"attributes": {
"connector_id": "microsoft",
"connector_type": "oidc",
"name": "Alice Example",
"request_id": "8f0c1e6a-2f7e-4a7a-9f1e-1f0b6d0f1a2b",
"username": "alice@example.com"
}
}
}
}
{
"kind": "AuditEvent",
"apiVersion": "almaforge.com/v1",
"spec": {
"id": "6d7e8f90-a1b2-43c4-95d6-e7f8091a2b3c",
"index": 0,
"timestamp": "2026-03-30T15:58:58.999000Z",
"clusterName": "almaforge.example.com",
"code": "TC000I",
"type": "CertificateCreate",
"verb": "create",
"certificateCreate": {
"certType": "user",
"identity": {
"user": "alice@example.com",
"roles": [
"admin"
],
"logins": [
"tester",
"ec2-user",
"root"
],
"kubernetesGroups": [
"system:masters"
],
"traits": {
"groups": [
"44d56e07-85fc-...",
"7907db2a-2e6b-..."
],
"name": [
"Alice Example"
],
"preferred_username": [
"alice@example.com"
]
}
}
}
}
}
Three things to notice in these two events:
spec.certificateCreate.identity.traits.groups: the group object IDs Entra emitted in the raw token, which is exactly whatclaimsToRolesmatched against. Nothing else is added to this list. If thegroupsarray is missing entirely, Step 3 was skipped or the user is over the group overage limit.spec.certificateCreate.identity.traits.preferred_username: compare this claim withspec.certificateCreate.identity.userto check theusernameClaim: preferred_usernamemapping. If this field is missing or contains the wrong value, check the user's UPN in Entra.spec.certificateCreate.identity.rolesandspec.certificateCreate.identity.logins: confirm the expected roles and SSH logins. See SSO verification for the full identity.
After a successful login, you can also run
alma get user/<username> -o yaml and inspect spec.traits.
Read the full audit-event breakdown on the generic OIDC verification section.
Troubleshooting
AADSTS50011: reply URL does not match
The Redirect URI registered in
Step 1 does not match the URL
AlmaForge sends as redirect_uri. AlmaForge sends
https://<proxy_public_addr>/api/v1/oidc/callback with port
443 omitted. Register that exact string in Entra and re-test.
Login fails with invalid_client
The client secret on the connector does not match what Entra has
on file. The most common causes are a mistyped value, a secret
that was rotated at Entra without re-applying the connector, or a
secret that has expired (Entra secrets cap at 24 months). Create
a fresh secret in Certificates & secrets, copy the Value
column, and re-apply the connector. See
Client secret rotation for the full
procedure.
No roles match the login
The login is rejected when no mapping assigns a role. Either the token does not carry the groups claim
(Step 3 was skipped), or a
claimsToRoles value is a group display name rather than a group
Object ID. Set ALMA_LOG_LEVEL=debug on the Auth host, sign in
again, and inspect the claims Entra actually sent:
journalctl -u almad -f | grep 'Extracted claims'The groups array holds object IDs, and every claimsToRoles
value has to be one of them character for character. If the
login succeeds but the stored identity still looks wrong,
inspect the user record as described in
Claims, traits, and role templates.
Admin group missing for a user with many groups
The user hit Entra's group overage limit. Limit the emitted claim
to groups assigned to the application and confirm the resulting ID
token contains the required group IDs. A smaller claimsToRoles
mapping alone cannot fix an omitted token claim. See
Group overage.
Operations
Lifecycle
Apply connector changes before testing the next login. No
almad restart is needed. See
When connector changes take effect
for discovery caching and IdP-side changes.
Client secret rotation
Entra client secrets expire after at most 24 months. When a secret expires, token exchange fails and every sign-in through the connector returns an error. In the audit log, look for:
UserLoginaudit events withspec.status.successset to false and aspec.status.errorfield containinginvalid_client.- No new certificate for that failed login attempt. Use the login
record for the connector context.
CertificateCreatehas no connector field.
An Entra app can hold multiple active client secrets at the same
time, but
OIDCConnector only
accepts one clientSecret value. Keep both secrets valid in Entra
while updating the connector:
- Create a new client secret in the app's Certificates & secrets blade. Do not delete the old one yet.
- Copy the new secret's
Valuecolumn immediately. - Run
alma apply -f microsoft.yamlwith the new value. - Run a test login through the connector to confirm the new secret works.
- Delete the old secret in Entra.
Confirm logins from each auth host before retiring the old secret. Track the expiration date of the current secret in whatever system you use for certificate expiry alerting. Entra surfaces the expiration on the Client secrets page but does not page anyone when it expires.
High availability
Every auth node calls the Entra token endpoint independently and caches the OIDC discovery document per-instance for one hour. See High availability on the generic OIDC page for the full list of per-instance assumptions.
Secret storage
Load clientSecret from a file or environment variable using
Secret storage, which also covers protection
of inline credentials and backups.
Break-glass access
Test break-glass access before relying on this connector. Use it when an expired secret, Entra outage, incorrect role mapping, or signing-key change blocks new logins.
Monitoring
Monitor the audit log for login outcomes. For diagnostics, inspect the Auth journal for token validation and the Proxy journal for browser callbacks. On each service host, tail OIDC-related messages with:
journalctl -u almad -f | grep -Ei 'microsoft|oidc'Check UserLogin failures and their spec.status.error field. See
SSO verification for event codes,
certificate fields, and debug-only journal copies. Collect records from
audit event storage for SIEM ingestion.
Collect service diagnostics from the journal separately.
Removal
Delete the connector resource:
alma delete oidcconnector/microsoftExisting sessions issued through the connector remain valid until their certificates expire. No new sessions will be issued.
On the Entra side, delete the App registration in Entra ID so the client ID and secret can no longer be used.