Test failed sign-ins: denied users, silent sign-in and revoked refresh tokens
Give a test user a sign-in outcome, and signing in as that user fails the way you choose. Your app's error handling gets tested against real error responses, without changing a real identity provider.
The problem
The unhappy paths are the ones that go untested: the user who isn't allowed in, the identity provider that is briefly down, the silent sign-in that needs a prompt, the refresh token that was revoked. Against a real provider they need tenant policy changes, consent revocation or waiting.
Sign-in outcomes
A sign-in outcome is a property of a test user, like its roles. It is a Pro feature: it takes effect with an active Pro or Enterprise plan, or for Vetted tenants. Set it in the user dialog of the management page (Sign-in outcome), or as sign_in_outcome in the Agent API.
| Outcome | Label | What your app receives | Where |
|---|---|---|---|
access_denied | Denies access | error=access_denied and your state | Authorize redirect, whether the user is picked, pinned or the only user |
temporarily_unavailable | Unavailable | error=temporarily_unavailable | Authorize redirect |
interaction_required | Fails silent sign-in | error=interaction_required for prompt=none; interactive sign-in works | Authorize redirect, for a pinned or only user |
refresh_revoked | Refresh fails | HTTP 400 invalid_grant on every refresh; sign-in works | Token endpoint, grant_type=refresh_token |
Every error carries an error_description that names the user and the outcome. For a pinned user it points to the pinned user page and prompt=login; for a silent sign-in or a refresh it says what to do instead. With the Microsoft profile it starts with the Entra code (for example AADSTS50105), on both route families, so MSAL and Microsoft.Identity.Web react as they do to Entra.
Set it up
- Sign in with your verified owner email and add a user, for example
dana, with Sign-in outcome set to Denies access. - Start your app's sign-in. The user picker marks Dana as Denies access; pick her and your app is redirected back with
error=access_denied. - For silent sign-in, set a user to Fails silent sign-in, pin that user (or make it the only user) and send
prompt=none. - For refresh, set a user to Refresh fails, sign in with
offline_access, and refresh. MSAL.NET raisesMsalUiRequiredExceptionfor this.
Verify it
- The owner's Test sign-in shows a sign-in that ends with
access_denied,temporarily_unavailableorinteraction_requiredas expected when the configuration has a user with that outcome, above the rawerroranderror_description. - Setting the outcome back to Signs in normally restores sign-in. A pinned failing user never leaves you stuck: send
prompt=loginor clear the pin on the pinned user page.
Limitations
- Outcomes are per user. They don't depend on the client or the request, and they don't change over time.
- Token-level faults (a wrong audience, a bad signature) and slow or failing endpoints are not simulated.
- When a plan ends, stored outcomes are kept but ignored, and take effect again with Pro.
- For development and testing only. Never use HeyIdentity to protect production identities.