# Sign-in Flows

> Every route a person can take through the hosted sign-in UI, from the application's redirect to its callback, with each screen, state and error.

These pages follow a person through the hosted sign-in UI the way they meet it: an
application sends them to `/authorize`, they sign in, sign up, verify an address, prove a
second factor or consent, and come back to the application's `redirect_uri`. Every screen
is shown as it renders today, with its loading state and the errors it can show.

The screenshots come from nauthera-server's Storybook, one story per state, and are
refreshed from it automatically. **Open in Storybook** under a screenshot opens the live
story, where you can switch language, brand and layout.

## The whole map

```mermaid
flowchart LR
  App([Application]) --> AZ["/authorize"]
  AZ -->|signed in| CB([Back to the application])
  AZ -->|no session, or prompt=login| LOGIN["/login"]
  LOGIN <-->|Create one / Sign in| REG["/register"]
  LOGIN -->|Forgot password?| FP["/forgot-password"]
  FP -->|emailed link| RP["/reset-password"]
  RP -->|Sign in| LOGIN
  LOGIN -->|Continue with provider| IDP([Identity provider])
  AZ -->|broker client| BE["Broker email screen"]
  BE -->|member's domain| MEMBER([Member organization's sign-in])
  MEMBER --> NEXT
  IDP --> NEXT
  LOGIN -->|Sign in with a passkey| PK["/passkey"]
  PK --> NEXT
  LOGIN --> NEXT{Next step}
  REG --> NEXT
  NEXT -->|address not verified| VE["/verify-email"]
  VE -->|emailed link, Continue| LOGIN
  NEXT -->|no second factor yet| SETUP["/mfa/setup"]
  NEXT -->|second factor| MFA["/mfa"]
  NEXT -->|passkey offer| OFFER["/mfa/setup?offer"]
  NEXT -->|consent needed| CONSENT["/consent"]
  NEXT -->|new password required| CP["/change-password"]
  CP -->|Sign in| LOGIN
  SETUP --> CONSENT
  MFA --> CONSENT
  OFFER --> CONSENT
  NEXT -->|done| CB
  SETUP --> CB
  MFA --> CB
  OFFER --> CB
  CONSENT -->|Allow| CB
  CONSENT -->|Deny| CBE([Back with access_denied])
```

| Flow | Starts at | Page |
|---|---|---|
| Sign in with a password | `/login` | [Sign in](https://www.nauthera.io/docs/v1/sign-in-flows/sign-in) |
| Replace a password at an administrator's request | `/change-password` | [Sign in](https://www.nauthera.io/docs/v1/sign-in-flows/sign-in#a-new-password-is-required) |
| Create an account | `/register` | [Sign up](https://www.nauthera.io/docs/v1/sign-in-flows/sign-up) |
| Verify an address, reset a password, accept an invitation | an emailed link | [Emailed links](https://www.nauthera.io/docs/v1/sign-in-flows/emailed-links) |
| Sign in through an identity broker | the broker's email screen | [Broker sign-in](https://www.nauthera.io/docs/v1/sign-in-flows/broker) |
| Set up or prove a second factor | `/mfa/setup`, `/mfa` | [Second factor](https://www.nauthera.io/docs/v1/sign-in-flows/second-factor) |
| Passkeys: offer, passwordless sign-in | `/mfa/setup`, `/passkey` | [Passkeys](https://www.nauthera.io/docs/v1/sign-in-flows/passkeys) |
| Sign in through another identity provider | a provider button | [Social sign-in](https://www.nauthera.io/docs/v1/sign-in-flows/social-sign-in) |
| Consent, and connecting a device | `/consent`, `/device` | [Consent and devices](https://www.nauthera.io/docs/v1/sign-in-flows/consent-and-devices) |
| Sign out | `/logout` | [Sign out](https://www.nauthera.io/docs/v1/sign-in-flows/sign-out) |
| Throttles, locks, expired sessions, unknown pages | any screen | [When something goes wrong](https://www.nauthera.io/docs/v1/sign-in-flows/errors) |

## Rules every flow follows

- **The application's request travels with the user.** Every screen carries the
  authorization request in its URL, so a detour to sign-up, a password reset or a second
  factor ends at the same application with the same `state`. Emailed links carry it too.
- **Every wait shows.** A button that sends something spins and locks its form until the
  server answers; one click sends one request.
- **Every failure says what happened and leaves a way forward.** A refusal the user can fix
  is a banner on the form they are on, and the form keeps what they typed. A throttle, a
  lapsed sign-in or a refusal with nothing to fix gets a page of its own, with an action.
- **A screen that does not know the application says to go back to it.** The sign-in
  screen answers `404` unless its URL names a registered client, so a screen reached
  without one (an invitation link, a link opened on another device) ends with *"To
  continue, go back to the app you came from and sign in there."* rather than a button that
  goes nowhere. A flow the server finishes without an application, such as a federated
  sign-in that came back after it lapsed or a sign-up from a bare `/register` link, is sent
  to `/return`, which says the same.

<StoryScreenshot
  id="screens-error-pages-return-to-app--default"
  alt="Return to your app: this page can't tell which app you were signing in to"
  caption="/return — where a flow that no longer names its application ends"
/>
