summaryrefslogtreecommitdiff
path: root/lib/web/controllers/open_id_controller.ex
blob: 24dc1a5c441fc4d1a0a5a1c5c016f1aa8d402ade (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
defmodule NolaWeb.OpenIdController do
  use NolaWeb, :controller
  plug NolaWeb.ContextPlug, restrict: :public
  require Logger

  def login(conn, _) do
    url = OAuth2.Client.authorize_url!(new_client(), scope: "openid", state: Base.url_encode64(:crypto.strong_rand_bytes(32), padding: false))
    redirect(conn, external: url)
  end

  def callback(conn, %{"error" => error_code, "error_description" => error}) do
    Logger.warn("OpenId error: #{error_code} #{error}")
    render(conn, "error.html", error: error)
  end

  def callback(conn, %{"code" => code, "state" => state}) do
    with \
         client = %{token: %OAuth2.AccessToken{access_token: json}} = OAuth2.Client.get_token!(new_client(), state: state, code: code),
         {:ok, %{"access_token" => token}} <- Jason.decode(json),
           client = %OAuth2.Client{client | token: %OAuth2.AccessToken{access_token: token}},
         {:ok, %OAuth2.Response{body: body}} <- OAuth2.Client.get(client, "/userinfo"),
         {:ok, %{"sub" => id, "preferred_username" => username}} <- Jason.decode(body)
    do
      if account = conn.assigns.account do
          if !Nola.Account.get_meta(account, "identity-id") do # XXX: And oidc id not linked yet
            Nola.Account.put_meta(account, "identity-id", id)
          end
          Nola.Account.put_meta(account, "identity-username", username)
          conn
      else
        conn
      end

      conn
      |> put_session(:oidc_id, id)
      |> put_flash(:info, "Logged in!")
      |> redirect(to: Routes.path(conn, "/"))
    else
      {:error, %OAuth2.Response{status_code: 401}} ->
        Logger.error("OpenID: Unauthorized token")
        render(conn, "error.html", error: "The token is invalid.")
      {:error, %OAuth2.Error{reason: reason}} ->
        Logger.error("Error: #{inspect reason}")
        render(conn, "error.html", error: reason)
    end
  end

  def callback(conn, _params) do
    render(conn, "error.html", error: "Unspecified error.")
  end

  defp new_client() do
    config = Application.get_env(:nola, :oidc)
    OAuth2.Client.new([
      strategy: OAuth2.Strategy.AuthCode,
      client_id: config[:client_id],
      client_secret: config[:client_secret],
      site: config[:base_url],
      authorize_url: config[:authorize_url],
      token_url: config[:token_url],
      redirect_uri: Routes.open_id_url(NolaWeb.Endpoint, :callback)
    ])
  end
end