Modifying phx.gen.auth to use ueber-auth
Recently I was wanting to add Google oauth account registration and login to my Phoenix LiveView app. I found a few articles, but there were a handful of errors in the tutorial. I adapted the changes to work with my project and figured I would write about it here to help anyone else looking to set up their app as well.
Assumptions
This guide assumes the following:
- You've run
mix phx.gen.auth Accounts User users- You haven't made any major changes to the
Userstruct.
- You haven't made any major changes to the
- You've set up an application in Google
- The application has the following scopes in google granted
emailprofile- The application has a redirect url safelisted for
http://localhost:4000/google/auth/callback
- The application has a redirect url safelisted for
- The application has the following scopes in google granted
- You have set the following environment variables
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET
- The top level module is
App, a lot of tutorials start withMyApp, just rename this to whatever the top level module is for your app.
Files
mix.exs
First add the deps to your mix.exs. Then run mix deps.get.
def deps do [ # ... {:ueberauth, "~> 0.10.8"}, {:ueberauth_google, "~> 0.12.1"} ] end
config/config.exs
Update your config.exs file to have the following configuration.
config :ueberauth, Ueberauth, providers: [ google: {Ueberauth.Strategy.Google, [default_scope: "email profile"]} ]
config/runtime.exs
Update your runtime.exs to pull the environment variables to configure the client_id and client_secret.
config :ueberauth, Ueberauth.Strategy.Google.OAuth, client_id: System.get_env("GOOGLE_CLIENT_ID"), client_secret: System.get_env("GOOGLE_CLIENT_SECRET")
lib/app/accounts.ex
Add a function to the accounts module.
defmodule App.Accounts do def register_oauth_user(attrs) do %User{} |> User.oauth_registration_changeset(attrs) |> Repo.insert() end end
lib/app/accounts/user.ex
Update your user.ex schema to support the oauth flow.
defmodule App.Accounts.User do schema "users" do # ... field :is_oauth_user, :boolean, default: false # ... end def oauth_registration_changeset(user, attrs, opts \\ []) do user |> cast(attrs, [:email]) |> validate_required([:email]) |> validate_email(opts) |> put_change(:is_oauth_user, true) end end
lib/app_web/controllers/oauth_controller.ex
Add a controller which uses the new changeset and registration functions.
defmodule AppWeb.OAuthController do alias AppWeb.UserAuth alias App.Accounts use AppWeb, :controller require Logger plug Ueberauth def request(conn, _params) do Phoenix.Controller.redirect(conn, to: Ueberauth.Strategy.Helpers.callback_url(conn)) end def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do email = auth.info.email case Accounts.get_user_by_email(email) do nil -> user_params = %{email: email} case Accounts.register_oauth_user(user_params) do {:ok, user} -> UserAuth.log_in_user(conn, user) {:error, changeset} -> Logger.error("Failed to create user #{inspect(changeset)}.") conn |> put_flash(:error, "Failed to create user.") |> redirect(to: ~p"/") end user -> UserAuth.log_in_user(conn, user) end end end
lib/app_web/router.ex
Wire in the oAuth controller to support support the callback.
scope "/auth", AppWeb do pipe_through :browser get "/:provider", OAuthController, :request get "/:provider/callback", OAuthController, :callback end
priv/repo/migrations/[timestamp]_add_oauth_user.exs
Run mix ecto.gen.migration add_oauth_user. Then in the file that's generated replace the contents with the following.
defmodule App.Repo.Migrations.AddOauthUser do use Ecto.Migration def up do alter table(:users) do add :is_oauth_user, :boolean, default: false modify :hashed_password, :string, null: true end end def down do alter table(:users) do remove :is_oauth_user modify :hashed_password, :string, null: false end end end
Testing
Finally run mix ecto.migrate. Once the migrations are completed, run iex -S mix phx.server, then visit https://localhost:4000/google/auth. From there you should be able to go through the full login flow and successfully create a user from the login.