# Authentication and Authorization

Iwf provides primitives for common auth flows without forcing a generated user
model or a database-specific account schema.

## Framework Boundary

Iwf auth is a framework-level state machine and adapter contract, not a hosted
identity product. The framework owns validation helpers, signed-session
integration, token lifecycle rules, OAuth state checks, and passkey challenge
lifecycle checks.

Applications own the production boundaries:

- user, token, lockout, OAuth identity, and passkey credential persistence
- password hashing/KDF implementations and parameters
- cryptographic random token generation
- OAuth provider HTTP exchange, profile fetching, and provider-specific claims
- WebAuthn authenticator-data, client-data JSON, signature, RP id, origin,
  user-presence/user-verification, and sign-count verification

Keep these boundaries explicit in app code. Iwf should receive app-owned
adapters such as `PasswordHashProvider`, `OAuthTokenExchange`,
`OAuthProfileFetch`, `PasskeyVerifier`, and `SessionTokenSource`; it should not
hide production storage, random token generation, or cryptography behind a
framework default.

## 2. Credentials and Login

Core auth helpers cover:

- credentials
- pluggable password hashing
- effectful password hashing providers for production Argon2id/bcrypt adapters
- signup user construction
- login validation
- login/logout session helpers
- current-user session lookup

`PasswordHasher` is the pure helper surface used by tests and deterministic
state helpers. Production code should use `PasswordHashProvider` plus
`signupUserWithProvider`, `loginUserWithProvider`, and
`completePasswordResetWithProvider`, backed by an Argon2id, bcrypt, scrypt, or
PBKDF2 implementation. `passwordHashUsesProductionFormat` is a guard for
encoded hashes such as `$argon2id$...` and `$2b$...`.

Iwf does not ship a password KDF implementation. Applications must provide the
production hashing adapter and keep password hash parameters aligned with their
security policy.

Generated user models can be adapted with `CurrentUserModel`:

```idris
userModel : CurrentUserModel User
userModel =
  MkCurrentUserModel
    (\user => show user.id)
    (\user => user.email)
    (\user => user.passwordHash)
```

Then use:

- `authUserFromModel`
- `loginUserModel`
- `loginModelSession`

Install the current-user lookup at the app runner boundary with `withAuth`:

```idris
findCurrentUser : DatabasePool -> String -> ControllerApp (Maybe User)
findCurrentUser pool userId =
  -- Load the user row for `userId` from your app database.
  pure Nothing

currentUserAuth : CurrentUserAuth
currentUserAuth =
  currentUserAuthForModel userModel findCurrentUser

main : IO ()
main =
  runApp
    (app "My App" routes
      |> withAuth currentUserAuth)
```

Handlers can then read `context.currentUser` or use the guard helpers below.
Apps that want IHP-style typed `currentUser` helpers should define them once in
`Application/CurrentUser.idr` and import that module from controllers.

## 3. Controller Guards

For one handler:

```idris
settings : ControllerContext -> ControllerApp Response
settings context = do
  user <- requireCurrentUser context
  pure (render context (settingsPage context user [] []))
```

Useful helpers:

- `ensureIsUser`
- `currentUserOrNothing`
- `currentUserIdOrNothing`
- `requireCurrentUser`
- `requireCurrentUserId`

## 4. Authorization

Use access-denied helpers when a user is authenticated but not allowed:

```idris
deleteArticle : ControllerContext -> ControllerApp Response
deleteArticle context = do
  accessDeniedUnless (canDelete context) context
  pure (seeOther "/")
```

Helpers:

- `accessDeniedUnless`
- `accessDeniedWhen`
- `renderAccessDenied`

Use access denied for `403 Forbidden`. Use login redirects only for anonymous
users.

## 5. Basic Auth

Basic Auth is useful for small internal surfaces:

```idris
adminVerifier : BasicAuthVerifier
adminVerifier credentials =
  credentials.username == "admin" && credentials.password == "secret"

adminRoute : Handler
adminRoute =
  protectBasicAuth "Admin" adminVerifier (\_ => okText "admin")
```

Use `withBasicAuth` or `withBasicAuthContext` when the handler needs decoded
credentials.

## 6. Account Safety

Iwf includes pure helpers for:

- account lockout
- rate limiting
- password reset tokens
- email verification tokens
- invite-only signup

Use persisted lockout/rate-limit state in the app database and call:

- `loginUserWithLockout`
- `checkRateLimit`
- `issuePasswordResetToken`
- `verifyPasswordResetToken`
- `completePasswordReset`
- `issueEmailVerificationToken`
- `verifyEmailVerificationToken`
- `completeEmailVerification`
- `issueInvite`
- `verifyInvite`
- `acceptInvite`

Iwf also exposes `authPersistenceSchemaSql`, which creates tables for password
reset tokens, email verification tokens, invites, account lockouts, rate
limits, OAuth state, and passkey challenges. Apps can use the full SQL string
or the individual `passwordResetTokenSchemaSql`,
`emailVerificationTokenSchemaSql`, `inviteSchemaSql`,
`accountLockoutSchemaSql`, `rateLimitSchemaSql`, `oauthStateSchemaSql`, and
`passkeyChallengeSchemaSql` definitions in migrations.

The framework token records carry hashed raw tokens, `expiresAt`, and consumed
state. `verifyPasswordResetToken`, `completePasswordReset`,
`verifyEmailVerificationToken`, and `completeEmailVerification` enforce expiry
and reuse checks. Application code still owns persistence: load the token row,
call the helper, then update the stored consumed state inside the same
application transaction.

## 7. OAuth and Passkeys

OAuth helpers cover provider authorization URLs, state issue/verify, callback
handling, token exchange request construction, PKCE verifier storage, and
identity linking:

- `OAuthProviderConfig`
- `OAuthPkceChallenge`
- `OAuthCallback`
- `OAuthTokenRequest`
- `OAuthTokenResponse`
- `OAuthTokenExchange`
- `OAuthProfileFetch`
- `oauthAuthorizationUrl`
- `issueOAuthState`
- `verifyOAuthState`
- `completeOAuthCallback`
- `completeOAuthLogin`

Iwf validates callback shape, provider state, expiry, reuse, and PKCE before it
builds an `OAuthTokenRequest`. The application still performs the provider HTTP
exchange through an `OAuthTokenExchange`, fetches the provider profile through
an `OAuthProfileFetch`, and persists linked identities.

Passkey/WebAuthn helpers cover challenge expiry, purpose checks, reuse
prevention, credential ids, and sign-count advancement:

- `issuePasskeyChallenge`
- `completePasskeyRegistration`
- `verifyPasskeyAssertion`

`PasskeyVerifier` is intentionally adapter-only. It is the boundary where an app
calls a real WebAuthn library to verify the authenticator data, client data JSON,
signature, RP id, origin, user presence/user verification flags, and sign count.
Iwf only stores and validates challenge lifecycle state.

## Next

Read [Jobs, Scripts, and Mail](jobs-scripts-and-mail.md).
