---
title: 'Authentication in Elixir Web Applications with Ueberauth and Guardian: Part
  4

  '
teaser: 'Part 4 in a series on Ueberauth and Guardian authentication. Let users register
  and log in using OAuth providers like Google.

  '
tags: guardian,ueberauth,authentication,phoenix,elixir,web
author: Lance Johnson
published_on: 2020-05-13
---

In the previous posts in this series we walked through:

* [creating a basic Phoenix application][1]
* [providing pages for registration and log in][1]
* [adding the ability to register for an account using an email address and
  password][2]
* [adding the ability to log in and out of one's account][3]

In this post we are going to expand upon that base to allow users to create an
account or log in to an existing account using OAuth providers such as Google or
Twitter.

## OAuth providers

A detailed explanation of OAuth is outside the scope of this blog post. If
you're not familiar with OAuth, [this video] is a good brief introduction. It
will be helpful, however, to explore how OAuth works in the context of using it
to log in to accounts in our application. In the case of using an email address
and password, our application, Yauth, assumed two responsibilities:

1. establishing the identity of our users by receiving and storing emails and
   passwords upon account registration; and
2. verifying the identity of users by challenging them to produce the correct
   email and password combination on subsequent attempts to log in to an
   account.

Our application can, however, offload one or both of those responsibilities to
OAuth providers with which we can integrate.

Our users may have already established their identity and the credentials needed
to prove that identity with OAuth providers like Google, Twitter, Facebook, etc.
Yauth can register with those providers as an application that would like to
allow users access via the identity and credentials they manage. When we
register Yauth with the provider, the provider gives us a client id and client
secret and we give them a callback URL to our application. Yauth can use that
information to create a link on Yauth pages. When our user clicks that link,
they are redirected to the OAuth provider's login page (i.e. their identity
challenge). When the user completes the identity challenge (e.g. providing their
user name and password for that provider), that information is submitted to the
OAuth provider's server. The OAuth server then redirects the user to the
callback URL that Yauth provided with the result of the identity challenge. When
Yauth receives that data, it creates an account and logs in upon success or
displays an error message if the challenge failed.

By using OAuth providers, Yauth can accept the identity information for the user
from those providers and allow them to manage user authentication. This provides
flexibility for our users, some of whom may not want another password to manage,
and could free us from managing passwords, password resets, etc. if we wanted to
use OAuth exclusively.

## Implementing OAuth log in

We will use Google as our example OAuth provider for this post. The first step
will be to create a project with Google and set up credentials for an OAuth
client. Walking through that process step-by-step will make this post too long
but following "Step 1" in [these instructions] should get you where you need to
be to follow along.

Once we have registered with Google as an OAuth client, we need to implement
registration and log in to Yauth using Google. In an earlier post, we described
Ueberauth and used the Ueberauth Identity strategy to help us with
email/password registration and log in. Here we'll use the Ueberauth
Google strategy.

To get started, we need to add the Ueberauth Google Strategy package to our
project.

```elixir
# mix.exs
defmodule Yauth.MixProject do
  # ...
  defp deps do
    [
      # ...
      {:ueberauth, "~> 0.6"},
      {:ueberauth_google, "~> 0.8"},
      # ...
    ]
  end

  # ...
end
```

```sh
mix deps.get
```

In our previous post we configured Ueberauth for the Identity strategy. Now we
need to provide some configuration for the Google strategy.

```elixir
# config/config.exs
config :ueberauth, Ueberauth,
  providers: [
    google: {Ueberauth.Strategy.Google, []},
    # ...
  ]

config :ueberauth, Ueberauth.Strategy.Google.OAuth,
  client_id: System.get_env("GOOGLE_CLIENT_ID"),
  client_secret: System.get_env("GOOGLE_CLIENT_SECRET")
```

The first configuration tells `ueberauth` which providers we intend to use in
the application and the module that implements the strategy behavior for that
provider. The second configuration provides the client id and secret for the
Google strategy we intend to use in the form of environment variables. These
values are available from the Google Developer Console where you set up your
application. Remember to export those values in your shell before starting
your Phoenix server.

### Routes

As you may recall from our previous discussion, Ueberauth uses a "two-phase"
approach to authentication. The first phase presents an authentication challenge
to the user. The second phase handles the data provided in that challenge. To
accomplish this with OAuth providers, we need to provide routes by which users
access this functionality.

```elixir
# lib/yauth_web/router.ex
defmodule YauthWeb.Router do
  # ...

  scope "/auth", YauthWeb do
    pipe_through [:browser, :guardian]

    get "/:provider", AuthController, :request
    get "/:provider/callback", AuthController, :callback
  end
end
```

The Ueberauth package expects routes to be prefixed with a certain path before
the OAuth provider name. The default option is "/auth", which works for our
application, but another path could be used by adding the `:base_path` option
in the `ueberauth` configuration in your `config/config.exs` file. We accomplish
that by placing these routes in the `scope "/auth" ...` block.

With the routes in place, we need to provide the controller. Here are the module
and function signatures for that controller that we'll walk through together.

```elixir
# lib/yauth_web/controllers/auth_controller.ex
defmodule YauthWeb.AuthController do
  use YauthWeb, :controller
  plug Ueberauth

  def request(conn, _params) do
    # Present an authentication challenge to the user
  end

  def callback(%{assigns: %{ueberauth_auth: auth_data}} = conn, _params) do
    # Find the account if it exists or create it if it doesn't
  end

  def callback(%{assigns: %{ueberauth_failure: _}} = conn, _params) do
    # Tell the user something went wrong
  end
end
```

#### Authentication challenge

The function `AuthController.request/2` exists to provide the authentication
challenge to the user. As we are not presenting an authentication challenge
(i.e. a form for the user to complete) to the user at this route, we don't
actually need to implement or even define this function. Why not? At the top of
the controller we call `plug Ueberauth`. In the event that the request is for a
configured OAuth provider, this plug redirects the request to that OAuth
provider's authentication challenge page in place of any function call within
the Yauth controller.

In other words, our Google button on our login and registration pages has the
route `/auth/google` that our `Router` directs to `AuthController.request/2`.
When the user clicks on that button, the request is directed to the
`AuthController` that first passes it through the `Ueberauth` plug. That plug
sees the request is for a configured OAuth provider and redirects the request to
the provider's URL, circumventing a call to the `request/2` function. Our final
implementation won't have this function signature but I wanted to explain why it
will be absent as it can be puzzling to see it in the `Router` but missing in
the controller. If we wanted, we could also move all of the handling for the
identity strategy out of the `RegistrationController` and `SessionController`
into the `AuthController`, which would require implementing `request/2` to
present the identity authentication challenge, but we'll leave that as is for
now.

#### Authentication verification

The `AuthController.callback/2` is the function registered with our OAuth
providers and handles the incoming request from the provider after their
authentication challenge has been completed. When the user provides the Google
credentials at the Google page, Google sends the requested data to the route
handled by this function.

The `Ueberauth` plug enters the picture in this case as well. That plug calls
functions provided by the Ueberauth strategy
implementation&mdash;`Ueberauth.Strategy.Google` in our case&mdash;that extract
the expected data from the incoming request. That extracted data is added to the
`Plug.Conn` assigns where our controller can easily access it. As you can see in
the two function heads above, this will assign either the key `:ueberauth_auth`
in the event of success or `:ueberauth_failure` in the event of failure.

We're now in a position to fill in the implementation of the `callback/2`
function. In a successful request we receive the email address of the user and
want to do two things. First, we want to see if an account exists with that
email address already, either through a previous registration via email address
and password or a previous OAuth login. If the account exists, we want to log in
the account and redirect them to their profile page. Second, if an account does
not exist we want to register and log in an account.<sup><a href="#fn1" id="r1"
title="Footnote 1">1</a></sup>

As we've done in the past, let's write the code we wish we had and fill in the
details as we go.

```elixir
# lib/yauth_web/controllers/auth_controller.ex
defmodule YauthWeb.AuthController do
  # ...
  alias Yauth.Accounts
  alias YauthWeb.Authentication

  def callback(%{assigns: %{ueberauth_auth: auth_data}} = conn, _params) do
    case Accounts.get_or_register(auth_data) do
      {:ok, account} ->
        conn
        |> Authentication.log_in(account)
        |> redirect(to: Routes.profile_path(conn, :show))

      {:error, _error_changeset} ->
        conn
        |> put_flash(:error, "Authentication failed.")
        |> redirect(to: Routes.registration_path(conn, :new))
    end
  end

  # ...
end
```

When we successfully receive data from the OAuth provider (as indicated by the
`:ueberauth_auth` key), we pass that data to the `get_or_register/1` function on
the `Accounts` context. The controller expects an `:ok` tuple with the account
(either loaded from the database or newly created) or an `:error` tuple. If we
receive the `:ok` tuple, we use the `log_in/2` function we introduced in our
prior post on registering with an email and password. Finally, we redirect the
user to their profile page. If we receive the `:error` tuple, we redirect to the
registration page with a message telling the user their authentication failed.

We also need to handle a failure from the OAuth provider (as indicated by the
`:ueberauth_failure` key). We can handle that situation with a second head of
the `callback/2` function.

```elixir
# lib/yauth_web/controllers/auth_controller.ex
defmodule YauthWeb.AuthController do
  # ...
  def callback(%{assigns: %{ueberauth_failure: _}} = conn, _params) do
    conn
    |> put_flash(:error, "Authentication failed.")
    |> redirect(to: Routes.registration_path(conn, :new))
  end
end
```

This is a simple function that adds a flash message and redirects to the
registration page.

With those in place we need to add the functions to our `Accounts` context
that are called by our controller.

```elixir
# lib/yauth/accounts.ex
defmodule Yauth.Accounts do
  # ...
  def get_or_register(%Ueberauth.Auth{info: %{email: email}} = params) do
    if account = get_by_email(email) do
      {:ok, account}
    else
      register(params)
    end
  end
  # ...
```

This function accepts the Ueberauth struct containing our user's information
from the OAuth provider. We pattern match on that struct to get the email
address of the user and pass that to our existing `get_by_email/1` function.
That function returns either an account or `nil`. If an account is returned we
wrap it in an `{:ok, account}` tuple. Otherwise we call through to the existing
`register/1` function with the Ueberauth struct. Our `register/1` function,
however, will need to handle the Google data as well as the identity data we
established in our previous post. Ueberauth adds the provider to its data
struct so we can pattern match on that value to handle our different use cases.
Update the existing `register/1` function to handle the identity provider.

```elixir
# lib/yauth/accounts.ex
defmodule Yauth.Accounts do
  # ...
  def register(%Ueberauth.Auth{provider: :identity} = params) do
    %Account{}
    |> Account.changeset(extract_account_params(params))
    |> Yauth.Repo.insert()
  end
  # ...
end
```

Now we can add a function head to handle our OAuth-provided data.

```elixir
# lib/yauth/accounts.ex
defmodule Yauth.Accounts do
  # ...
  def register(%Ueberauth.Auth{} = params) do
    %Account{}
    |> Account.oauth_changeset(extract_account_params(params))
    |> Yauth.Repo.insert()
  end
  # ...
end
```

This function head calls out to a new changeset function on the `Account`
module.

```elixir
# lib/yauth/accounts/account.ex
defmodule Yauth.Accounts.Account do
  # ...
  def oauth_changeset(struct, params) do
    struct
    |> cast(params, [:email])
    |> validate_required([:email])
    |> unique_constraint(:email)
  end
  # ...
end
```

For OAuth data we don't manage passwords at all but we still want to require an
email address and enforce the uniqueness of that email within our application.

## Views

Up to this point we've added our Ueberauth dependencies, provided the necessary
configuration, added routes for social login, and added the controller to
handle those requests. All that remains is for us to update our social sign in
links to point to the appropriate routes. Update your template with the
following:

```eex
<!-- lib/yauth_web/templates/session/social_links.html.eex -->
<!-- ... -->
<div class="social-log-in">
  <p>Or log in with</p>
  <%= link(
    "Google",
    to: Routes.auth_path(@conn, :request, "google"),
    class: "button button-outline"
  ) %>
</div>
<!-- ... -->
```

At this point our users should be able to register for or log in to an account
using their Google credentials.

## Recap

In this post we expanded our authentication options by letting users use their
OAuth service accounts to authenticate with Yauth. We used Google as our example
service. We registered our application with Google, added the Ueberauth Google
strategy to our application, and provided the controllers, views, and supporting
functions to register and log in users who authenticated with Google. To expand
our options to Twitter or GitHub or other OAuth providers, we can easily
repeat this process with those services.

[1]:https://thoughtbot.com/blog/authentication-in-elixir-web-applications-with-guardian-part-1
[2]:https://thoughtbot.com/blog/authentication-in-elixir-web-applications-with-guardian-part-2
[3]:https://thoughtbot.com/blog/authentication-in-elixir-web-applications-with-guardian-part-3
[this video]:https://www.youtube.com/watch?v=KT8ybowdyr0
[these instructions]:https://developers.google.com/identity/sign-in/web/server-side-flow

---

<p><a href="#r1" id="fn1">[1]</a> As an aside, we should note there are more
options for handling existing accounts when integrating with OAuth providers.
For the sake of simplicity, we allow users with existing identity accounts to
use the Google strategy as well. In other words, if you have a Yauth account
with a Gmail address and then later use the Google OAuth option with that same
Gmail address, you will access the same Yauth account. However, you could not do
the same in the opposite direction. The merits and limitations of this approach
should be compared with other options when setting up OAuth for production
systems. This post chose the simplest path for illustration; using it here is
not an endorsement of it as the best way to handle that situation.</p>
