The Email Claim Anyone Could Set — JavaScript Bug Hunt
Modelled on "nOAuth", disclosed by Descope in June 2023. In Microsoft Azure AD (now Entra ID), the email claim of a multi-tenant app's sign-in token is a…
- Language: JavaScript
- Layer: Backend
- Difficulty: Medium
- Concepts: Security, Auth
- Modelled on: Azure AD nOAuth · 2023
- Visible tests: a returning user reaches their own account; another tenant claiming the same email gets its own account
- Reward: 50 XP for a complete fix
Briefing
Modelled on "nOAuth", disclosed by Descope in June 2023. In Microsoft Azure AD (now Entra ID), the email claim of a multi-tenant app's sign-in token is a mutable, unverified attribute that an administrator of any tenant can set. Apps that used that claim to find or merge user accounts could therefore sign someone into another person's account. Microsoft's guidance is to identify users by the immutable oid (object id) together with tid (tenant id), never by email.
This reconstruction's signIn looks users up by the token's email and links the account to whichever tenant presented it.
Fix signIn so accounts are keyed on tid + oid.
Bug report
BUG-NOAUTH · Priority: Critical · Reported by: security
signIn(store, claims) — claims come from a signature-verified ID token: { tid, oid, email }. The account key is tid + "/" + oid.
- tid or oid missing/empty -> throw
- a user whose azureKey equals the key -> return that user (whatever the email claim says now; the stored user is not modified)
- otherwise create a NEW user with store.add(claims.email, key) and return it. Never attach the identity to an existing user because the email matches — not even an account that has no azureKey yet.
Observed: a user from another tenant whose email claim was set to an existing customer's address was signed in as that customer.
Logs
[sso] sign-in tid=2f9c… oid=a71e… email=ana@corp.example -> user u1
[sso] user u1 azureKey changed 7b20…/0c4d… -> 2f9c…/a71e…The code as shipped
src/sso/link.js (editable)
// Signs a user in from a verified Microsoft identity platform ID token.
// claims = { tid, oid, email }
exports.signIn = function (store, claims) {
var user = store.find(function (u) { return u.email === claims.email; });
if (user) {
user.azureKey = claims.tid + "/" + claims.oid;
return user;
}
return store.add(claims.email, claims.tid + "/" + claims.oid);
};
Read-only context: src/sso/users.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.