Before an attacker guesses a single password, they want to know which accounts are worth guessing against. In Microsoft 365 they can find out — without any credentials, without tripping a lockout, and without ever authenticating. Post a username to the right endpoint and the tenant tells you whether that account exists. That is tenant enumeration, and it is the quiet first step in front of almost every password-spray and phishing campaign I get called in to clean up.
In short
Tenant enumeration lets an unauthenticated attacker confirm whether an email address is a real account in a Microsoft 365 tenant. Endpoints like GetCredentialType return a plain "exists / does not exist" flag, so the attacker validates targets first and only then sprays passwords — which is far harder to detect than brute-forcing blind. Microsoft can't fully close it because federation and B2B need to know if a user exists. You can't remove the leak, but you can make it useless: block legacy auth, enforce MFA, turn on Smart Lockout and password protection, and watch the sign-in logs.
What tenant enumeration actually is
Tenant enumeration is the ability for an unauthenticated attacker to determine whether a given email address or username is valid in a target Microsoft 365 tenant. Instead of blindly guessing credentials, the attacker first validates which accounts exist, then focuses password attacks on the ones that do. The security principle it violates is a simple one: don't tell an attacker whether a user exists. M365 tells them anyway.
Why it is a problem
Confirming that [email protected] is a real account changes the economics of an attack. Once an attacker has a list of accounts they know are real, they can:
- Launch password spraying against validated accounts — one or two common passwords tried across thousands of confirmed users is much harder to detect than hammering a single account.
- Run spear phishing with higher confidence, because they are not wasting shots on addresses that bounce.
- Map the org structure — who works there, and the tenant's username convention.
- Clear the first hurdle of most MFA-bypass techniques. MFA fatigue and token theft both need a valid UPN to aim at.
How enumeration works — the technical vectors
This is not one bug. It is a family of endpoints that each answer the same question in a slightly different dialect.
1. GetCredentialType
The most famous one. A single unauthenticated POST returns a flag telling you whether the account exists:
POST https://login.microsoftonline.com/common/GetCredentialType
Content-Type: application/json
{ "Username": "[email protected]" }
The response carries IfExistsResult: 0 for a valid user, 1 for one that does not exist. The endpoint was built for client-side UX — it lets the login page decide whether to show a password field or redirect you to your organisation's identity provider. It was never meant as an oracle, but it is completely unauthenticated and it answers the existence question directly. Tooling like o365creeper, MailSniper, and CredMaster has leaned on it since at least 2018.
2. Login endpoint timing and error codes
Post a username to the token endpoint (/common/oauth2/token) and the tenant reacts differently depending on the account:
- Valid user → a "password required" response, or a redirect to the federated IdP (ADFS, Ping, Okta).
- Invalid user → an immediate "user not found in this tenant" error.
Different response times, error codes, and HTTP statuses all leak existence. A federated valid user gets redirected somewhere; a non-existent user gets an instant rejection. Even without GetCredentialType, the timing alone is enough.
3. Autodiscover, OWA, EWS and ActiveSync
The legacy mail protocols were designed long before anti-enumeration was a concern, and they answer valid and invalid users differently:
autodiscover-s.outlook.com/autodiscover/autodiscover.xmlreturns mailbox configuration for a valid user and a 600 "Invalid Request" for one that does not exist./ews/,/owa/and/Microsoft-Server-ActiveSynceach give distinguishable responses for valid versus invalid accounts.
4. Azure AD B2B invitation timing
The B2B invitation API (/invitations) behaves differently when the target already exists versus when it doesn't, which leaks existence at the tenant level as a side effect of how guest onboarding resolves an address.
5. Teams external access
Trying to start a chat with an external user can reveal whether that user exists. Teams has patched and re-broken this behaviour more than once, so treat it as a moving target rather than a closed door.
6. OpenID Connect discovery
The OIDC metadata endpoints (/.well-known/openid-configuration) combined with tenant-specific endpoints leak whether a domain is Managed or Federated, and to which IdP. That single fact tells an attacker whether to run a straight password spray or aim at the ADFS endpoint instead — reconnaissance before a single credential is tried.
Why Microsoft can't easily fix it
This is not a bug waiting on a patch. It is baked into how cloud identity works, and every layer has a reason it leaks.
| Layer | Why it leaks existence |
|---|---|
| Federation | A federated domain must redirect the user to the correct IdP. If the user doesn't exist, there is no IdP to redirect to — and the redirect (or its absence) reveals which case you are in. |
| B2B / guest | Inviting a guest by email inherently requires resolving whether that address exists. |
| UX design | GetCredentialType exists so the login UI can show "enter password" versus "redirecting to your organisation" — two paths for two account states. |
| Legacy protocols | Autodiscover, ActiveSync and EWS predate anti-enumeration thinking entirely and were never designed to hide it. |
Microsoft has made incremental improvements — Smart Lockout rate-limits per account and per IP to make spraying harder, Conditional Access can block legacy auth (which closes the legacy enumeration vectors along with it), and some error messages have been obfuscated in certain flows. But the root cause stays open, because federation and B2B genuinely need to know whether a user exists.
What you can do as a defender
You cannot delete the leak. What you can do is make the information it leaks worthless — a confirmed username is only dangerous if it leads somewhere. Layer the controls so it doesn't.
Immediate, high impact
- Block legacy authentication via Conditional Access. This kills the Autodiscover, EWS and ActiveSync enumeration vectors at the same time as it closes basic-auth password spray.
- Enable Smart Lockout with aggressive thresholds so validated accounts can't be sprayed at volume.
- Turn on password protection — ban common and custom weak passwords so a spray of the usual suspects finds nothing.
- Enforce MFA for all users. This is the one that matters most here: enumeration plus a correct password still fails without the second factor, so a valid UPN on its own is worthless.
Medium term
- Monitor sign-in logs for enumeration patterns — high volumes of error
50126(invalid username or password) or50053(account locked out) from a single IP are the fingerprint of a spray in progress. - Use Microsoft Entra ID Protection to detect leaked credentials, risky sign-ins and impossible travel, and feed that risk into Conditional Access.
- Disable basic auth tenant-wide if you somehow still have it on. It is the single biggest open door for both enumeration and spraying.
Key takeaways
- Tenant enumeration confirms which accounts exist before any password is tried — it is reconnaissance, not an attack in itself.
GetCredentialTypeand itsIfExistsResultflag are the classic vector, but timing, legacy mail protocols, B2B, Teams and OIDC discovery all leak the same fact.- Microsoft can't fully close it — federation and B2B need to resolve whether a user exists.
- The defence is not to hide the username, but to make it useless: MFA, block legacy auth, Smart Lockout, password protection, and monitoring.
If you want to know how exposed your own tenant is — whether legacy auth is still open, whether your sign-in logs are already full of 50126 spikes, whether MFA actually covers everyone — that is exactly the kind of review I do. Get in touch.
Glossary
- Tenant enumeration
- Determining, without authentication, whether a username or email address is a valid account in a target Microsoft 365 tenant.
- GetCredentialType
- An unauthenticated Microsoft login endpoint that returns an
IfExistsResultflag indicating whether an account exists; built for login-page UX, abused for enumeration. - Password spraying
- Trying one or two common passwords across many accounts, rather than many passwords against one — low and slow, to stay under lockout thresholds.
- Managed vs Federated domain
- A managed domain authenticates directly against Entra ID; a federated domain redirects to an external IdP such as ADFS. OIDC discovery reveals which, guiding the attacker's next move.
- Smart Lockout
- Entra ID's adaptive lockout that rate-limits sign-in attempts per account and per IP to blunt password spraying.
- Entra ID Protection
- Microsoft's risk engine that flags leaked credentials, risky sign-ins and impossible travel, and can feed risk into Conditional Access.
Frequently Asked Questions
Can I stop Microsoft 365 tenant enumeration completely?
No. The leak is architectural — federation and B2B guest invitations both need to resolve whether a user exists, so endpoints like GetCredentialType and the login flow will keep answering that question. The realistic goal is to make a confirmed username worthless by layering MFA, blocking legacy authentication, enabling Smart Lockout and password protection, and monitoring the sign-in logs.
Is tenant enumeration illegal or an actual attack?
On its own it is reconnaissance, not intrusion — no credentials are used and nothing is accessed. It is the step that comes before password spraying or phishing. That's exactly why it matters: stopping the follow-on attack is where your controls earn their keep.
Does enforcing MFA make enumeration harmless?
MFA is the single most important control here, because a valid username plus a correct password still fails without the second factor. It does not stop the enumeration itself, so pair it with blocking legacy authentication (which bypasses MFA) and Smart Lockout to cover the whole path.
Which sign-in log signals point to an enumeration or spray attempt?
Watch for high volumes of error code 50126 (invalid username or password) and 50053 (account locked out) originating from a single IP or a small range, especially spread thinly across many accounts. Entra ID Protection's risky-sign-in and impossible-travel detections add another layer.