Guardian Documentation

repository·master·Indexed 26 days ago

https://github.com/ueberauth/guardian

A token-based authentication library for Elixir applications. Guardian supports JSON Web Tokens (JWT) out of the box and is compatible with Plug/Phoenix as well as non-web contexts like TCP/UDP protocols. It provides tools for encoding, decoding, revoking, and refreshing tokens, along with support for custom plug pipelines, permission encoding via Guardian.Permissions, and token tracking via GuardianDb.

Tokens
16.3K
Snippets
36
Records
91
Agent score
85%

What's inside Guardian

  1. Understand Guardian Pipelines

    master

    A Guardian Pipeline is a composable way to manage authentication using Plug. It provides two primary components to the Plug.Conn struct, making them available to all downstream plugs:

    1. Implementation Module: Your custom Guardian module.
    2. Error Handler: A module responsible for handling unauthenticated requests.

    By injecting these into the conn struct at the start of a plug chain, you can easily swap them out downstream for different parts of your application (e.g., switching from a JSON error handler to an HTML error handler).

  2. Understand Guardian's core functionality

    master

    Guardian is an Elixir authentication toolkit providing token-based authentication. It focuses on two core responsibilities:

    1. Creating Tokens
    2. Verifying Tokens

    Note that Guardian does not handle the initial 'challenge phase' (the first verification of a user/resource). You should use a methodology like Ueberauth to handle the initial authentication before using Guardian for token management.

  3. Understand the default JWT payload in Guardian

    master

    Guardian uses JSON Web Tokens (JWT) as its default token implementation. A standard JWT payload produced by Guardian includes the following claims:

    • iss (Issuer): The principal that issued the JWT. This is typically configured in your application config (e.g., config :idp, Idp.Auth.Guardian, issuer: "idp").
    • sub (Subject): Identifies the subject of the JWT (e.g., User:123).
    • aud (Audience): The intended recipients. Defaults to the same value as iss.
    • exp (Expiration Time): Unix timestamp after which the token is invalid. Defaults to 4 weeks.
    • iat (Issued at): Unix timestamp of when the JWT was issued.
    • nbf (Not before): Unix timestamp before which the token is not accepted. Defaults to 1 ms before iat.
    • typ (Token Type): The type of token. Defaults to "access". Note that this is distinct from the typ field in the JWT header, which is always "JWT".
    • jti (JWT ID): A unique identifier for the token.
  4. Understand Guardian token concepts

    master

    Guardian uses tokens as the currency for authentication credentials. A valid token must be:

    1. Tamper proof: It must be signed or encrypted such that it is verifiable by your application.
    2. Payload-bearing: It must include claims, which should be a map using string keys.

    By default, Guardian uses JWT tokens (JSON Web Tokens), which are widely supported and can use either hashed or certificate-based signing algorithms. However, you can implement other behaviors such as revocation, database tracking, or different token standards.

  5. Implement a Guardian Plug Pipeline

    master

    To use Guardian with Plugs in 1.0, you must define a pipeline module using Guardian.Plug.Pipeline. This pipeline handles implementation selection, error handling, and the plug sequence.

    defmodule MyApp.Guardian.AuthPipeline do
      @claims %{typ: "access"}
    
      use Guardian.Plug.Pipeline, otp_app: :my_app,
                                  module: MyApp.Guardian,
                                  error_handler: MyApp.Guardian.AuthErrorHandler
    
      plug Guardian.Plug.VerifySession, claims: @claims
      plug Guardian.Plug.VerifyHeader, claims: @claims, realm: "Bearer"
      plug Guardian.Plug.EnsureAuthenticated
      plug Guardian.Plug.LoadResource, ensure: true
    end
  6. Configure a manual Guardian Pipeline in Phoenix

    master

    To build a manual pipeline in a Phoenix router, you must first initialize the pipeline with your implementation module and error handler, then add plugs to find, verify, and load the user.

    Steps:

    1. Use Guardian.Plug.Pipeline to set the module and error_handler.
    2. Use Guardian.Plug.VerifySession or Guardian.Plug.VerifyHeader to locate and validate tokens. You can pass a claims map to restrict validation to specific token types (e.g., claims: %{"typ" => "access"}).
    3. Use Guardian.Plug.EnsureAuthenticated to enforce authentication (returns an error via the error handler if no valid token is found).
    4. Use Guardian.Plug.LoadResource to load the resource associated with the token into the connection.
    pipeline :auth do
      plug Guardian.Plug.Pipeline, module: AuthMe.UserManager.Guardian,
                                   error_handler: AuthMe.UserManager.ErrorHandlers.JSON
    
      plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"}
      plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"}
      plug Guardian.Plug.EnsureAuthenticated
      plug Guardian.Plug.LoadResource
    end
    
    scope "/", AuthMeWeb do
      pipe_through [:auth]
    end
  7. Best practices for Guardian authentication pipelines

    master

    For optimal flexibility, it is recommended to use at least two distinct Phoenix pipelines:

    1. A 'maybe_auth' pipeline: This pipeline finds and verifies the token (via VerifySession or VerifyHeader) but does not enforce authentication. Use allow_blank: true with Guardian.Plug.LoadResource so that if no user is logged in, the request continues without error.
    2. An 'ensure_auth' pipeline: This pipeline uses Guardian.Plug.EnsureAuthenticated to strictly require a valid session/token.

    This approach allows you to have routes that are accessible to everyone (but identify users if they are logged in) and routes that are strictly protected.

    pipeline :maybe_auth do
      plug Guardian.Plug.Pipeline, module: AuthMe.UserManager.Guardian,
                                   error_handler: AuthMe.UserManager.ErrorHandlers.JSON
      plug Guardian.Plug.VerifySession, %{"typ" => "access"}
      plug Guardian.Plug.VerifyHeader, %{"typ" => "access"}
      # if there isn't anyone logged in we don't want to return an error. Use allow_blank
      plug Guardian.Plug.LoadResource, allow_blank: true
    end
    
    pipeline :ensure_auth do
      plug Guardian.Plug.EnsureAuthenticated
    end
  8. Implement and configure custom token backends

    master

    Guardian uses an adapter pattern. You can implement the Guardian.Token behaviour to create a custom backend. You specify this backend using the token_module option, either directly in the use statement or via your application's configuration.

    # Option 1: Specify token_module in the use call
    ```elixir
    defmodule MyApp.TokenModuleCustom do
      use Guardian, otp_app: :my_app,
          token_module: MyApp.CustomTokenBackend
    
      # ...
    end

    Option 2: Specify token_module via Mix configuration

    defmodule MyApp.TokenModuleCustom do
      use Guardian, otp_app: :my_app
    
      # ...
    end
    use Mix.Config
    
    config :my_app, MyApp.TokenModuleCustom,
      token_module: MyApp.CustomTokenBackend
  9. Change the Guardian error handler downstream

    master

    Because the pipeline configuration is injected into the conn struct, you can override the error_handler at any point downstream in your plug chain by calling Guardian.Plug.Pipeline again with a different error_handler option.

      pipeline :auth do
        plug AuthMe.UserManager.Pipeline
      end
    
      pipeline :auth_html_errors do
        plug Guardian.Plug.Pipeline, error_handler: AuthMe.UserManager.ErrorHandlers.HTML
      end
    
      pipeline :auth_json_errors do
        plug Guardian.Plug.Pipeline, error_handler: AuthMe.UserManager.ErrorHandlers.JSON
      end
    
      scope "/api" do
        pipe_through [:auth, :auth_json_errors]
        # ...
      end
    
      scope "/www" do
        pipe_through [:auth, :auth_html_errors]
        # ...
      end