PowAssent
Use Google, Github, Twitter, Facebook, or add your custom strategy for authorization to your Pow enabled Phoenix app.
Features
- Collects required user id field from the user if the user id is missing from the provider
Multiple providers can be used for accounts
- When removing authentication, user is validated for password or alternative provider
- You can add your custom strategy with ease
Includes the following base strategies:
Includes the following provider strategies:
Installation
Add PowAssent to your list of dependencies in mix.exs
:
def deps do
[
# ...
{:pow_assent, "~> 0.1.0"},
# Optional, but recommended for SSL validation with :httpc adapter
{:certifi, "~> 2.4"},
{:ssl_verify_fun, "~> 1.1"},
# ...
]
end
Run mix deps.get
to install it.
Getting started
Set up Pow
It’s required to set up Pow first. You can run these quick setup instructions if Pow hasn’t already been set up.
Set up PowAssent
Install the necessary files:
mix pow_assent.install
This will add the following files to your app:
LIB_PATH/user_identities/user_identity.ex
PRIV_PATH/repo/migrations/TIMESTAMP_create_user_identities.ex
Update LIB_PATH/users/user.ex
:
defmodule MyApp.Users.User do
use Ecto.Schema
use Pow.Ecto.Schema
use PowAssent.Ecto.Schema
schema "users" do
has_many :user_identities,
MyApp.UserIdentities.UserIdentity,
on_delete: :delete_all
pow_user_fields()
# ...
timestamps()
end
# ..
end
Set up router.ex
:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
use Pow.Phoenix.Router
use PowAssent.Phoenix.Router
# ...
scope "/" do
pipe_through [:browser]
pow_routes()
pow_assent_routes()
end
# ...
end
The following routes will now be available in your app:
pow_assent_authorization_path GET /auth/:provider/new PowAssent.Phoenix.AuthorizationController :new
pow_assent_authorization_path DELETE /auth/:provider PowAssent.Phoenix.AuthorizationController :delete
pow_assent_authorization_path GET /auth/:provider/callback PowAssent.Phoenix.AuthorizationController :callback
pow_assent_registration_path GET /auth/:provider/add-user-id PowAssent.Phoenix.RegistrationController :add_user_id
pow_assent_registration_path POST /auth/:provider/create PowAssent.Phoenix.RegistrationController :create
Remember to run the new migrations with:
mix ecto.setup
Modified Pow templates
If you’re modifying the Pow templates, then you have to generate the PowAssent template too:
mix pow_assent.phoenix.gen.templates
Otherwise, Pow will raise an error about missing template when the user id field template is shown.
Provider links
You can use PowAssent.Phoenix.ViewHelpers.provider_links/1
to add provider links to your template files:
<h1>Registration</h1>
<%= for link <- PowAssent.Phoenix.ViewHelpers.provider_links(@conn),
do: content_tag(:span, link) %>
It’ll automatically link with “Sign in with PROVIDER” or “Remove PROVIDER authentication” depending on if there’s an authenticated user in the connection.
Setting up a provider
PowAssent has multiple strategies that you can use. Let’s go through how to set up the Github strategy.
First, register a new app on Github and add http://localhost:4000/auth/github/callback
as the callback URL. Then add the following to config/config.exs
and add the client id and client secret:
config :my_app, :pow_assent,
providers: [
github: [
client_id: "REPLACE_WITH_CLIENT_ID",
client_secret: "REPLACE_WITH_CLIENT_SECRET",
strategy: PowAssent.Strategy.Github
]
]
Now start (or restart) your Phoenix app, and visit http://localhost:4000/auth/github/new
.
Custom provider
You can add your own custom strategy.
Here’s an example of an OAuth 2.0 implementation using PowAssent.Strategy.OAuth2.Base
:
defmodule TestProvider do
use PowAssent.Strategy.OAuth2.Base
def default_config(_config) do
[
site: "http://localhost:4000/",
authorize_url: "http://localhost:4000/oauth/authorize",
token_url: "http://localhost:4000/oauth/access_token",
user_url: "/user",
authorization_params: [scope: "email profile"]
]
end
def normalize(_config, user) do
%{
"uid" => user["sub"],
"name" => user["name"],
"email" => user["email"]
}
end
end
You can also use PowAssent.Strategy
:
defmodule TestProvider do
use PowAssent.Strategy
@spec authorize_url(Keyword.t(), Plug.Conn.t()) :: {:ok, %{conn: Plug.Conn.t(), url: binary()}} | {:error, any()}
def authorize_url(config, conn) do
# Generate authorization url
end
@spec callback(Keyword.t(), Plug.Conn.t(), map()) :: {:ok, %{conn: Plug.Conn.t(), user: map()}} | {:error, any()}
def callback(config, conn, params) do
# Handle callback response
end
end
I18n
The template can be generated and modified to use your Gettext module with mix pow.extension.phoenix.gen.templates --extension PowAssent
For flash messages, you should add them to your Pow.Phoenix.Messages
module the same way as all Pow extension flash messages:
defmodule MyAppWeb.Pow.Messages do
use Pow.Phoenix.Messages
use Pow.Extension.Phoenix.Messages,
extensions: [PowAssent]
import MyAppWeb.Gettext
# ...
def pow_assent_signed_in(_conn, provider), do: gettext("You've been signed in with %{provider}.", provider: provider)
end
You can find all messages in PowAssent.Phoenix.Messages
.
Populate fields
To populate fields in your user struct that are fetched from the provider, you only need to cast them in user_identity_changeset/4
method like so:
defmodule MyApp.Users.User do
# ...
def user_identity_changeset(user_or_changeset, user_identity, attrs, user_id_attrs) do
user_or_changeset
|> Ecto.Changeset.cast(attrs, [:custom_field_1, :customer_field_2])
|> pow_assent_user_identity_changeset(user, user_identity, attrs, user_id_attrs)
end
end
The fields available can be found in the normalize/2
method of the strategy.
HTTP Adapter
By default Erlangs built-in :httpc
is used for requests. SSL verification is automatically enabled when :certifi
and :ssl_verify_fun
packages are available. :httpc
only supports HTTP/1.1.
If you would like HTTP/2 support, you should consider adding Mint
to your project.
Update mix.exs
:
def deps do
[
# ...
{:mint, "~> 0.1.0"},
{:castore, "~> 0.1.0"}, # Required for SSL validation
# ...
]
end
Update the PowAssent configuration with:
config :my_app, :pow_assent,
http_adapter: PowAssent.HTTPAdapter.Mint
Security concerns
All sessions created through PowAssent provider authentication are temporary. However, it’s a good idea to do some housekeeping in your app and make sure that you have the level of security as warranted by the scope of your app. That may include requiring users to re-authenticate before viewing or editing their user details.
LICENSE
(The MIT License)
Copyright (c) 2018-2019 Dan Schultzer & the Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.