Running Multiple SSO Connectors on One Cluster
AlmaForge is designed around one SSO connector per cluster.
Apply an OIDCConnector
or a GithubConnector,
and let role templates read a predictable, stable set of traits from
that one provider.
When a cluster has two or more active connectors, traits and external identities behave differently. Use this configuration only for the three scenarios below.
Running two SSO connectors as co-equal primary identity providers has
two important consequences. A user's trait map is replaced in full
on every login. A user signing in through a second connector under the
same AlmaForge username is refused by default unless you enable
spec.allowIdentityMerging on
AuthConfig. Both are
documented below. They fit the three scenarios in
When to run multiple connectors,
but not a long-lived production configuration.
TL;DR
- Know the three supported scenarios: dev, migration, disaster recovery (When to run multiple connectors).
- Turn on identity merging explicitly on
AuthConfigbefore the second connector sees a login (Enable identity merging). - Understand that traits are replaced on every login, not merged (How traits work).
- Understand that external identities merge into one user record, but user accounts do not merge (How user merging works).
- Know that there is no default connector. Every caller picks one in the browser, CLI logins included (Choosing a connector at login).
When to run multiple connectors
The three scenarios where running more than one SSO connector is useful, in order of blast radius:
Dev
Use a dev cluster to test a new SSO provider alongside the current one. For example, add Okta temporarily to a cluster that already uses Entra ID, test each connector in a separate window, then delete the one you do not want to keep. Every login is deliberate, and neither connector coexists long term.
Migration from one SSO to another
You are moving your organization from one identity provider to another, for example Google Workspace to Microsoft Entra ID. For a brief cutover window, both connectors are applied at the same time so engineers who have already moved to the new IdP can log in through it, while engineers still on the old IdP are unaffected. Once the cutover is complete, the old connector is removed.
During a migration window, each engineer logs in through one of the two connectors on any given day: either the old one or the new one. They do not ping-pong between them. This keeps the trait map stable for each user and keeps role templates predictable. The moment an individual engineer cuts over to the new IdP, they start logging in through the new connector and stay there.
Disaster recovery
When the primary provider is down, apply a preconfigured secondary connector of a different kind for the duration of the incident. For example, switch from Google to Microsoft while Google is unavailable. Remove the secondary connector after the primary provider recovers.
The secondary connector in this scenario should be prepared in advance and kept as an un-applied YAML file in the same store as your break-glass procedure. Applying it in the middle of an incident requires an existing admin session or break-glass access:
alma apply -f dr-microsoft-connector.yamlalma loginAnd the rollback is:
alma delete oidcconnector/microsoftConfigure and test the backup connector in advance. Store its YAML
with your break-glass procedure, and apply it only when needed. Enable
spec.allowIdentityMerging before an outage. See
Enable identity merging.
Prerequisites
Before you run more than one connector at the same time, make sure you have:
- At least one SSO connector already applied and known to work end to end. Every page under the SSO integrations section is a valid starting point.
- The YAML for every additional connector you plan to apply, already tested against its own provider in isolation. This page does not teach you how to set up a new connector. It teaches you how to run more than one at once.
- Cluster-admin rights on
AlmaForge, because you will be editingAuthConfigand applying connector resources. - A current, working break-glass access procedure. See Break-glass access. Test identity merging before an outage. It is disabled by default.
Enable identity merging
With the default AuthConfig, a second login attempt for the
same AlmaForge username through a different connector is
refused. The auth server returns cannot merge identities: allow_identity_merging is disabled.
To allow one user to sign in through more than one connector,
set spec.allowIdentityMerging: true on
AuthConfig:
alma edit authconfig/cluster# Auth configuration for a temporary multi-SSO migration or recovery window.
apiVersion: almaforge.com/v1
kind: AuthConfig
metadata:
name: cluster
spec:
allowIdentityMerging: true
The field is a tri-state boolean: unset (the default, which is
equivalent to false), explicitly false, or explicitly
true. Only explicit true enables merging. Once flipped on,
the change is effective immediately and applies to every future
login.
The flag has two important properties:
- It is a cluster-wide switch. You cannot turn identity merging on for one connector and off for another. The gate is at the auth-preference level, not the connector level.
- It does not retroactively touch existing user records. A
user who logged in once through a connector before you flipped
the flag still has one identity in their
spec.identitieslist. The next login through a different connector will succeed and will append the new identity to that list.
How traits work
When a user signs in through an OIDC connector, AlmaForge
stores supported ID-token claims as traits under their original
names. GitHub supplies logins and teams traits from its user and
team lookups. The connector name is recorded with the external
identity, not added to each trait key.
For example, both Microsoft and Keycloak can provide a groups
trait. Its values come from the provider used for that login. A role
must interpret those values consistently or be assigned only by the
connector whose claims it expects.
On a cluster with a Microsoft connector, a user record after one login looks like this:
alma get user/alice@example.com -o yamlkind: User
apiVersion: almaforge.com/v1
metadata:
name: alice@example.com
labels:
internal/expires: "1775921955"
spec:
createdBy:
connector:
id: microsoft
identity: alice@example.com
subject: 8f14e45f-ea6a-4c1d-9b2e-7d3a0c5b1f92
type: OIDC
createdAt: "2026-04-10T15:39:15Z"
user:
name: system
identities:
- id: microsoft
identity: alice@example.com
subject: 8f14e45f-ea6a-4c1d-9b2e-7d3a0c5b1f92
type: OIDC
roles:
- admin
traits:
groups:
- 44d56e07-85fc-4ab8-9146-738c259a401c
- 7907db2a-2e6b-46c6-ae9b-91dbaddb156c
name:
- Alice Example
preferred_username:
- alice@example.com
The record shows three important details:
metadata.labels["internal/expires"]is the expiry in Unix seconds. User records are temporary and refreshed on each login. Their lifetime follows the requested certificate lifetime, capped by the assigned roles and cluster certificate limit. After expiry, the next sign-in creates a fresh record. Keep durable assignments in the IdP and connector. See SSO user records.spec.identitieslists every external identity the user has signed in through during the lifetime of this record, subject toallowIdentityMerging. See How user merging works.spec.traitsis a flat map keyed by claim name. Role templates read those values throughexternal.
Traits are replaced on every login
Every SSO login replaces the trait map in full. When a user
logs in through a different connector than the one they used
last time, AlmaForge overwrites the user's trait map with
the new connector's traits and drops everything from
the old connector. The identities list accumulates. The
traits do not.
Concretely:
alice@example.comsigns in throughgoogle. The user record hasidentities: [google]and traits such asgroups,email, andnamefrom Google.- Later,
alice@example.comsigns in throughmicrosoft. The user record hasidentities: [google, microsoft]and traits such asgroups,preferred_username, andnamefrom Microsoft. - The stored
groupsvalues now come from Microsoft. Claims not returned by Microsoft, such asemailin this example, are absent. Signing in through Google again replaces the map with Google's claims.
A certificate carries the traits from the connector used to issue it.
A later login replaces the stored user traits but does not rewrite
existing certificates. Role templates that reference
external["<claim>"] need that claim in the certificate
used for the connection.
Role templates against claims
Role templates read traits through the external variable.
The bare dot form works for plain identifier names:
# Role template that maps a plain OIDC claim to the SSH login.
apiVersion: almaforge.com/v1
kind: Role
metadata:
name: dev
spec:
allow:
serverLogins:
- '{{external.preferred_username}}'
Bracket syntax also works. It is required when the claim name
contains characters such as /:
# Role template for OIDC claim names that require bracket syntax.
apiVersion: almaforge.com/v1
kind: Role
metadata:
name: dev
spec:
allow:
serverLogins:
- '{{external["preferred_username"]}}'
serverLabels:
team: '{{external["groups"]}}'
The bracket form uses the exact claim name supplied by the IdP.
In a multi-SSO setup, a role template that is meant to cover users from two connectors has to choose one of two shapes:
- Use shared claim semantics. Both providers emit the same claim names and values for the permissions the role needs. Test that an equivalent user receives equivalent access through either connector.
- Use one role per connector. Each role references its provider's
traits, and that connector's
claimsToRolesassigns it. Keep the intended permissions consistent across the roles and test each login path during the migration. See external trait templates.
How user merging works
User merging adds external identities to one user record. It does not combine separate user accounts.
A user record in the backend is keyed by the AlmaForge
username. That username is derived from a claim in the SSO
response, typically the email for OIDC or the GitHub username
for GitHub. If two different connectors return the same
username claim for the same human, both map to the same
AlmaForge user record.
At login time, the auth server looks at the existing user
record (if any) and compares its spec.identities against the
incoming connector. Each identity in the list is a three-field
tuple:
type: connector type ("OIDC"or"Github").id: the connector'smetadata.name.identity: the external username returned by that connector.
With allowIdentityMerging: false (the default)
The auth server requires the existing user to have exactly
one identity, and that one identity must have the same
type, id, and identity as the incoming login. If either
condition fails, the login is refused with cannot merge identities: allow_identity_merging is disabled.
While the existing user record remains, the user must sign in through that one connector. A second connector can exist on the cluster, but a login under the same username cannot merge into the record. Applying a second connector is harmless until the first user from the second connector tries to log in under a username that already exists, at which point the login fails. This is the default because it prevents a misconfigured second connector from silently rewriting existing user records.
With allowIdentityMerging: true
The auth server walks the existing user's identities list
looking for a match on type + id. If found, the existing
identity entry is updated in place (the identity field is
refreshed to whatever the new login returned). If not found, a
new identity is appended. Either way, the login succeeds and
the user's spec.identities list grows to reflect the set of
external accounts they have signed in through.
The trait map, on the other hand, is still replaced in full.
The identities list accumulates. Traits do not. There is no
way to reverse this because it is a property of how the auth
server writes the user record at the end of every login, not
an option you can turn on and off separately.
What merging does not do
- It does not reconcile usernames. Two connectors that return different usernames for the same human create two separate user records. AlmaForge has no way to know they are the same person.
- It does not merge roles. The user's
spec.rolescomes from the connector'sclaimsToRolesmapping on the most recent login, and is replaced on every login alongside the traits. A user who isadminthrough Google andaccessthrough Microsoft will beadminright after a Google login andaccessright after a Microsoft login. - It does not change the certificate. An engineer holding
a valid
AlmaForgecertificate issued through one connector does not automatically pick up capabilities from a second connector. They have to log out and log in again through the other connector for that login's traits and roles to take effect.
Choosing a connector at login
There is no default connector and no way to mark one. The caller chooses among the active connectors in a browser.
With one active connector, the sign-in page goes straight to it. The page is shown instead after signing out, after the identity provider refuses a sign-in, and on a cluster with a message of the day.
The sign-in page renders one button per connector, labeled with the
connector's spec.display if it has one and its metadata.name
otherwise. That label is the only control you have over the choice,
so give each connector a display name that makes it unambiguous.
A CLI login lands on the same page:
alma login --proxy=almaforge.example.com:443alma prints a sign-in URL and a one-time code, and opens
a browser at that page unless you pass --no-browser. Where there is
more than one connector, the user picks there, and nothing about the
choice is recorded in the local profile, so the next login asks again.
Scripts and CI do not use this path at all. They authenticate through workload identity, where the join method takes the place of a connector.
Operations
Rolling a migration forward
Use this sequence to migrate from one SSO provider to another:
- Apply and test the new connector. With identity merging disabled, a username already bound to the old connector cannot sign in through the new one. New usernames can still sign in when their claims map to a role, so restrict the new IdP app to the intended users.
- Flip
allowIdentityMerging: trueonAuthConfig. Every existing user's next login through the new connector will now append a second identity to their record rather than failing. - Tell engineers to log in through the new connector, and give
it a
spec.displaythat makes it the obvious button on the sign-in page. - Watch the auth audit log for
UserLoginevents withconnector_id: <new-connector-name>. Stragglers will show up as the last fewUserLoginevents still coming from the old connector. - When the last straggler has moved, delete the old connector. Existing sessions issued through the old connector stay valid until their certificates expire. No new sessions will be issued through it.
- Keep identity merging enabled while any user record contains multiple identities. Disabling it at that point rejects that user's next login, even through the remaining connector. Before restoring the default, inspect the user records and use your tested break-glass procedure to plan the transition.
Rolling a DR cutover forward and back
Use this sequence for a DR cutover:
- Your primary connector's provider is unreachable. Users cannot log in.
- Apply the pre-prepared secondary connector (see
Disaster recovery). If your cluster
does not already have
allowIdentityMerging: true, editAuthConfigin the same window to turn it on. Do this from a machine that already has a validAlmaForgecertificate, because your break-glass procedure is the only way in if your certificate has also expired. - Tell engineers to sign in through the secondary connector for
the duration of the incident. It is the second button on the
sign-in page, for CLI logins as well as Web UI ones, so give
it a
spec.displaythat says so. - When the primary recovers, delete the secondary. Existing sessions on it stay valid until expiry, and new logins go back to the primary.
Removal
Delete an additional connector the same way as any other:
alma delete oidcconnector/<name>Existing sessions issued through the deleted connector remain valid until their certificates expire. No new sessions will be issued.
Removing a connector removes its button from the sign-in page. Anyone who was using it picks a different one on their next login, with no local state to clear first.
Audit trail
Every login emits a UserLogin event whose attributes carry
connector_id and connector_type. Filter the Web UI's audit log by
the UserLogin type to see which connector a user is actually using,
or list these events from the CLI:
alma audit --type=UserLogin --last=1h -o jsonUse --follow to keep watching for new logins. See the
CLI reference for date and output options.
If you see events from a connector you thought you had
removed, the connector resource is still in the cluster state
store. Confirm with
alma get oidcconnector,githubconnector.
Limitations
The three scenarios above are the supported use cases. The limitations below explain why multi-SSO is not a production steady-state configuration:
- Trait-map replacement. The stored traits come from the latest login. Issued certificates retain the traits from their own login. A shared role can grant different access if providers emit different values for the same claim. Verify each connector's mapping and templates before admitting its users.
- Identity-merging collisions. A second connector that
happens to return the same email as the first one will
attempt to merge onto the existing user record. If
allowIdentityMergingis off, the second login fails. If it is on, the user'sidentitieslist grows and the user's traits and roles get rewritten every time they switch connectors. For production, pick one connector. - Role mapping drift. Each connector has its own
claimsToRolesblock. A human who isadminin Microsoft andaccessin Google will flip roles every time they switch connectors, because roles are replaced on every login alongside traits. For production, pick one connector. - No cross-connector policy. AlmaForge does not evaluate both connectors at the same time and give the user the union of their claims. There is no multi-IdP attribute merging or cross-provider assertion composition. The login is a single event with a single source of truth, and the most recent event wins.
For production steady state, run one SSO connector. Use the three scenarios above as transient states, not as permanent configurations.
References
AuthConfigresource reference: the resource that carriesallowIdentityMerging.OIDCConnectorresource referenceGithubConnectorresource reference- Break-glass access guide: the cluster-level recovery procedure that multi-SSO does not replace.
- OIDC integration: the canonical reference for configuring a single OIDC connector on a cluster.