# What the compiler checks of sign-in

Jadpo checks your sign-in setup when it compiles, so a mistake is an
error with a fix rather than a sign-in that fails in production. Each row
says what you wrote and the error you get.

## Magic link settings

| What you wrote | Error |
|---|---|
| `sign_in_providers:`, the old name | `semantic.auth_method_unknown`, whose edit renames it `sign_in:` |
| `magic_link:` without `browser_session:`, or without its `origin:` | `semantic.auth_setting_required` |
| `magic_link:` without the app's `email:` connection, and no `send:` | `email.connection_required` |
| `magic_link:` without `application { display_name: }`, which Jadpo's email names your app by | `semantic.auth_setting_required` |
| `magic_link:` without `link:` | `semantic.auth_setting_required` |
| `address:` naming anything but a field of the principal's user entity built on `Email` and `unique: true` | `semantic.auth_setting_invalid` |
| `address:` naming a field the entity doesn't have | `type.unknown_field` |
| No `address:`, and no `email` field on the entity | `semantic.auth_setting_required` |
| A setting `magic_link:` doesn't take | `semantic.auth_setting_unknown` |
| A `link:` that isn't a path on your app's site, a `path:` that isn't fixed segments, `expires_after:` under `1m`, `resend_after:` not above zero, a count under 1, or `same_browser:` that isn't `true` or `false` | `semantic.auth_setting_invalid` |
| A setting loosened past its limit | `toolchain.not_built_yet`, until [acceptances](acceptances.md) put it in the approval report |
| `generate_routes:` that isn't `true` or `false`, or `false` beside `path:` | `semantic.auth_setting_invalid` |
| A route your app writes at a generated magic-link path | `route.path_overlap`; with `generate_routes: false` there are none |
| `MagicLinkAsk`, `MagicLinkConfirmation` or `MagicLinkCode` built with `{ ... }` anywhere but a test, fixture or seed | `type.not_record` |
| A request to `/auth/magic-link/redeem` without `api_token:`, which generates no such route | `semantic.request_route_unknown` |

## The first sign-in action

`on_first_sign_in:` naming anything but an action that takes one
`FirstSignIn` and returns the user entity's `id` is
`semantic.auth_setting_invalid`.

## Your own magic-link email

| What you wrote | Error |
|---|---|
| `email:` and `send:` together | `semantic.auth_setting_invalid` |
| `email:` naming anything but an `EmailMessage` of your app's | `semantic.auth_setting_invalid` |
| `email:` naming an email with a field, besides `to:` and `locale:`, that isn't picked from `MagicLinkEmail` | `semantic.auth_setting_invalid` |
| `send:` naming anything but an action that takes one `MagicLinkRequest` and returns `Unit` | `semantic.auth_setting_invalid` |
| `emit` of the email `email:` names | `event.emit_invalid` |
| Copying from `MagicLinkEmail` into anything but an email | `type.not_record` |
| A `SignInCode` kept in an entity | `type.auth_credential_stored` |
| A `SignInCode` in an `Output`, a failure or an event | `type.secret_exposed` |

See [Built-in emails](built-in-services.md) for both settings.

## Writing the magic-link routes yourself

| What you wrote | Error |
|---|---|
| `magic_links.request`, `.redeem` or `.redeem_code` with no `magic_link:`, or with its routes generated | `semantic.auth_setting_required` |
| One of them called outside an action | `type.auth_sessions_context` |
| One of them as a route's `run:` itself | `route.run_invalid` |
| A `SignInToken` kept in an entity | `type.auth_credential_stored` |
| A `SignInToken` in an `Output`, a failure, an event, an email, a message or a prompt | `type.secret_exposed` |

## Sign-in events

| What you wrote | Error |
|---|---|
| A subscriber of `SignedIn`, `SignInRefused` or `SignInCodesDisabled` with no `magic_link:` | `subscriber.event_invalid` |
| A field one of them doesn't have | `type.unknown_field` |
| `emit` of one of them | `event.emit_invalid` |
| `FirstSignIn` or a sign-in event built with `{ ... }` anywhere but a test, fixture or seed | `type.not_record` |

## Passkeys

| What you wrote | Error |
|---|---|
| `passkey:` without `browser_session:`, or without its `origin:` | `semantic.auth_setting_required` |
| `passkey:` without `magic_link:` in the same `sign_in:` | `semantic.auth_setting_required` |
| A setting `passkey:` doesn't take | `semantic.auth_setting_unknown` |
| `expires_after:` outside `1m` to `10m`, or a `path:` that isn't fixed segments | `semantic.auth_setting_invalid` |
| Anything in `sign_in:` but `magic_link:`, `passkey:` and `providers:` | `semantic.auth_method_unknown` |
| A route your app writes at a generated passkey path | `route.path_overlap` |
| A subscriber of `PasskeyAdded` or `PasskeyRemoved` with no `passkey:` | `subscriber.event_invalid` |
| `emit` of `PasskeyAdded` or `PasskeyRemoved` | `event.emit_invalid` |
| `test_passkeys.register` or `.sign_in` anywhere but a test | `semantic.request_invalid` |
| `test_passkeys.register` or `.sign_in` with no `passkey:` | `semantic.auth_setting_required` |
