Pow Documentation

repository·main·Indexed 23 days ago

https://github.com/pow-auth/pow

A modular and extendable authentication and user management solution for Phoenix and Plug-based Elixir applications. It includes core authentication features and a system of extensions such as PowEmailConfirmation for email verification, PowInvitation for user invites, PowPersistentSession for rolling session cookies, and PowResetPassword for email-based password resets.

Tokens
29.1K
Snippets
60
Records
128
Agent score
81%

What's inside Pow

  1. Extend custom controllers with additional logic

    main

    Once you have custom controllers, you can inject additional business logic into the authentication flow. For example, you can check if a user's email is confirmed immediately after the authenticate_user step.

    Best Practice: If you use custom controllers, avoid relying on the default controller actions provided by Pow extensions. Instead, implement the logic explicitly within your own controllers to keep the flow predictable.

    defmodule MyAppWeb.SessionController do
      # ...
    
      def create(conn, %{"user" => user_params}) do
        conn
        |> Pow.Plug.authenticate_user(user_params)
        |> verify_confirmed()
      end
    
      defp verify_confirmed({:ok, conn}) do
        conn
        |> Pow.Plug.current_user()
        |> email_confirmed?()
        |> case do
          true ->
            conn
            |> put_flash(:info, "Welcome back!")
            |> redirect(to: ~p"/")
    
          false ->
            conn
            |> Pow.Plug.delete()
            |> put_flash(:info, "Your e-mail address has not been confirmed.")
            |> redirect(to: ~p"/login")
        end
      end
      # ...
    end
  2. How Pow configuration works

    main
    Pow uses functional configuration instead of global configuration. This means you are not tied to a single, application-wide setup. You can implement multiple, independent Pow setups within the same application (for example, one for a super admin backend and another for regular user logins). Configuration can be passed to nearly all Pow functions at either runtime or compile time.
  3. How PowPersistentSession works

    main

    The PowPersistentSession extension allows for reissuing sessions using a single-use token stored in a cookie.

    Lifecycle:

    1. A cookie is set containing a token that can be used exactly once to issue a session.
    2. Both the cookie and the token expire after 30 days.
    3. Once a session is issued using the token, a new cookie and token are set, which expire after another 30 days. This creates a rolling 30-day window for the user to remain logged in.
  4. Understand user enumeration protection in PowResetPassword

    main

    By default, PowResetPassword protects against user enumeration attacks. When a user requests a password reset, the system always displays the generic PowResetPassword.Phoenix.Messages.maybe_email_has_been_sent/1 message, regardless of whether the email exists in the database.

    If you need to disable this protection (e.g., for debugging or specific UX requirements), set pow_prevent_user_enumeration: false in conn.private. In this case, the form will instead show the PowResetPassword.Phoenix.Messages.user_not_found/1 message if the email is not recognized.

  5. How PowInvitation works

    main

    PowInvitation sets up a system where users can invite others to join.

    • Email Delivery: If the user schema has an :email field, an email containing the invitation link is sent. Otherwise, the invitation link is displayed on a page.
    • User Creation: Invited users are persisted in the database without a password. The system validates the user ID during invitation, but uses your schema's changeset/2 when the user eventually accepts the invitation.
    • User Enumeration Protection: To prevent attackers from discovering if an email is already registered, the system reports a successful invitation even if a unique constraint error on :email occurs. In this case, no email is sent. To disable this behavior and show errors instead, set pow_prevent_user_enumeration: false in conn.private.
    • Email Changes: If an invited user changes their email during the acceptance process, the PowEmailConfirmation extension can be used to require verification of the new address.
  6. Understand Pow's session management lifecycle

    main

    Pow manages sessions using a UUID token that retrieves credentials via a GenServer. The lifecycle follows these rules:

    • Storage: Credentials are stored in a key-value cache with a TTL (Time To Live) of 30 minutes.
    • Renewal: The credentials and session are automatically renewed after 15 minutes of activity.
    • User Updates: Credentials and sessions are also renewed whenever a user updates their information.
  7. Use Pow.Plug.Session and Cache Stores

    main

    The Pow.Plug.Session module enables session-based authorization. It uses a cache store to manage user structs via a unique token.

    • Development/Test: Pow.Store.Backend.EtsCache is used by default.
    • Production: It is recommended to use a distributed, persistent store like Pow.Store.Backend.MnesiaCache.

    To use MnesiaCache, add it to your application's supervisor and set the :cache_store_backend in your configuration.

    # In application.ex
    defmodule MyApp.Application do
      use Application
    
      def start(_type, _args) do
        children = [
          MyApp.Repo,
          MyAppWeb.Endpoint,
          Pow.Store.Backend.MnesiaCache
        ]
    
        opts = [strategy: :one_for_one, name: MyAppWeb.Supervisor]
        Supervisor.start_link(children, opts)
      end
    end
    
    # In config/config.exs
    config :my_app, :pow,
      cache_store_backend: Pow.Store.Backend.MnesiaCache
  8. Extend Pow with additional features

    main

    Pow provides a core set of features (Session management and user registration) that can be extended using specialized libraries. Common extensions include:

    • E-mail confirmation
    • Reset password
    • Long-term session (remember me)
    • Multi-provider support (e.g., Twitter, GitHub)
  9. Understand Pow's modular architecture

    main
    Pow is designed with an explicit API for its Plug, Ecto, and Phoenix modules. This modularity allows you to remove or modify any part of the system. Each main category module has a clear separation of responsibilities, enabling you to micro-adjust the authentication flow as needed.
  10. Implement multitenancy via Process Dictionary

    main

    If your application uses the process dictionary approach (e.g., setting a tenant ID in your Repo module), you do not need to pass :repo_opts to Pow. Instead, ensure your Repo is updated with the tenant ID in a plug or controller before Pow operations occur.

    defmodule MyAppWeb.SetTenantPlug do
      def init(opts), do: opts
    
      def call(conn, _opts_) do
        MyApp.Repo.put_org_id(conn.private[:tenant_org_id])
    
        conn
      end
    end
  11. Mitigate timing and user enumeration attacks

    main

    Pow includes built-in protections against common web attacks:

    Timing Attacks

    • If a user is not found or the :password_hash is nil, Pow uses a blank password to ensure consistent response times.
    • A UUID is always generated during the reset password flow.
    • Tokens used in Pow.Plug.Session, PowPersistentSession.Plug.Cookie, PowResetPassword.Plug, PowEmailConfirmation.Plug, and PowInvitation.Plug are signed for public consumption and verified before lookup.

    User Enumeration

    To prevent attackers from discovering valid user emails, Pow returns generic messages for failed actions:

    • Authentication failure: Returns The provided login details did not work. Please verify your credentials, and try again.
    • Password reset: For non-existent emails, returns If an account for the provided email exists, an email with reset instructions will be send to you. Please check your inbox.
    • User invitation: For already taken emails, returns An e-mail with invitation link has been sent.

    Note on PowEmailConfirmation: Enabling this extension adds extra protection by redirecting users to confirm their email if they attempt to register with an existing email, and requiring confirmation for email updates.

    Disabling Enumeration Protection

    If you need to disable these generic messages, you can set pow_prevent_user_enumeration: false in the conn.private storage.

  12. Understand the Pow module groups

    main

    Pow is organized into three main module groups that can be used together or independently:

    1. Pow.Plug: Handles the plug connection. It manages authentication, user creation, updates, deletions, and automatic session generation/renewal. Configuration is assigned to conn.private[:pow_config].
    2. Pow.Ecto: Manages the Ecto-based user schema and context. By default, it uses Pow.Ecto.Context for database lookups, but you can specify a custom context using the :users_context configuration key.
    3. Pow.Phoenix: Provides Phoenix controllers and templates. It requires setting up a session plug in endpoint.ex and adding routes in router.ex. While templates are compiled into Pow by default, you can generate your own using mix pow.phoenix.gen.templates for customization.