Skip to content

Commit

Permalink
feat: logout with ueberauth_oidc
Browse files Browse the repository at this point in the history
  • Loading branch information
paulswartz committed Nov 6, 2023
1 parent 7f47bbb commit 44f51bf
Show file tree
Hide file tree
Showing 17 changed files with 143 additions and 36 deletions.
9 changes: 7 additions & 2 deletions assets/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# is restricted to this project.

# General application configuration
use Mix.Config
import Config

config :arrow,
ecto_repos: [Arrow.Repo],
Expand All @@ -16,7 +16,8 @@ config :arrow,
# Run migrations synchronously before anything else. Must finish in <5 seconds
migrate_synchronously?: true,
redirect_http?: true,
cognito_groups: %{
ueberauth_provider: :cognito,
auth_group_mapping: %{
"arrow-admin" => :admin
},
required_groups: %{
Expand Down Expand Up @@ -57,7 +58,12 @@ config :arrow, ArrowWeb.AuthManager, issuer: "arrow"

config :ueberauth, Ueberauth,
providers: [
cognito: {Ueberauth.Strategy.Cognito, []}
cognito: {Ueberauth.Strategy.Cognito, []},
keycloak: {
Ueberauth.Strategy.OIDC,
# userinfo is needed for roles
fetch_userinfo: true, uid_field: "email", response_type: "code", scope: "openid email"
}
]

config :ueberauth, Ueberauth.Strategy.Cognito,
Expand Down
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

# Configure your database
config :arrow, Arrow.Repo,
Expand Down
2 changes: 1 addition & 1 deletion config/prod.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

# For production, don't forget to configure the url host
# to something meaningful, Phoenix uses this information
Expand Down
26 changes: 26 additions & 0 deletions config/runtime.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
import Config

is_test? = config_env() == :test

case System.fetch_env("KEYCLOAK_DISCOVERY_URI") do
{:ok, keycloak_uri} when keycloak_uri != "" and not is_test? ->
config :arrow,
ueberauth_provider: :keycloak

keycloak_opts = [
discovery_document_uri: keycloak_uri,
client_id: System.fetch_env!("KEYCLOAK_CLIENT_ID"),
client_secret: System.fetch_env!("KEYCLOAK_CLIENT_SECRET")
]

keycloak_opts =
if keycloak_idp = System.get_env("KEYCLOAK_IDP_HINT") do
Keyword.put(keycloak_opts, :request_params, %{kc_idp_hint: keycloak_idp})
else
keycloak_opts
end

config :ueberauth, Ueberauth.Strategy.OIDC, keycloak: keycloak_opts

_ ->
:ok
end

if config_env() == :prod do
sentry_env = System.get_env("SENTRY_ENV")

Expand Down
2 changes: 1 addition & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

# Configure your database
config :arrow, Arrow.Repo,
Expand Down
2 changes: 1 addition & 1 deletion lib/arrow/accounts/group.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Arrow.Accounts.Group do
Functions for working with groups.
"""

@admin_group Application.compile_env!(:arrow, :cognito_groups)
@admin_group Application.compile_env!(:arrow, :auth_group_mapping)
|> Enum.find(&(elem(&1, 1) == :admin))
|> elem(0)

Expand Down
4 changes: 3 additions & 1 deletion lib/arrow/ueberauth/strategy/fake.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ defmodule Arrow.Ueberauth.Strategy.Fake do

@impl Ueberauth.Strategy
def handle_request!(conn) do
provider = Application.get_env(:arrow, :ueberauth_provider)

conn
|> redirect!("/auth/cognito/callback")
|> redirect!("/auth/#{provider}/callback")
|> halt()
end

Expand Down
3 changes: 2 additions & 1 deletion lib/arrow_web/auth_manager/error_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule ArrowWeb.AuthManager.ErrorHandler do

@impl Guardian.Plug.ErrorHandler
def auth_error(conn, {_type, _reason}, _opts) do
Controller.redirect(conn, to: Routes.auth_path(conn, :request, "cognito"))
provider = Application.get_env(:arrow, :ueberauth_provider)
Controller.redirect(conn, to: Routes.auth_path(conn, :request, "#{provider}"))
end
end
54 changes: 50 additions & 4 deletions lib/arrow_web/controllers/auth_controller.ex
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
defmodule ArrowWeb.AuthController do
use ArrowWeb, :controller
plug Ueberauth
plug(Ueberauth)

@spec logout(Plug.Conn.t(), map()) :: Plug.Conn.t()
def logout(conn, _params) do
logout_url = Map.get(Guardian.Plug.current_claims(conn), "logout_url")
conn = clear_session(conn)

if logout_url do
redirect(conn, external: logout_url)
else
redirect(conn, to: "/")
end
end

@spec callback(Plug.Conn.t(), map()) :: Plug.Conn.t()
def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
username = auth.uid
expiration = auth.credentials.expires_at
credentials = conn.assigns.ueberauth_auth.credentials

current_time = System.system_time(:second)

groups =
case auth do
# keycloak
%{extra: %{raw_info: %{userinfo: %{"roles" => roles}}}} -> roles
# cognito
%{credentials: %{other: %{groups: groups}}} -> groups
_ -> []
end

logout_url = logout_url(auth)

conn
|> Guardian.Plug.sign_in(
ArrowWeb.AuthManager,
username,
%{groups: credentials.other[:groups]},
%{
groups: groups,
logout_url: logout_url
},
ttl: {expiration - current_time, :seconds}
)
|> put_session(:arrow_username, username)
Expand All @@ -27,4 +51,26 @@ defmodule ArrowWeb.AuthController do
) do
send_resp(conn, 401, "unauthenticated")
end

defp logout_url(%{
credentials: %{other: %{id_token: id_token}},
extra: %{raw_info: %{opts: opts}}
}) do
# keycloak
case OpenIDConnect.end_session_uri(opts, %{
"post_logout_redirect_uri" => "https://www.mbta.com/",
"id_token_hint" => id_token
}) do
{:ok, logout_url} ->
logout_url

_ ->
nil
end
end

defp logout_url(_auth) do
# cognito
nil
end
end
13 changes: 7 additions & 6 deletions lib/arrow_web/plug/assign_user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule ArrowWeb.Plug.AssignUser do
import Plug.Conn
alias Arrow.Accounts.User

@cognito_groups Application.compile_env!(:arrow, :cognito_groups)
@auth_group_mapping Application.compile_env!(:arrow, :auth_group_mapping)

@spec init(Plug.opts()) :: Plug.opts()
def init(options), do: options
Expand All @@ -15,14 +15,15 @@ defmodule ArrowWeb.Plug.AssignUser do
%{"sub" => user_id} = claims = Guardian.Plug.current_claims(conn)

groups =
claims
|> Map.get("groups", [])
|> Enum.map(&@cognito_groups[&1])
|> Enum.reject(&is_nil/1)
for group <- Map.get(claims, "groups") || [],
{:ok, mapped} <- [Map.fetch(@auth_group_mapping, group)],
into: MapSet.new() do
mapped
end

assign(conn, :current_user, %User{
id: user_id,
groups: MapSet.new(groups)
groups: groups
})
end
end
1 change: 1 addition & 0 deletions lib/arrow_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ defmodule ArrowWeb.Router do
scope "/", ArrowWeb do
pipe_through([:redirect_prod_http, :browser, :authenticate])

get("/logout", AuthController, :logout)
get("/unauthorized", UnauthorizedController, :index)
get("/feed", FeedController, :index)
get("/mytoken", MyTokenController, :show)
Expand Down
5 changes: 5 additions & 0 deletions lib/arrow_web/templates/layout/app.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@

<div class="m-header__long-name navbar-text justify-content-end navbar-collapse collapse">
Adjustments to the Regular Right of Way
<%= if Map.has_key?(Guardian.Plug.current_claims(@conn), "logout_url"), do:
link("logout",
to: Routes.auth_path(@conn, :logout),
class: "ml-2 btn btn-outline-danger"
) %>
</div>
</nav>
</header>
Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ defmodule Arrow.MixProject do
{:telemetry_poller, "~> 0.5"},
{:tzdata, "~> 1.1"},
{:ueberauth_cognito, "0.4.0"},
{:ueberauth_oidc, github: "mbta/ueberauth_oidc"},
{:ueberauth, "~> 0.9"},
{:wallaby, "~> 0.28.1", runtime: false, only: :test},
{:sentry, "~> 8.0"}
Expand Down
Loading

0 comments on commit 44f51bf

Please sign in to comment.