Enterprise-Managed Authorization: Account linking with no user
The standard defence against account takeover is a verified email plus a user who clicks confirm. EMA gives you neither. Here is what to key on when nobody is there.
An ID-JAG lands at your token endpoint. The signature verifies, the audience is your authorization server, the client binding matches, and the enterprise IdP that signed it is one you have configured and trust. The token asserts sub: U-9931 and email: alice@customer.com.
You have a user called Alice, created eleven months ago when she signed up directly. You have no record of U-9931.
Are they the same person?
Nothing in the token answers that, and the usual way of settling it is unavailable. The rule for account linking is to key on the subject identifier, treat email as profile data, and never join two logins without proof of inbox access and a human confirming the link. That rule needs a browser. Enterprise-managed authorization closes the browser after single sign-on: no redirect, no consent screen, nobody to ask. Both halves of the defence are gone at precisely the moment you have to decide who is asking.
What follows is about the decision you are left holding. It assumes you know the EMA flow, so it skips the mechanics and goes straight to subject resolution. Inside the ID-JAG covers the flow end to end, and Stop using email as a primary key covers the linking rule this post has to work without.
!!Spec references below are to draft-ietf-oauth-identity-assertion-authz-grant-04, published 21 May 2026 and expiring 22 November 2026. Note that the draft's own sub claim definition says "See Section 5 for additional considerations," but Section 5 is Cross-Domain Client ID Handling. The subject identifier considerations are in Sections 9.5 and 9.6, and the tenant uniqueness rules are in Section 6.3.!!
There is no email_verified in an ID-JAG
Start with the finding that should change your implementation.
The claims list in Section 3.1 defines email as OPTIONAL, referred to Section 5.1 of OpenID Connect Core. It does not define email_verified. Not as required, not as optional, not at all. The claim is absent from the draft.
That matters because the guard in the August 6 post looks like this:
Against an ID-JAG, the right-hand side of that condition is always undefined. There is nothing to check. You are not deciding whether to trust a verification flag; you are deciding whether to trust an email address with no accompanying claim about whether anyone ever confirmed the user controls it.
Now read what the draft recommends you do with it, Section 3.1:
"It is RECOMMENDED that the ID-JAG contain an email [OpenID.Core] and/or aud_sub [OpenID.Enterprise] claim. The Resource Authorization Server MAY use these claims for subject resolution, including JIT provisioning, for example when the user has not yet SSO'd into the Resource Authorization Server."
So the spec recommends carrying an email, permits resolving subjects with it, explicitly contemplates provisioning new accounts from it, and provides no signal about its verification status. The case it names, a user who has not yet signed into your service, is precisely the first-contact case where you have nothing else to match on and the least basis for trusting what you are matching.
This is not an argument that the draft is wrong. The IdP is enterprise-operated and asserting attributes about its own employees, which is a meaningfully stronger position than a consumer social login. It is an argument that the trust you are extending is trust in an IdP's provisioning hygiene, not trust in a verified fact, and that you should decide that deliberately rather than by writing the obvious if statement.
What you are actually allowed to key on
Four claims can carry identity, and they are not equivalent.

sub is required, and it is an identifier in the IdP's namespace, not yours. Section 3.1 sets its uniqueness guarantee, and Section 6.3 restates it as a normative requirement on the IdP:
"For single-tenant issuers: The subject identifier MUST be unique when scoped with issuer (iss + sub).For multi-tenant issuers: The subject identifier MUST be unique when scoped with issuer and tenant (iss + tenant + sub)."
Read that as a correction to the advice in the August 6 post, which says to key on (iss, sub). For EMA that is not sufficient. Against a multi-tenant issuer, (iss, sub) is not guaranteed unique and tenant is part of the key. The draft puts the obligation on the IdP to include tenant when it matters, but you are the one who eats the collision if it does not, so treat the tenant column as mandatory in your schema rather than conditional.
aud_sub is your own identifier for the user, as known by the IdP. When present it solves the entire problem, because there is nothing to resolve: the IdP is telling you which of your users this is. It is also the weakest-specified claim in the draft. It is OPTIONAL, its use for resolution is a bare MAY, it has no processing rules anywhere in Section 4.4.1, it appears nowhere in the security considerations, and the only uniqueness requirement attached to it applies to the combination aud plus aud_tenant plus aud_sub, not to aud_sub standing alone.
The practical consequence is that aud_sub is the claim worth asking your customers' IdPs to send, and the claim you cannot design around receiving. Prefer it when it arrives. Do not require it.
sub_id carries a subject identifier in another namespace, typically a SAML NameID, for servers that resolve users by their SAML SSO identity. Its processing rules are the strictest thing in the draft, and they are easy to implement wrongly. Section 3.2.2:
"The Resource Authorization Server MUST compare every member of the SAML NameID Subject Identifier that is part of the set of identifier fields it uses for subject resolution for that SAML issuer. The Resource Authorization Server MUST NOT resolve the subject using only the nameid value unless local policy explicitly defines nameid alone as the subject identifier for that SAML issuer. In particular, when the SAML Assertion Subject <NameID> is scoped to a Service Provider, the sp_name_qualifier value is part of the subject namespace."
A NameID scoped to one service provider is not the same subject as the same string scoped to another. Matching on nameid alone collapses that distinction.
There is also a trust rule in Section 9.5 that is easy to miss because it is a negative requirement:
"When the ID-JAG includes a sub_id claim, the Resource Authorization Server MUST NOT use the sub_id.issuer value to establish trust in the ID-JAG issuer. The ID-JAG MUST first be validated using the iss claim, signature, audience, expiration, and client binding according to this specification."
Validate first on iss. Only then, and only when your local configuration associates that validated issuer with the SAML issuer named in sub_id, may you use the NameID. An implementation that reads sub_id.issuer to decide which keys to verify against has inverted the order and will accept a grant on the strength of a claim inside the unverified token.
email is the fallback, with the caveat above. Note one more thing about it, from Section 9.6: the ID-JAG is visible to the client that presents it, so an email or a NameID in the token discloses that identifier to the client, which may not otherwise have it. The draft asks IdPs to minimise what they include. If you are the one requesting claims, ask for what you need to resolve and nothing more.
The four ways this fails
First contact, with a user you have never seen. An employee has been granted access in the IdP but has never signed into your service, so you hold no mapping. You either provision from the ID-JAG's claims or refuse the grant. The spec offers JIT provisioning as the answer and leaves it entirely to policy. This is the least dangerous of the four failures and the most common.
Email matches a local user carrying a different sub. The scenario from the top of this post, and the one worth losing sleep over. Link Alice on the strength of the matching address and you have granted a federated principal access to an existing local account on the basis of a string, with no verification claim attached to it and nobody available to confirm the join. It is the classic-federated merge attack from the 2022 pre-hijacking research, with the mitigation those researchers recommend, requiring the user to prove control of the existing account, unavailable because there is no user in the flow.
Cross-tenant assertion. Trust establishment is out of scope in the draft. There is no normative mechanism for how you discover an issuer's keys or come to trust it, which means the mapping from issuer to your tenant lives in your configuration. Get it wrong and one customer's IdP can assert a subject that resolves into another customer's tenant. Your resolution query must be scoped by the tenant the issuer is configured for, not merely by the claims in the token.
Pairwise subject mismatch. If the IdP issues pairwise subject identifiers, the sub it puts in an ID token for the client is not the sub it would put in an ID token for you. The draft's Section 5 note assigns the IdP responsibility for getting this right:
"The IdP Authorization Server needs to ensure that the subject identifier issued in the ID-JAG is the same identifier for the user that it would have included in an ID Token intended for the Resource Authorization Server."
When an IdP gets that wrong, the symptom is not an error. It is an ID-JAG whose sub matches nothing you hold, for a user who has signed in successfully many times, which looks exactly like first contact and will quietly provision a duplicate account if your fallback is JIT.
A resolution order that holds
Resolve in order of how much the identifier actually proves, and stop at the first hit.
Three things about that shape are deliberate.
The tenant is resolved from configuration before any lookup runs, so every query is already scoped and a token cannot reach across customers by asserting a different tenant string.
Step 4 splits into two different questions that get conflated constantly. Provisioning a brand new user from an unmatched email is a low-risk operation: nothing existing is at stake. Adopting an existing local account on the strength of a matching email is the account takeover case. Those deserve separate policy flags, and in the code above they are separate. A per-issuer mayLinkOnEmail that defaults to false, with admin review as the fallback, gives you the enterprise convenience where a customer has explicitly accepted the trade and denies it silently everywhere else.
Failure returns invalid_grant, which is what the draft gives you for essentially every validation failure. Refusing is the right default. An unresolvable subject is not an error condition to paper over; it means you do not know who is asking.
Refusing is a product decision, so make it visible
The uncomfortable part of that last paragraph is that invalid_grant is a terrible customer experience. An admin granted access in Okta, the employee's client attempted the exchange, and it failed with an opaque OAuth error nobody in the loop can debug. They will file a ticket against you, not against their IdP.
So build the reconciliation path before you need it. Log every refused resolution with the issuer, tenant, sub, and whichever claims were present, and surface those as pending links an admin can approve in your dashboard. That turns an unexplained failure into a queue with an owner. It also gives you the audit record the extension itself does not provide, since, as the Inside the ID-JAG post notes, the IdP's visibility stops at token issuance and does not extend to what happened afterwards.
The broader point is that EMA moved the consent decision into an admin console you cannot see, and account linking is the one part of that decision it left with you. The protocol will tell you, with cryptographic confidence, that an enterprise IdP asserts this subject may use this client to reach your server. Whether that subject is the Alice you already have a row for is a question about your data, and no amount of signature validation answers it.
The decisions the spec leaves to you
Conformance is the easy half, and the validation rules for that are in the ID-JAG walkthrough. What follows is the other half: the choices the draft explicitly defers to local policy, where there is no MUST to follow and a wrong answer is silent. Each one needs a deliberate answer before you ship, so here they are with the default I would argue for.
- Which claims resolve a subject, and in what order? Nothing in the draft ranks them. Default:
aud_sub, then(iss, tenant, sub), thensub_idwith full member comparison, thenemail. Stop at the first hit, and treat the order as configuration you can tighten per issuer rather than a constant. - May a matching email adopt an existing local user? This is the account takeover decision, and the draft neither permits nor forbids it. Default: no, off per issuer, with an admin review queue as the fallback. Turn it on only where a customer has explicitly accepted the trade.
- May a matching email provision a new local user? A different question that gets conflated with the one above. Default: yes. Nothing existing is at stake, and Section 3.1 contemplates exactly this case.
- How does an issuer map to one of your tenants? Trust establishment is out of scope in the draft, so this is entirely your configuration. Default: an explicit issuer-to-tenant record you administer, resolved before any lookup runs, never inferred from a claim in the token.
- Which NameID members participate in resolution, for each SAML issuer? Section 3.2.2 requires you to compare every member in the set you use, and forbids resolving on
nameidalone unless policy explicitly says that is the identifier. Default: every member present, includingsp_name_qualifier. If you wantnameidalone, write that down as policy per issuer rather than letting it happen by omission. - What happens when nothing resolves? Default:
invalid_grant, plus a logged pending-link record carrying the issuer, tenant,suband the claims that were present, surfaced to an admin. Refusing without a reconciliation path converts a security decision into a support ticket. - Do you require
aud_subat onboarding? Not a code decision but a contract one. Default: ask for it, design for its absence. When a customer's IdP can send it, resolution stops being a guess.
Frequently asked questions
Does an ID-JAG contain email_verified? No. Section 3.1 defines email as an optional claim and does not define email_verified at all. If your linking logic branches on a verification flag, that branch will never be true against an ID-JAG, and you should decide explicitly what you want to happen instead.
Is (iss, sub) enough to key a federated identity under EMA? Not against a multi-tenant issuer. Section 6.3 requires subject uniqueness scoped as iss plus tenant plus sub for multi-tenant issuers, and only iss plus sub for single-tenant ones. Include the tenant in your key so you do not have to know which kind of issuer you are talking to.
Should I auto-provision a user from an ID-JAG? Provisioning a new user from claims that match nothing is a reasonable default, and the draft contemplates it. Adopting an existing local user because an email matched is a different operation with a real takeover risk, and it should be off unless a specific customer has opted in.
What is aud_sub and should I require it? It is the IdP's record of your own identifier for the user, and when it is present subject resolution is solved outright. It is optional with no processing rules in the draft, so prefer it when it arrives and never depend on it.
Can I use sub_id.issuer to work out which issuer signed the token? No. Section 9.5 forbids it. Validate the token first on iss, signature, audience, expiry and client binding, and only then use a SAML NameID, and only when your own configuration associates the validated issuer with that SAML issuer.