defmodule Ueberauth.Strategy.NuSSO do @moduledoc """ NuSSO Strategy for Überauth. Redirects the user to an NuSSO login page and verifies the auth token the NuSSO server returns after a successful login. The login flow looks like this: 1. User is redirected to the NuSSO server's login page by `Ueberauth.Strategy.NuSSO.handle_request!` 2. User signs in to the NuSSO server. 3. NuSSO server redirects back to the Elixir application, sending an auth token as an HTTP Cookie header. 4. This auth token is validated by this Überauth NuSSO strategy, fetching the user's information at the same time. 5. User can proceed to use the Elixir application. """ use Ueberauth.Strategy, ignores_csrf_attack: true alias Ueberauth.Auth.{Extra, Info} alias Ueberauth.Strategy.NuSSO import Plug.Conn @referer_key "nussoReferer" @doc """ Ueberauth `request` handler. Redirects to the NuSSO server's login page. """ def handle_request!(conn) do conn |> put_session(@referer_key, extract_referer(conn)) |> redirect!(redirect_url(conn)) end @doc """ Ueberauth after login callback with a valid NuSSO Cookie. """ def handle_callback!(%Plug.Conn{} = conn) do conn |> reset_referer() |> handle_token(conn.cookies[NuSSO.API.sso_cookie()]) end @doc "Ueberauth UID callback." def uid(conn), do: conn.private.nusso_user.uid @doc """ Ueberauth extra information callback. Returns all attributes the NuSSO server returned about the user that authenticated. """ def extra(conn) do %Extra{ raw_info: %{ user: conn.private.nusso_user } } end @doc """ Ueberauth user information. """ def info(conn) do user = conn.private.nusso_user %Info{ email: user |> Map.get(:mail), name: Enum.join([user |> Map.get(:givenName), user |> Map.get(:sn)], " "), nickname: user |> Map.get(:uid) } end defp redirect_url(conn) do callback_url(conn) |> NuSSO.API.force_https() |> NuSSO.API.login_url() end defp handle_token(conn, nil) do conn |> set_errors!([error("missing_sso_cookie", "No NuSSO SSO cookie received")]) end defp handle_token(conn, token) do token |> fetch_user |> handle_token_response(conn) end defp handle_token_response({:ok, user}, conn) do conn |> put_private(:nusso_user, user) end defp handle_token_response({:error, reason}, conn) do with errors <- [error(reason.exception, reason.message)] do conn |> set_errors!(errors) end end defp fetch_user(token) do token |> NuSSO.API.redeem_token() end defp reset_referer(%Plug.Conn{} = conn) do case conn |> get_session(@referer_key) do nil -> conn value -> conn |> delete_session(@referer_key) |> put_req_header("referer", value) end end defp extract_referer(%Plug.Conn{} = conn) do conn |> Plug.Conn.get_req_header("referer") |> extract_referer() |> NuSSO.API.force_https() end defp extract_referer([referer | _]), do: referer defp extract_referer([]), do: "/" end