Phoenix 1.5 authentication with POW

Phoenix, Elixir, TailwindCSS, AlpineJS, LiveView - PETAL Stack

Auth

Auth with POW

POW and LiveViews

Auth with auth.gen

Auth with PubSub

Auth with Email

General Auth Principles https://nithinbekal.com/posts/phoenix-authentication/

Mail in Test Env

Intro

Pow has the advantage that it updates security patches – since its a well maintained library.

This repo can be found at: https://github.com/btihen/phoenix_1_5_pow_auth_config

Get the latest version from: https://hex.pm/packages/pow

{:pow, "~> 1.0"}

Install the dependency:

mix deps.get

Install POW:

mix pow.install

Lets verify all is good with the install:

mix deps.compile
mix help | grep pow

Now hopefully you see some new pow commands

Configure Pow

There are three files you’ll need to configure first before you can use Pow.

First, append this to config/config.exs:

config :fare, :pow,
  user: Fare.Users.User,
  repo: Fare.Repo

Next, add Pow.Plug.Session plug to lib/fare_web/endpoint.ex after plug Plug.Session:

defmodule FareWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :fare   # add this line HERE!

  # ...

  plug Plug.Session, @session_options
  plug Pow.Plug.Session, otp_app: :fare  # add this line HERE!
  plug FareWeb.Router
end

Last, update lib/fare_web/router.ex with the Pow routes - this first time we need to do a little extra config:

  pipeline :api do
    plug :accepts, ["json"]
  end
  scope "/", MyAppWeb do
    pipe_through [:browser, :protected]

    # Add your protected routes here
  end

  scope "/", MyAppWeb do
    pipe_through :browser

    live "/", PageLive, :index
  end

Should now look like:

  pipeline :api do
    plug :accepts, ["json"]
  end

  pipeline :protected do
    plug Pow.Plug.RequiredAuthentication,
          error_handler: Pow.Phoenix.PlugErrorHandler
  end

  scope "/" do
    pipe_through :browser

    pow_routes()
  end

  scope "/", MyAppWeb do
    pipe_through [:browser, :protected]

    # Add your protected routes here
    resources "/tasks", TaskController
  end

  scope "/", MyAppWeb do
    pipe_through :browser

    live "/", PageLive, :index
  end

Now lets check the routes - that all is well configured:

mix phx.routes | grep pow

Hopefully you see some new pow routes.

Now we can migrate to create our users table:

mix ecto.migrate

Now if we start phoenix:

mix phx.server

and open phoenix: localhost:4000

https://experimentingwithcode.com/phoenix-authentication-with-pow-part-1/

Notice there is no menu option to login - lets build a simple signup/signin/logout link.

In root.html.eex find <li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li> and we will replace it with:

            <%= if Pow.Plug.current_user(@conn) do %>
              <li>
                <%= link "#{@current_user.email}", to: Routes.pow_registration_path(@conn, :edit) %>
              </li>
              <li>
                <%= link "Sign-out", to: Routes.pow_session_path(@conn, :delete), method: :delete %>
              </li>
            <% else %>
              <li><%= link "Sign-in", to: Routes.pow_session_path(@conn, :new) %></li>
              <li><%= link "Register", to: Routes.pow_registration_path(@conn, :new) %></li>
            <% end %>

Now reload and try it out:

  1. you should be able to register
  2. sign-out
  3. sign in

Customizable Login pages

Generate the pages to customize with:

mix pow.phoenix.gen.templates

now be sure to change the config in config/confix.ex from:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: Fare.Repo

to:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb

Without updating the config the newly generated pages won’t be used!

The new templates to modify are found in:

  • lib/fare_web/templates/pow/registration &
  • lib/fare_web/templates/pow/session

Now make a small change to the pages to ensure all works.

Create a restricted user page

https://www.youtube.com/watch?v=hnD0Z0LGMIk https://experimentingwithcode.com/phoenix-authentication-with-pow-part-1/

Create a normal html page first:

mix phx.gen.html Tasks Task tasks description:string completed:boolean

BE SURE TO PUT the new route in the protected area of the routes file:

# lib/fare_web/router.ex
  scope "/", MyAppWeb do
    pipe_through [:browser, :protected]

    # Add your protected routes here
    resources "/tasks", TaskController
  end

Now of course run the migration:

mix ecto.migrate

now /tasks should only be availble to signed in users. Be sure you are logged out and cannot get to the /tasks route (and infact are redirected to sign-in). And once logged in the page works as expected.

Extensions

Persistent Login Sessions (Remember me)

https://experimentingwithcode.com/phoenix-authentication-with-pow-part-2/

Currently every time the user closes the browser they are logged out - the login cookie doesn’t persist - most users would like the option to change this - with a remember me option.

in config/config.exs change the :pow config to look like:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb,
  # add the following two lines
  extensions: [PowPersistentSession],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks

in /lib/my_app_web/endpoint.ex we need to add the persistent cookie setting immediately after the Pow.Plug.Session plug and before the routing MyAppWeb.Router plug – now the end of the endpoint file should look like:

  # enable Pow session based authentication
  plug Pow.Plug.Session, otp_app: :warehouse
  # enable Pow persistent sessions
  plug PowPersistentSession.Plug.Cookie
  # routing plug
  plug MyAppWeb.Router
end

just above the login button on the sign-in page add the following check-box:

# lib/fare_web/templates/pow/session/new.html.eex
  <%= label f, :persistent_session, "Remember me" %>
  <%= checkbox f, :persistent_session %>

  <div>
    <%= submit "Sign in" %>
  </div>

restart Phoenix with: mix phx.server and now you should be able to close your browser and re-open the link and stay logged in if the remember-me is clicked.

After Logout - go to Landing Page (After Hook Routing)

https://experimentingwithcode.com/phoenix-authentication-with-pow-part-2/

One little annoying thing is that when we logout we go to the sign-in page instead of the landing page. We can fix that by adding a call_back_route - you can find all the callback routes at: https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/routes.ex - we will use: the after_sign_out_path callback.

To do this we will make a new pow.routes file:

touch lib/warehouse_web/pow/routes.ex

Add the following contents:

cat << EOF> lib/my_app_web/pow/routes.ex
defmodule MyAppWeb.Pow.Routes do
  use Pow.Phoenix.Routes
  alias MyAppWeb.Router.Helpers, as: Routes

  def after_sign_out_path(conn), do: Routes.page_path(conn, :index)
end
EOF

Now finally update config/confix.exs by adding routes_backend: MyAppWeb.Pow.Routes to the :pow config so now it would look like:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb,
  extensions: [PowPersistentSession],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
  routes_backend: MyAppWeb.Pow.Routes    # add this line

Assuming all works we will snapshot now!

git add .
git commit -m "on logout go to landing page"

Password Reset and Email Confirmation

https://github.com/pow-auth/pow_assent https://www.youtube.com/watch?v=hnD0Z0LGMIk https://experimentingwithcode.com/phoenix-authentication-with-pow-part-2/

The following are the possible extensions:

  • PowResetPassword
  • PowEmailConfirmation
  • PowPersistentSession
  • PowInvitation

Let’s start with password reset and email confirmation.

First we need to do a migration:

mix pow.extension.ecto.gen.migrations --extension PowResetPassword --extension PowEmailConfirmation

now update the phoenix config config/config.ex again from:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb

to:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb,
  extensions: [PowPersistentSession, PowResetPassword, PowEmailConfirmation],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks

now update the LIB_PATH/users/user.ex file from:

defmodule Fare.Users.User do
  use Ecto.Schema
  use Pow.Ecto.Schema

  schema "users" do
    pow_user_fields()

    timestamps()
  end
end

to:

defmodule MyApp.Users.User do
  use Ecto.Schema
  use Pow.Ecto.Schema
  use Pow.Extension.Ecto.Schema,
      extensions: [PowResetPassword, PowEmailConfirmation]

  schema "users" do
    pow_user_fields()

    timestamps()
  end

  def changeset(user_or_changeset, attrs) do
    user_or_changeset
    |> pow_changeset(attrs)
    |> pow_extension_changeset(attrs)
  end
end

And of course the routes WEB_PATH/router.ex too - at the top of the file add: so it looks like:

defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  use Pow.Phoenix.Router
  use Pow.Extension.Phoenix.Router,
      extensions: [PowResetPassword, PowEmailConfirmation]

And in the pow routes config change from:

  scope "/" do
    pipe_through :browser

    pow_routes()
  end

to:

  scope "/" do
    pipe_through :browser

    pow_routes()
    pow_extension_routes()
  end

Now finally, we need can update any views needed by POW’s new extensions with:

mix pow.extension.phoenix.gen.templates --extension PowResetPassword --extension PowEmailConfirmation

Now we can update the sign-in page with a reset password button. We will add the following, to the end of lib/fare_web/templates/pow/session/new.html.eex:

|
<span>
<%= link "Reset Password", to: Routes.pow_reset_password_reset_password_path(@conn, :new) %>
</span>

Now lets be sure we can link to reset password view.

First we will do our migration:

mix ecto.migrate

now to to sign-in and see if the reset password link works. Cool it does, but it we try to use it - it complains it needs email back-end setup.

Email backend

https://dev.to/oliverandrich/learn-elixir-and-phoenix-add-authentication-55kl

First we will create a mailer function in: lib/my_app_web/pow/pow_mailer.ex

mkdir lib/my_app_web/pow/
touch lib/my_app_web/pow/pow_mailer.ex
cat <<EOF > lib/my_app_web/pow/pow_mailer.ex
defmodule FareWeb.Pow.Mailer do
  use Pow.Phoenix.Mailer

  require Logger

  @impl true
  def cast(%{user: user, subject: subject, text: text, html: html}) do
    # Forward Struct to logger - disable/remove when Bamboo configured
    %{to: user.email, subject: subject, text: text, html: html}
  end

  @impl true
  def process(email) do
    # log email sent
    Logger.debug("E-mail sent: #{inspect email}")
  end
end
EOF

now that we have an email template we need to tell pow about the mailer with the config: mailer_backend: MyAppWeb.Pow.Mailer in config/config.exs so change to:

# config for pow - user authentication
config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb,
  mailer_backend: MyAppWeb.Pow.Mailer,  # add this
  extensions: [PowPersistentSession, PowResetPassword, PowEmailConfirmation],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks

Now generate the POW mail templates - with:

mix pow.extension.phoenix.mailer.gen.templates --extension PowResetPassword --extension PowEmailConfirmation

Phoenix also needs to know about the mailer templates we will generate so add to lib/my_app_web.ex:

  def mailer_view do
    quote do
      use Phoenix.View, root: "lib/my_app_web/templates",
                        namespace: MyAppWeb

      use Phoenix.HTML
    end
  end

Now the final config change in config/config.ex to access our new templates:

# config for pow - user authentication
config :fare, :pow,
  user: Fare.Users.User,
  repo: Fare.Repo,
  web_module: MyAppWeb,
  web_mailer_module: MyAppWeb,          # add this to access the templates
  mailer_backend: MyAppWeb.Pow.Mailer,
  extensions: [PowPersistentSession, PowResetPassword, PowEmailConfirmation],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks

Now if we resart phoenix and test out reset link - we should see in the logs ‘a pretend sent email’ - something like:

[debug] E-mail sent: %{html: "<h3>Hi,</h3>\n<p>Please use the following link to reset your password:</p>\n<p><a href=\"http://localhost:4000/reset-password/SFMyNTY.MTJkNDliZWItZTg2My00ZDM3LTg2YzgtYzE5MDdjMDk5ODgz.kFRCfvdOSeEnupbbujdAKoaCuMXXk91qzZCUMrB43mw\">http://localhost:4000/reset-password/SFMyNTY.MTJkNDliZWItZTg2My00ZDM3LTg2YzgtYzE5MDdjMDk5ODgz.kFRCfvdOSeEnupbbujdAKoaCuMXXk91qzZCUMrB43mw</a></p>\n<p>You can disregard this email if you didn&#39;t request a password reset.</p>", subject: "Reset password link", text: "Hi,\n\nPlease use the following link to reset your password:\n\nhttp://localhost:4000/reset-password/SFMyNTY.MTJkNDliZWItZTg2My00ZDM3LTg2YzgtYzE5MDdjMDk5ODgz.kFRCfvdOSeEnupbbujdAKoaCuMXXk91qzZCUMrB43mw\n\nYou can disregard this email if you didn't request a password reset.\n", to: "[email protected]"}

copy the link out of the email in the log:

http://localhost:4000/reset-password/SFMyNTY.MTJkNDliZWItZTg2My00ZDM3LTg2YzgtYzE5MDdjMDk5ODgz.kFRCfvdOSeEnupbbujdAKoaCuMXXk91qzZCUMrB43mw

into the browser - type a new password and try to login.

Assuming all works we will snapshot now!

git add .
git commit -m "pow configured to send emails - no sender yet"

After Logout - go to Landing Page (After Hook Routing)

https://experimentingwithcode.com/phoenix-authentication-with-pow-part-2/

One little annoying thing is that when we logout we go to the sign-in page instead of the landing page. We can fix that by adding a call_back_route - you can find all the callback routes at: https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/routes.ex - we will use: the after_sign_out_path callback.

To do this we will make a new pow.routes file:

touch lib/warehouse_web/pow/routes.ex

Add the following contents:

cat << EOF> lib/my_app_web/pow/routes.ex
defmodule MyAppWeb.Pow.Routes do
  use Pow.Phoenix.Routes
  alias MyAppWeb.Router.Helpers, as: Routes

  def after_sign_out_path(conn), do: Routes.page_path(conn, :index)
end
EOF

Now finally update config/confix.exs by adding routes_backend: MyAppWeb.Pow.Routes to the :pow config so now it would look like:

config :my_app, :pow,
  user: MyApp.Users.User,
  repo: MyApp.Repo,
  web_module: MyAppWeb,
  web_mailer_module: MyAppWeb,
  mailer_backend: MyAppWeb.Pow.Mailer,
  routes_backend: MyAppWeb.Pow.Routes,  # Add this line
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
  extensions: [PowPersistentSession, PowResetPassword, PowEmailConfirmation]

Assuming all works we will snapshot now!

git add .
git commit -m "on logout go to landing page"

Configure Email BAMBOO with POW

Use Bamboo to do the mailing find the new versions at:

    {:bamboo, "~> 2.1"}

get the new dependency:

mix deps.get

In the Test Config we need to configure bamboo with:

# config/test.exs
config :my_app, MyApp.Mailer,
  adapter: Bamboo.TestAdapter

In the Dev Config lets setup the in-memory email config

# config/dev.exs
config :fare, FareWeb.Pow.Mailer,
  adapter: Bamboo.LocalAdapter

When using Bamboo.LocalAdapter in dev mode we can view the email (without digging through the log file) using Bamboo.EmailPreviewPlug - we set this up with:

if Mix.env == :dev do
  forward "/sent_emails", Bamboo.EmailPreviewPlug
end

Now let’s setup our mailer to use Bamboo - we will edit:

# lib/my_app_web/pow/pow_mailer.ex
defmodule MyAppWeb.Pow.Mailer do
  use Pow.Phoenix.Mailer

  # ADDED to use Bamboo
  use Bamboo.Mailer, otp_app: :fare  # Bamboo needs to know the supervisor
  import Bamboo.Email                # provides access to the Bamboo's methods

  require Logger

  @impl true
  def cast(%{user: user, subject: subject, text: text, html: html}) do
    # ADDED when Bamboo is configured
    new_email(
      to: user.email,
      from: "[email protected]",
      subject: subject,
      html_body: html,
      text_body: text
    )
  end

  @impl true
  def process(email) do
    # ADDED when Bamboo is configured
    deliver_now(email)

    # check email functionality and contents
    Logger.debug("E-mail sent: #{inspect email}")
  end
end

Let’s register a new account (or reset a password). Now we can see the sent email at: http://localhost/sent_emails (in dev mode)

Bamboo Adapters - SMTP Config (Production Ideas)

Available adapters are listed at: https://hexdocs.pm/bamboo/readme.html#available-adapters

Given the number of adapters - here we will configure just SMTP (and learn to trap it while testing)

https://www.kabisa.nl/tech/real-world-phoenix-lets-send-some-emails/ https://dev.to/oliverandrich/learn-elixir-and-phoenix-add-authentication-55kl

Use Bamboo to do the mailing find the new versions at:

    {:bamboo, "~> 2.1"},
    {:bamboo_smtp, "~> 2.1"}

Now install and setup up: https://github.com/mailhog/ (on a MacOS) simply install with:

brew install mailhog

and run mailhog with:

mailhog

or if you want mailhog running all the time in the background you can type:

  brew services start mailhog

Or you can use: or https://mailcatcher.me/

These serivices - listen on localhost:1025 and you can view the email at: http://localhost:8025

now configure the mail service (in config/dev.exs) to use Mailhog or Mailcather with Phoenix by adding:

# config/dev.exs
config :my_app, MyAppWeb.Pow.Mailer,
  adapter: Bamboo.SMTPAdapter,
  server: "localhost",
  port: 1025

In production it might look like:

# config/config.exs
config :my_app, MyApp.Mailer,
  adapter: Bamboo.SMTPAdapter,
  server: "smtp.domain",
  hostname: "your.domain",
  port: 1025,
  username: "[email protected]", # or {:system, "SMTP_USERNAME"}
  password: "pa55word", # or {:system, "SMTP_PASSWORD"}
  tls: :if_available, # can be `:always` or `:never`
  allowed_tls_versions: [:"tlsv1", :"tlsv1.1", :"tlsv1.2"], # or {:system, "ALLOWED_TLS_VERSIONS"} w/ comma seprated values (e.g. "tlsv1.1,tlsv1.2")
  ssl: false, # can be `true`
  retries: 1,
  no_mx_lookups: false, # can be `true`
  auth: :if_available # can be `:always`. If your smtp relay requires authentication set it to `:always`.

Now you will need to start your mail-trap (in a separate cli terminal):

mailhog
# or `mailcather`

Now when you register a new account or change a password you can see the email at:

http://localhost:8025

Customizing - POW Flash messages

https://experimentingwithcode.com/phoenix-authentication-with-pow-part-2/

Create a new module for our messages.

touch lib/my_app_web/pow/messages.ex
cat <<EOF>>lib/my_app_web/pow/messages.ex
defmodule MyAppWeb.Pow.Messages do
  use Pow.Phoenix.Messages
  use Pow.Extension.Phoenix.Messages,  # add extensions in use
    extensions: [PowResetPassword, PowEmailConfirmation]

  import MyAppWeb.Gettext

  # PowMessages - functions defined:
  # https://hexdocs.pm/pow/Pow.Phoenix.Messages.html#summary
  # https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/messages.ex
  def signed_in(_conn), do: gettext("Welcome back.")
  def signed_out(_conn), do: gettext("Signed out successfullly.")
  def user_not_authenticated(_conn), do: gettext("You need to sign in to see this page.")

  # PowResetPassword - functions defined:
  # https://github.com/danschultzer/pow/blob/master/lib/extensions/reset_password/phoenix/messages.ex
  def invalid_token(_conn), do: "The reset token has expired."
  def password_has_been_reset(_conn), do: "The password has been updated."
  def email_has_been_sent(_conn), do: "An email with reset instructions has been sent to you."

  # PowEmailConfirmation - functions defined:
  # https://github.com/danschultzer/pow/blob/master/lib/extensions/email_confirmation/phoenix/messages.ex
  def email_has_been_confirmed(_conn), do: "The email address has been confirmed."
  def email_confirmation_failed(_conn), do: "The email address couldn't be confirmed."
  def email_confirmation_required(_conn), do: "You need to confirm your e-mail with the link e-mailed to you."
end
EOF

To be able to use this module we need to tell our config about it - so we update config/config.exs with messages_backend: FareWeb.Pow.Messages - so now it looks like:

config :fare, :pow,
  user: Fare.Users.User,
  repo: Fare.Repo,
  web_module: FareWeb,
  web_mailer_module: FareWeb,
  mailer_backend: Fare.Pow.Mailer,
  routes_backend: FareWeb.Pow.Routes,
  messages_backend: FareWeb.Pow.Messages,  # Add this line
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
  extensions: [PowPersistentSession, PowResetPassword, PowEmailConfirmation]

Now you should see your custom messages!

let’s snapshot this:

git add .
git commit -m "allow POW to send custom / i18n messages"

Configure to allow 3rd Parties - Google, Apple, Github, etc.

https://github.com/pow-auth/pow_assent https://www.youtube.com/watch?v=hnD0Z0LGMIk

First add to the mix file:

    # third party auth
    {:pow_assent, "~> 0.4.10"},
    # recommended for SSL validation with :httpc adapter
    {:certifi, "~> 2.4"},
    {:ssl_verify_fun, "~> 1.1"},

and of course: mix deps.get

and install with: mix pow_assent.install

and now configure lib/fare/users/user.ex after use Pow.Ecto.Schema add use PowAssent.Ecto.Schema so now the top of this file should look like:

# lib/fare/users/user.ex
defmodule Fare.Users.User do
  use Ecto.Schema
  use Pow.Ecto.Schema
  use PowAssent.Ecto.Schema  # added in this step
  use Pow.Extension.Ecto.Schema,
      extensions: [PowResetPassword, PowEmailConfirmation]

At the top of the lib/fare_web/router.ex file after use PowAssent.Phoenix.Router add use PowAssent.Phoenix.Router - now the top of this file should look like:

# lib/fare_web/router.ex
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  use Pow.Phoenix.Router
  use PowAssent.Phoenix.Router
  use Pow.Extension.Phoenix.Router,
      extensions: [PowResetPassword, PowEmailConfirmation]

Now after the last pipelines add a new pipeline and its scope - its a copy of the :browser pipeline - without :protect_from_forgery since that conflicts with OAuth & after pow_routes() add pow_assent_routes() so now this section of the routes looks like (when Phoenix is configured for LiveView):

  pipeline :skip_csrf_protection do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {FareWeb.LayoutView, :root}
    # plug :protect_from_forgery     # conflicts with oauth
    plug :put_secure_browser_headers
  end

  scope "/" do
    pipe_through :skip_csrf_protection

    # this adds new pow routes
    pow_assent_authorization_post_callback_routes()
  end

  scope "/" do
    pipe_through :browser

    pow_routes()
    pow_assent_routes()    # newly added
    pow_extension_routes()
  end

Remember to run the new migrations with:

mix ecto.migrate

Generate the PowAssent template too (the page when using this where the user add username and OAuth password from remote site):

mix pow_assent.phoenix.gen.templates

Setup remote OAuth providers (Github - for now)

Go to: https://github.com/settings/applications/new

Enter an Application name and enter the Homepage url as:

http://localhost:4000/

and the Authorization callback (for our dev environment) as:

http://localhost:4000/auth/github/callback

Configure Github Credential Secrets

First update .gitignore with the line:

**/*.secret.exs

then add in our case the dev.secrets.exs file:

touch config/dev.secret.exs

Once you get your Client ID and Client secrets you can configure config/dev.secret.exs with the following config:

import Config

config :my_app, :pow_assent,
  providers: [
    github: [
      client_id: "REPLACE_WITH_CLIENT_ID",
      client_secret: "REPLACE_WITH_CLIENT_SECRET",
      strategy: Assent.Strategy.Github
    ]
  ]

Now at the END of config/dev.exs add the line:

import_config "dev.secret.exs"

Now at the end of:

  • lib/my_app_web/templates/pow/registration/edit.html.eex (edit profile),
  • lib/my_app_web/templates/pow/registration/new.html.eex (register),
  • lib/fare_web/templates/pow/session/new.html.eex (sign-in)

add the following comprehension to list all the configured OAuth log-in links:

<%=
  for link <- PowAssent.Phoenix.ViewHelpers.provider_links(@conn),
      do: content_tag(:span, link)
%>

Resources:

Bill Tihen
Bill Tihen
Developer, Data Enthusiast, Educator and Nature’s Friend

very curious – known to explore knownledge and nature