lib/ueberauth/strategy/oidcc.ex

defmodule Ueberauth.Strategy.Oidcc do
  @moduledoc """
  OIDC Strategy for Ueberauth.

  ## Options
  - uid_field: claim to use for the `uid` field in `Ueberauth.Auth` (default: "sub")

  See `UeberauthOidcc.Config` for other configuration.
  """
  use Ueberauth.Strategy

  import Ueberauth.Strategy.Helpers

  alias Ueberauth.Auth.Extra

  @doc """
  Handles the initial authentication request.
  """
  def handle_request!(conn) do
    opts = get_options!(conn)

    case UeberauthOidcc.Request.handle_request(opts, conn) do
      {:ok, conn} ->
        conn

      {:error, conn, reason} ->
        UeberauthOidcc.Error.set_described_error(conn, reason, "handle_request!")
    end
  end

  @doc """
  Handles the callback from the OIDC provider.
  """
  def handle_callback!(conn) do
    opts = get_options!(conn)

    conn = put_private(conn, :ueberauth_oidcc_opts, opts)

    case UeberauthOidcc.Callback.handle_callback(opts, conn) do
      {:ok, conn, token, userinfo} ->
        conn
        |> put_private(:ueberauth_oidcc_token, token)
        |> put_private(:ueberauth_oidcc_userinfo, userinfo)

      {:error, conn, reason} ->
        UeberauthOidcc.Error.set_described_error(conn, reason, "handle_callback!")
    end
  end

  @doc false
  def handle_cleanup!(conn) do
    conn
    |> put_private(:ueberauth_oidcc_opts, nil)
    |> put_private(:ueberauth_oidcc_token, nil)
    |> put_private(:ueberauth_oidcc_userinfo, nil)
  end

  @doc """
  Returns the configured uid field from the claims.
  """
  def uid(conn) do
    opts = conn.private.ueberauth_oidcc_opts
    uid_field = Map.get(opts, :uid_field, "sub")

    case conn.private do
      %{ueberauth_oidcc_userinfo: %{^uid_field => uid}} ->
        uid

      %{ueberauth_oidcc_token: token} ->
        Map.get(token.id.claims, uid_field)
    end
  end

  @doc """
  Returns the credentials from the oidc response.

  `other` includes `id_token`
  """
  def credentials(conn) do
    token = conn.private.ueberauth_oidcc_token
    UeberauthOidcc.Auth.credentials(token)
  end

  @doc """
  Returns an `Ueberauth.Auth.Extra` struct containing the claims and userinfo response.
  """
  def extra(conn) do
    %Extra{
      raw_info: %UeberauthOidcc.RawInfo{
        opts: conn.private.ueberauth_oidcc_opts,
        claims: conn.private.ueberauth_oidcc_token.id.claims,
        userinfo: conn.private[:ueberauth_oidcc_userinfo]
      }
    }
  end

  @doc """
  Returns a `Ueberauth.Auth.Info` struct populated with the data returned from
  the userinfo endpoint.

  This information is also included in the `Ueberauth.Auth.Credentials` struct.
  """
  def info(conn) do
    UeberauthOidcc.Auth.info(
      conn.private.ueberauth_oidcc_token,
      conn.private[:ueberauth_oidcc_userinfo]
    )
  end

  defp get_options!(conn) do
    compile_opts = options(conn)

    runtime_opts =
      (Application.get_env(:ueberauth_oidcc, :providers) || [])[strategy_name(conn)] || []

    UeberauthOidcc.Config.merge_and_expand_configuration([
      compile_opts,
      runtime_opts
    ])
  end
end