A collection of Plug middleware for web applications
Elixir
Switch branches/tags
Clone or download
YellowApple Merge pull request #6 from sugar-framework/ensure-auth-redirect
Add support for `:return_to` option on `Sugar.Plugs.EnsureAuthenticated`
Latest commit 8cc7cbe May 18, 2016
Permalink
Failed to load latest commit information.
lib/sugar/plugs Add clause for nil :return_to May 18, 2016
test Initial commit Feb 9, 2014
.gitignore update deps Oct 21, 2015
LICENSE Initial commit Feb 9, 2014
README.md README update May 18, 2016
mix.exs Version bump May 18, 2016
mix.lock Update mix.lock for posterity Feb 17, 2016

README.md

Plugs

Sugar.Plugs.HotCodeReload

Used to add hot code reloading to a project, preventing the need to stop, recompile, and start your application to see your changes.

Use:

defmodule MyRouter do
  use Plug.Router

  plug Sugar.Plugs.HotCodeReload

  plug :match
  plug :dispatch

  # Rest of router definition
  ...
end

Sugar.Plugs.EnsureAuthenticated

Used to ensure that a user is currently logged on before proceeding through a Plug pipeline. This is designed first and foremost for Sugar, but it can hypothetically work with any plug-based app (emphasis on "hypothetically").

Setup

In your router.ex:

defmodule MyApp.Router do
  use Sugar.Router

  plug :put_secret_key_base
  plug Plug.Session,
    store: :cookie,
    key: "_my_app_session",
    encryption_salt: "encryption salt",
    signing_salt: "signing salt",
    key_length: 64

  def put_secret_key_base(conn, _) do
    base = "some 64 character random string"
    put_in conn.secret_key_base, base
  end

  # ...

end

You'll also want a controller to present a login page and authenticate a user (and possibly act on conn.params["return_to"]; see below).

Use

Add this to a controller you want authentication on:

defmodule MyApp.Controllers.Foo do
  use Sugar.Controller

  plug Sugar.Plugs.EnsureAuthenticated

  # ...

end

If your controller has some actions that don't need authentication, then you can use plug Sugar.Plugs.EnsureAuthenticated, except: [:foo, :bar]. Likewise, if your controller's actions mostly don't require authentication, you can use plug Sugar.Plugs.EnsureAuthenticated, only: [:baz, :bat]. Only specify one or the other (:only should take precedence in the event that both are used).

Configuration

Sugar.Plugs.EnsureAuthenticated defaults to expect the following:

  • You have an Ecto repo at MyApp.Repos.Main
  • You have a User model at MyApp.Models.User
  • Users should be directed to your app's /login route if they're not currently logged in

The first two can be overridden by calling the plug as plug Sugar.Plugs.EnsureAuthenticated, repo: My.Custom.Repo, model: My.Custom.Model (should be self-explanatory). The third can't be changed quite yet (TODO: add that configuration option), at least not without implementing a custom handler (see below).

:return_to (:origin or an explicit URL)

One can provide a :return_to option, which adds a query parameter (named return_to by default) to signal to a login page where the user should be redirected to after logging in. The most automatic approach would be to set this to :origin, like so:

plug Sugar.Plugs.EnsureAuthenticated, return_to: :origin

The above reads the connection's original path and sets conn.params["return_to"] automatically.

Custom Handler

By default, Sugar.Plugs.EnsureAuthenticated uses a built-in handler to check for authentication. This handler is pretty minimal in terms of what it does:

  • Checks to see if the controller action being run actually requires authentication (by checking conn.private.action for an action name); if not, then continues without doing anything else.
  • Checks to see if there's a "user_id" session key (if not, then redirects to "/login")
  • Fetches a %MyApp.Models.User{} (or the struct for whatever model you've configured) from MyApp.Repos.Main (or whatever repo you've configured); if it can't find a user, then redirects to "/login"
  • Assigns the found user to the connection's :current_user (for use by other plugs, namely Canary), then continues on the pipeline

If you need to implement custom functionality (for example, if you're not using Sugar-compatible controllers, or if you're not using Ecto models), you can call plug Sugar.Plugs.EnsureAuthenticated, handler: {MyApp.Auth.Handler, :verify} (where MyApp.Auth.Handler is a module and :verify is the name of a function in that module; you could also do handler: MyApp.Auth.Handler, in which case the :verify function will be called).

In order to work, the handler must accept both a %Plug.Conn{} (i.e. a conn variable, like in a typical plug) and a Map containing the options passed to EnsureAuthenticated. From here, you can implement whatever checks you need to perform.

To Do

  • Allow more things to be customized without having to implement a custom handler
  • Move default values from hard-coded to config.exs-based configuration