Iwf Guide

Iwf Guide

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:

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:

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:

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:

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:

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.