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
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").
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).
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).
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).
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.
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) fromMyApp.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.
- Allow more things to be customized without having to implement a custom handler
- Move default values from hard-coded to
config.exs
-based configuration