@hackage yesod-auth-simple1.1.0

Traditional email/pass auth for Yesod.

yesod-auth-simple

This library is a Yesod subsite that serves as a basis for user management and authentication in the traditional way (that is, users and their credentials are stored in a database you control). It includes both the backend route handlers and the frontend UI for login, registration, and password resets, which can optionally be extended or replaced. As a user, you need to provide implementations of sending appropriate emails, and user persistence. Features include:

  • Ready-to-go route handlers and UI for login, registration and password reset flows.
  • Password hashing and salting is done via Colin Percival's Scrypt implementation. Just write the functions that store the hashes in the database.
  • Registration and password reset tokens are generated and hashed for you. You write your own read/write/delete persistence and code to send the emails.
  • A colourful password strength meter with feedback using zxcvbn (Traditional password checking based on length is also supported).
  • Sensible splitting of UI into subcomponents so that you can use, for example, just the password input and strength meter in your own larger-scale forms.
  • Useful lifecycle hooks that can be used to implement features like rate-limiting and session invalidation on password changes.

Installation

yesod-auth-simple is not yet on Hackage. For now, we recommend pulling directly from GitHub using Nix or Stack (instructions for Stack here). For nix, you will need to:

  1. copy the included yesod-auth-nix.nix derivation function into your project somewhere,
  2. change the src attribute to reference this remote repository (e.g., using fetchgit).
  3. build your project using a nixpkgs config that adds this package to the haskell.packages.overrides. More details on this Nix and Haskell workflow can be found here.

Usage

The library is built around the YesodAuthSimple type class. To use this package, you should write an instance of this class for the same type that instatiates the Yesod and YesodAuth type classes. To make the final connection, add Yesod.Auth.Simple.authSimple to the YesodAuth instance's authPlugins list.

data App = ...

instance Yesod App

instance YesodAuth App where
  -- Refer to documentation for yesod-auth: http://hackage.haskell.org/package/yesod-auth
  type AuthId App = Key User -- For some User datatype, often derived from Persistent models
  loginDest _ = HomeR
  logoutDest _ = HomeR
  authPlugins _ = [ authSimple ]
  getAuthId = return . fromPathPiece . credsIdent
  maybeAuthId = defaultMaybeAuthId

instance YesodAuthSimple App where
  type AuthSimpleId App = Key User -- Uses the same  User datatype as in YesodAuth

  passwordCheck = Zxcvbn Safe (Vec.fromList ["yesod"])

  insertUser email encryptedPass = -- Add the user to your database!
  updateUserPassword userId encryptedPass = ...

  getUserId userEmail = -- Look in the DB for a corresponding user ID if one exists
  getUserPassword userId = -- Look in the DB and return a Crypto.Scrypt.EncryptedPass
  getUserModified userId = ...

  -- The following functions should persist the hashed token for later lookup
  sendVerifyEmail email urlToSend hashedToken = ...
  sendResetPasswordEmail email urlToSend hashedToken = ...

  -- The following functions should return an email or user id if the token is valid
  matchRegistrationToken hashedToken = ...
  matchPasswordToken hashedToken = ...

  onRegistrationTokenUsed = -- e.g. invalidate the new user's token
  onPasswordUpdated  = -- e.g. invalidate sessions and any password reset tokens
  afterPasswordRoute = -- e.g. redirect to the homepage after resetting password
  onRegisterSuccess  = -- e.g. redirect them to the homepage after registering

The rest of the typeclass consists of various Yesod widget templates with default implementations. Override any as you see fit. The default templates are exported at the module's top level, so you can use them inside your own larger container widgets.

All customised templates with password form inputs must include, in the form's POST body, an anti-CSRF token using Yesod.Core.Handler.defaultCsrfParamName with the value retrieved from Yesod.Core.Handler.reqToken. A common template idiom is the following:

<form>
  $maybe antiCsrfToken <- reqToken request
    <input type=hidden name=#{defaultCsrfParamName} value=#{antiCsrfToken}>
  ... rest of form

Full details on the class functions can be found in the Hackage docs. An example of writing an instance for testing purposes can be found here.

The library provides Email and Password types for which you need a few Persist instances. You can choose from default text-derived instance as in the example, citext email instance for storing email in postgres, or write your own.

Security notes

This library will send POST requests containing the user's password (including as they type it via an XHR when you choose to use the Zxcvbn strength algorithm). It is therefore vital that you do not log the request bodies of these routes to avoid storing passwords in plaintext logs on your server. Sensitive routes are currently: loginR, setPasswordR, confirmR, and passwordStrengthR. We are investigating ways to improve developer experience here.

Please also beware of logging the HTTP Referrer header and confirmation/reset URLs themselves, which at certain points will include the account confirmation or password reset token.

NIST compliance

The library aims to support applications seeking NIST SP 800-63B AAL1 compliance. To the best of our understanding, the library provides a compliant "memorized secret authenticator", provided that the user implements rate-limiting (the shouldPreventLoginAttempt and onLoginAttempt class functions can be used for this).

The library cannot claim compliance with the guidelines overall (at a particular AAL) because: 1) the library does not handle everything covered there (for example, session management), 2) some template and route customisation could always override our compliant defaults, and 3) password reset functionality via email is problematic; in NIST terminology it is considered an "out of band authenticator" and the guidelines specifically prohibit the use of email for this.

Note that scrypt uses HMAC-SHA256 and so is compliant with NIST's hashing requirements.

More pleasing authentication URLs

By default, all route URLs from this library are quite verbose: /auth/page/simple/<login|register|..>. If you would like short URLs (e.g. /login), then you should:

  1. Use the urlParamRenderOverride function in your Yesod instance to change all PluginR "simple" _ routes plus the LoginR and LogoutR routes to their short versions. Tip: Case on the route types (e.g. LoginR) and return absolute URLs.
  2. Ensure incoming requests to the short URLs route to the extended URL handlers. Tip: Check out rewritePureWithQueries and its documentation in the wai-extra package.

Note that step 1 is for Yesod's type-safe routing while step 2 is for HTTP requests themselves. We will try to ease the developer experience of this in the future if possible!

Contributing

Get started by running nix-shell from the project root directory and then use cabal and ghci from there. Run the tests from inside ghci using the included :test command.