Authentication is not authority
A firm with an existing identity provider expects to bring it. That expectation is reasonable, and meeting it well is mostly a matter of protocol plumbing: redirect the browser, validate a signed token, issue a session.
The interesting part is what happens next. A validated assertion establishes that someone authenticated successfully at a particular provider. It does not establish which organization they belong to, what roles they hold, or—most consequentially—whether they may take over an account that already exists.
Every difficult decision in enterprise single sign-on lives in that gap between authentication and authority. This post walks the path an assertion takes through Celestice, and states the reasoning behind each rule, because several of these designs have an obvious-looking alternative that is a vulnerability.
Why federation is a security control, not a convenience
Federating sign-in is usually sold as a convenience feature. It is better understood as a security control, for three reasons that matter more than the reduced number of passwords.
Account lifecycle stays where the organization already governs it. When a firm removes someone at its identity provider, federated sign-in stops working immediately. There is no separate password that keeps functioning because an offboarding checklist missed a system.
Multi-factor enforcement is inherited rather than reimplemented. Authentication happens at the provider, so whatever step-up requirements the firm enforces there apply to Celestice without any configuration on our side.
Celestice holds no password for a federated user. A credential we never store is a credential that cannot be leaked from our systems.
Connections and verified domains
Federation configuration separates two things that are easy to conflate: who a provider is, and what it is permitted to claim.
A connection identifies the provider—its protocol, issuer, client identifier, a vault reference for its client secret, and a lifecycle state. The client secret is never stored alongside the configuration; the record holds only the name of a vault entry, resolved at request time.
A verified domain records an email domain that connection may authenticate, along with the evidence that the organization controls it.
Collapsing these into one table would remove the ability to express the state that matters most during onboarding: this provider is configured, and has not yet proven it controls this domain. That intermediate state is not an inconvenience to be engineered away. It is the entire basis of the strongest guarantee in the system.
Claiming a domain is not proving it
Before a domain routes sign-in, the organization publishes a DNS TXT record containing a per-domain token. Until that record is observed, the domain remains unverified, cannot route a login, and cannot connect to an existing account.
The reason is direct: without proof of control, any customer could assert any email domain and begin receiving sign-in traffic intended for another firm. Domain verification is the control that makes a provider's authority specific rather than universal.
Routing has three outcomes, and the third is the interesting one
Given an email address, routing resolves to one of three results: send the user to a federation, fall back to password, or refuse.
The refusal case exists because of a specific conflict. Consider a firm that has deliberately disabled password login and whose federation has entered an error state. Falling back to a password prompt would be the accommodating behavior, and it would reinstate exactly the login path the firm paid to remove. So routing refuses, and the condition surfaces to an operator instead.
Routing considers only verified domains. The database query filters on the verification timestamp, and the resolver checks it again. The redundancy is deliberate: a query that returns unverified rows is precisely the defect the second check exists to survive.
Validating the assertion
A returned token is untrusted input until proven otherwise. Validation covers six properties: the signature against the provider's published keys, the issuer, the audience, the expiry, the nonce, and the presence of a subject.
The nonce check is the one most often skipped, and the one worth understanding. It binds the token to the specific login attempt that requested it. Without it, a token captured from one session can be replayed into another. Signature validity is not freshness, and a correctly signed token proves only that the provider issued it at some point—not that it was issued for the request now presenting it.
Each of these six checks is covered by a test that was verified to fail when the check was removed. A test that passes both with and without the control it guards is not evidence of anything.
From assertion to authority
With a validated assertion in hand, provisioning decides the local outcome: create a new account, connect to an existing one, authenticate a returning user, or refuse.
The obvious implementation of the connect case matches on email address. The assertion says
person@firm.example, an account exists with that address, sign them in.
That is an account-takeover primitive.
Here is why. A tenant controls its own identity provider. It can configure that provider to emit an assertion claiming any email address it likes, including addresses belonging to users at other organizations. If the email address alone determines which account an assertion connects to, then onboarding a single federated connection grants the ability to authenticate as any user whose address can be guessed. The attack needs no stolen password, no phishing, and no flaw in the OIDC protocol. It is a logic error in the relying party.
Four rules close it, and none of them is optional.
The organization comes from the connection, never from the token. A tenant claim inside an assertion is attacker-controlled input. A test asserts that a token claiming one organization still provisions into the organization the verified connection names.
An existing account connects only when its email domain is verified for that connection. A firm's provider speaks for the domains it has proven and no others. This is the rule that stops one tenant asserting another tenant's address.
An unverified email never connects to an existing account. A provider that has not confirmed an address has not established that the person controls it.
An account already bound to a different provider subject is refused, not rebound. That state has two possible causes: a provider reissuing subject identifiers during a migration, or an attempt to rebind someone else's account. Guessing between them is how the wrong interpretation succeeds, so the flow stops and an operator resolves it.
Roles are not self-asserted
Providers can send group membership, and it is tempting to map those groups onto local roles directly. Doing so lets the provider choose its own authority by naming a group that happens to match a role we implement.
Group claims are therefore honored only through a mapping the organization configures explicitly. A group with no mapping is ignored, including one named to look privileged. A default role is always applied, so a misconfigured mapping cannot produce an account with no role and an unusable session.
Group claims are also read defensively. Providers disagree on the claim name, some send a single group as a bare string rather than a one-element array, and non-string entries are filtered rather than coerced into something that looks like a role.
A returning user is not a fresh authorization decision
When a known user signs in again, they are authenticated without role mapping being re-applied.
This is deliberate, and it is the kind of behavior that is easier to get wrong than right. An assertion is an authentication event. Treating it as an authorization update means that every sign-in silently reverts any role an administrator granted directly in Celestice. The privilege change would appear to work, persist until the user next signed in, and then disappear without a trace.



