diff --git a/lib/oidcc/plug/authorize.ex b/lib/oidcc/plug/authorize.ex index 9c96b94..669db11 100644 --- a/lib/oidcc/plug/authorize.ex +++ b/lib/oidcc/plug/authorize.ex @@ -31,7 +31,7 @@ defmodule Oidcc.Plug.Authorize do import Oidcc.Plug.Config, only: [evaluate_config: 2] import Plug.Conn, - only: [send_resp: 3, put_resp_header: 3, put_session: 3, get_req_header: 2] + only: [send_resp: 3, put_resp_header: 3, put_session: 3, get_req_header: 2, put_private: 3] alias Oidcc.Authorization alias Oidcc.ClientContext @@ -58,6 +58,7 @@ defmodule Oidcc.Plug.Authorize do * `scopes` - scopes to request * `redirect_uri` - Where to redirect for callback + * `redirect_mode` - Selects how the redirect happens. Either `:inline` or `:manual`. * `url_extension` - Custom query parameters to add to the redirect URI * `provider` - name of the `Oidcc.ProviderConfiguration.Worker` * `client_id` - OAuth Client ID to use for the introspection @@ -73,6 +74,7 @@ defmodule Oidcc.Plug.Authorize do @type opts :: [ scopes: :oidcc_scope.scopes(), redirect_uri: String.t() | (-> String.t()) | (Plug.Conn.t() -> String.t()), + redirect_mode: :inline | :manual, url_extension: :oidcc_http_util.query_params(), provider: GenServer.name() | nil, client_store: module() | nil, @@ -94,6 +96,7 @@ defmodule Oidcc.Plug.Authorize do :redirect_uri, :client_context_opts, :client_profile_opts, + redirect_mode: :inline, url_extension: [], scopes: ["openid"] ]) @@ -102,6 +105,7 @@ defmodule Oidcc.Plug.Authorize do @impl Plug def call(%Plug.Conn{params: params} = conn, opts) do redirect_uri = opts |> Keyword.fetch!(:redirect_uri) |> evaluate_config(conn) + redirect_mode = Keyword.fetch!(opts, :redirect_mode) client_profile_opts = Keyword.get(opts, :client_profile_opts, %{profiles: []}) state = @@ -144,14 +148,23 @@ defmodule Oidcc.Plug.Authorize do pkce_verifier: pkce_verifier, state_verifier: state_verifier }) - |> put_resp_header("location", IO.iodata_to_binary(redirect_uri)) - |> send_resp(302, "") + |> handle_redirect_uri(IO.iodata_to_binary(redirect_uri), redirect_mode) else {:error, reason} -> raise Error, reason: reason end end + defp handle_redirect_uri(conn, redirect_uri, :inline) do + conn + |> put_resp_header("location", redirect_uri) + |> send_resp(302, "") + end + + defp handle_redirect_uri(conn, redirect_uri, :manual) do + put_private(conn, __MODULE__, redirect_uri) + end + defp apply_profile(client_context, profile_opts), do: ClientContext.apply_profiles(client_context, profile_opts) @doc false diff --git a/test/oidcc/plug/authorize_test.exs b/test/oidcc/plug/authorize_test.exs index 88a3bda..36362c1 100644 --- a/test/oidcc/plug/authorize_test.exs +++ b/test/oidcc/plug/authorize_test.exs @@ -69,6 +69,35 @@ defmodule Oidcc.Plug.AuthorizeTest do assert ["http://example.com"] = get_resp_header(conn, "location") end + test_with_mock "manual redirect", %{}, Oidcc.Authorization, [], + create_redirect_url: fn _client_context, %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + {:ok, "http://example.com"} + end do + opts = + Authorize.init( + provider: ProviderName, + client_id: "client_id", + client_secret: "client_secret", + redirect_uri: "http://localhost:8080/oidc/return", + redirect_mode: :manual + ) + + assert %{ + halted: false, + status: nil, + private: %{ + Authorize => "http://example.com" + } + } = + conn = + "get" + |> conn("/", "") + |> Plug.Test.init_test_session(%{}) + |> Authorize.call(opts) + + assert [] = get_resp_header(conn, "location") + end + test_with_mock "error handling", %{}, Oidcc.Authorization, [], create_redirect_url: fn _client_context, %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> {:error, :provider_not_ready}