diff --git a/lib/oidcc/plug/authorization_callback.ex b/lib/oidcc/plug/authorization_callback.ex index c6fa13c..c3497c3 100644 --- a/lib/oidcc/plug/authorization_callback.ex +++ b/lib/oidcc/plug/authorization_callback.ex @@ -80,7 +80,11 @@ defmodule Oidcc.Plug.AuthorizationCallback do @behaviour Plug + alias Oidcc.ClientContext alias Oidcc.Plug.Authorize + alias Oidcc.ProviderConfiguration + alias Oidcc.Token + alias Oidcc.Userinfo import Plug.Conn, only: [get_session: 2, delete_session: 2, put_private: 3, get_peer_data: 1, get_req_header: 2] @@ -95,6 +99,8 @@ defmodule Oidcc.Plug.AuthorizationCallback do * `provider` - name of the `Oidcc.ProviderConfiguration.Worker` * `client_id` - OAuth Client ID to use for the introspection * `client_secret` - OAuth Client Secret to use for the introspection + * `client_context_opts` - Options for Client Context Initialization + * `client_profile_opts` - Options for Client Context Profiles * `redirect_uri` - Where to redirect for callback * `check_useragent` - check if useragent is the same as before the authorization request @@ -108,6 +114,8 @@ defmodule Oidcc.Plug.AuthorizationCallback do provider: GenServer.name(), client_id: String.t() | (-> String.t()), client_secret: String.t() | (-> String.t()), + client_context_opts: :oidcc_client_context.opts() | (-> :oidcc_client_context.opts()), + client_profile_opts: :oidcc_profile.opts(), redirect_uri: String.t() | (-> String.t()), check_useragent: boolean(), check_peer_ip: boolean(), @@ -131,6 +139,8 @@ defmodule Oidcc.Plug.AuthorizationCallback do :provider, :client_id, :client_secret, + :client_context_opts, + :client_profile_opts, :redirect_uri, :preferred_auth_methods, check_useragent: true, @@ -145,13 +155,30 @@ defmodule Oidcc.Plug.AuthorizationCallback do client_id = opts |> Keyword.fetch!(:client_id) |> evaluate_config() client_secret = opts |> Keyword.fetch!(:client_secret) |> evaluate_config() redirect_uri = opts |> Keyword.fetch!(:redirect_uri) |> evaluate_config() + client_context_opts = opts |> Keyword.get(:client_context_opts, %{}) |> evaluate_config() + client_profile_opts = opts |> Keyword.get(:client_profile_opts, %{profiles: []}) params = Map.merge(params, body_params) - %{nonce: nonce, peer_ip: peer_ip, useragent: useragent, pkce_verifier: pkce_verifier} = + %{ + nonce: nonce, + peer_ip: peer_ip, + useragent: useragent, + pkce_verifier: pkce_verifier, + state_verifier: state_verifier + } = case get_session(conn, Authorize.get_session_name()) do - nil -> %{nonce: :any, peer_ip: nil, useragent: nil, pkce_verifier: :none} - %{} = session -> session + nil -> + %{ + nonce: :any, + peer_ip: nil, + useragent: nil, + pkce_verifier: :none, + state_verifier: :none + } + + %{} = session -> + session end check_peer_ip? = Keyword.fetch!(opts, :check_peer_ip) @@ -159,8 +186,19 @@ defmodule Oidcc.Plug.AuthorizationCallback do retrieve_userinfo? = Keyword.fetch!(opts, :retrieve_userinfo) result = - with :ok <- check_peer_ip(conn, peer_ip, check_peer_ip?), + with {:ok, client_context} <- + ClientContext.from_configuration_worker( + provider, + client_id, + client_secret, + client_context_opts + ), + {:ok, client_context, profile_opts} <- + apply_profile(client_context, client_profile_opts), + :ok <- check_peer_ip(conn, peer_ip, check_peer_ip?), :ok <- check_useragent(conn, useragent, check_useragent?), + :ok <- check_state(params, state_verifier), + :ok <- check_issuer_request_param(params, client_context), {:ok, code} <- fetch_request_param(params, "code"), scope = Map.get(params, "scope", "openid"), scopes = :oidcc_scope.parse(scope), @@ -177,14 +215,12 @@ defmodule Oidcc.Plug.AuthorizationCallback do {:ok, token} <- retrieve_token( code, - provider, - client_id, - client_secret, + client_context, retrieve_userinfo?, - token_opts + Map.merge(profile_opts, token_opts) ), {:ok, userinfo} <- - retrieve_userinfo(token, provider, client_id, client_secret, retrieve_userinfo?) do + retrieve_userinfo(token, client_context, retrieve_userinfo?) do {:ok, {token, userinfo}} end @@ -234,16 +270,47 @@ defmodule Oidcc.Plug.AuthorizationCallback do end end + defp check_issuer_request_param(params, client_context) + + defp check_issuer_request_param(params, %ClientContext{ + provider_configuration: %ProviderConfiguration{ + issuer: issuer, + authorization_response_iss_parameter_supported: true + } + }) do + with {:ok, given_issuer} <- fetch_request_param(params, "iss") do + if issuer == given_issuer do + :ok + else + {:error, {:invalid_issuer, given_issuer}} + end + end + end + + defp check_issuer_request_param(_params, _client_context), do: :ok + + defp check_state(params, state_verifier) + defp check_state(%{"state" => _state}, :none), do: {:error, :state_not_verified} + defp check_state(_params, :none), do: :ok + + defp check_state(%{"state" => state}, state_verifier) do + if :erlang.phash2(state) == state_verifier do + :ok + else + {:error, :state_not_verified} + end + end + + defp check_state(_params, _state), do: :ok + @spec retrieve_token( code :: String.t(), - provider :: GenServer.name(), - client_id :: String.t(), - client_secret :: String.t(), + client_context :: ClientContext.t(), retrieve_userinfo? :: boolean(), token_opts :: :oidcc_token.retrieve_opts() ) :: {:ok, Oidcc.Token.t()} | {:error, error()} - defp retrieve_token(code, provider, client_id, client_secret, retrieve_userinfo?, token_opts) do - case Oidcc.retrieve_token(code, provider, client_id, client_secret, token_opts) do + defp retrieve_token(code, client_context, retrieve_userinfo?, token_opts) do + case Token.retrieve(code, client_context, token_opts) do {:ok, token} -> {:ok, token} {:error, {:none_alg_used, token}} when retrieve_userinfo? -> {:ok, token} {:error, reason} -> {:error, reason} @@ -252,21 +319,20 @@ defmodule Oidcc.Plug.AuthorizationCallback do @spec retrieve_userinfo( token :: Oidcc.Token.t(), - provider :: GenServer.name(), - client_id :: String.t(), - client_secret :: String.t(), + client_context :: ClientContext.t(), retrieve_userinfo? :: true ) :: {:ok, :oidcc_jwt_util.claims()} | {:error, error()} @spec retrieve_userinfo( token :: Oidcc.Token.t(), - provider :: GenServer.name(), - client_id :: String.t(), - client_secret :: String.t(), + client_context :: ClientContext.t(), retrieve_userinfo? :: false ) :: {:ok, nil} | {:error, error()} - defp retrieve_userinfo(token, provider, client_id, client_secret, retrieve_userinfo?) - defp retrieve_userinfo(_token, _provider, _client_id, _client_secret, false), do: {:ok, nil} + defp retrieve_userinfo(token, client_context, retrieve_userinfo?) + defp retrieve_userinfo(_token, _client_context, false), do: {:ok, nil} + + defp retrieve_userinfo(token, client_context, true), + do: Userinfo.retrieve(token, client_context, %{}) - defp retrieve_userinfo(token, provider, client_id, client_secret, true), - do: Oidcc.retrieve_userinfo(token, provider, client_id, client_secret, %{}) + defp apply_profile(client_context, profile_opts), + do: ClientContext.apply_profiles(client_context, profile_opts) end diff --git a/lib/oidcc/plug/authorize.ex b/lib/oidcc/plug/authorize.ex index d734794..9b0d350 100644 --- a/lib/oidcc/plug/authorize.ex +++ b/lib/oidcc/plug/authorize.ex @@ -20,13 +20,16 @@ defmodule Oidcc.Plug.Authorize do ## Query Params - * `state` - STate to relay to OpenID Provider. Commonly used for target redirect + * `state` - State to relay to OpenID Provider. Commonly used for target redirect URL after authorization. """ @moduledoc since: "0.1.0" @behaviour Plug + alias Oidcc.Authorization + alias Oidcc.ClientContext + import Plug.Conn, only: [send_resp: 3, put_resp_header: 3, put_session: 3, get_peer_data: 1, get_req_header: 2] @@ -57,6 +60,8 @@ defmodule Oidcc.Plug.Authorize do * `provider` - name of the `Oidcc.ProviderConfiguration.Worker` * `client_id` - OAuth Client ID to use for the introspection * `client_secret` - OAuth Client Secret to use for the introspection + * `client_context_opts` - Options for Client Context Initialization + * `client_profile_opts` - Options for Client Context Profiles """ @typedoc since: "0.1.0" @type opts :: [ @@ -65,7 +70,9 @@ defmodule Oidcc.Plug.Authorize do url_extension: :oidcc_http_util.query_params(), provider: GenServer.name(), client_id: String.t() | (-> String.t()), - client_secret: String.t() | (-> String.t()) + client_secret: String.t() | (-> String.t()), + client_context_opts: :oidcc_client_context.opts() | (-> :oidcc_client_context.opts()), + client_profile_opts: :oidcc_profile.opts() ] @impl Plug @@ -76,6 +83,8 @@ defmodule Oidcc.Plug.Authorize do :client_id, :client_secret, :redirect_uri, + :client_context_opts, + :client_profile_opts, url_extension: [], scopes: ["openid"] ]) @@ -86,8 +95,12 @@ defmodule Oidcc.Plug.Authorize do client_id = opts |> Keyword.fetch!(:client_id) |> evaluate_config() client_secret = opts |> Keyword.fetch!(:client_secret) |> evaluate_config() redirect_uri = opts |> Keyword.fetch!(:redirect_uri) |> evaluate_config() + client_context_opts = opts |> Keyword.get(:client_context_opts, %{}) |> evaluate_config() + client_profile_opts = opts |> Keyword.get(:client_profile_opts, %{profiles: []}) state = Map.get(params, "state", :undefined) + state_verifier = :erlang.phash2(state) + nonce = 31 |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false) pkce_verifier = 96 |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false) @@ -106,23 +119,39 @@ defmodule Oidcc.Plug.Authorize do ) |> Map.new() - case Oidcc.create_redirect_url(provider, client_id, client_secret, authorization_opts) do - {:ok, redirect_uri} -> - conn - |> put_session(get_session_name(), %{ - nonce: nonce, - peer_ip: peer_ip, - useragent: useragent, - pkce_verifier: pkce_verifier - }) - |> put_resp_header("location", IO.iodata_to_binary(redirect_uri)) - |> send_resp(302, "") - + with {:ok, client_context} <- + ClientContext.from_configuration_worker( + provider, + client_id, + client_secret, + client_context_opts + ), + {:ok, client_context, profile_opts} <- + apply_profile(client_context, client_profile_opts), + {:ok, redirect_uri} <- + Authorization.create_redirect_url( + client_context, + Map.merge(profile_opts, authorization_opts) + ) do + conn + |> put_session(get_session_name(), %{ + nonce: nonce, + peer_ip: peer_ip, + useragent: useragent, + pkce_verifier: pkce_verifier, + state_verifier: state_verifier + }) + |> put_resp_header("location", IO.iodata_to_binary(redirect_uri)) + |> send_resp(302, "") + else {:error, reason} -> raise Error, reason: reason end end + defp apply_profile(client_context, profile_opts), + do: ClientContext.apply_profiles(client_context, profile_opts) + @doc false @spec get_session_name :: String.t() def get_session_name, do: inspect(__MODULE__) diff --git a/mix.exs b/mix.exs index d4acf1c..79dfd8c 100644 --- a/mix.exs +++ b/mix.exs @@ -63,7 +63,7 @@ defmodule Oidcc.Plug.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:oidcc, "~> 3.0"}, + {:oidcc, "~> 3.2.0-beta"}, {:plug, "~> 1.14"}, {:ex_doc, "~> 0.29", only: :dev, runtime: false}, {:excoveralls, "~> 0.18.1", only: :test, runtime: false}, diff --git a/test/oidcc/plug/authorization_callback_test.exs b/test/oidcc/plug/authorization_callback_test.exs index ea0d196..f2fbc00 100644 --- a/test/oidcc/plug/authorization_callback_test.exs +++ b/test/oidcc/plug/authorization_callback_test.exs @@ -4,55 +4,92 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do import Mock + alias Oidcc.ClientContext alias Oidcc.Plug.AuthorizationCallback alias Oidcc.Plug.Authorize + alias Oidcc.ProviderConfiguration doctest AuthorizationCallback + setup_with_mocks([ + {Oidcc.ClientContext, [:passthrough], + [ + from_configuration_worker: fn _provider, _client_id, _client_secret, _opts -> + {:ok, provider_configuration} = + ProviderConfiguration.decode_configuration(%{ + "issuer" => "https://example.com", + "authorization_endpoint" => "https://example.com/auth", + "jwks_uri" => "https://example.com/jwks", + "scopes_supported" => ["openid"], + "response_types_supported" => ["code"], + "subject_types_supported" => ["public"], + "id_token_signing_alg_values_supported" => ["RS256"] + }) + + jwks = JOSE.JWK.generate_key({:oct, 64}) + + {:ok, + ClientContext.from_manual( + provider_configuration, + jwks, + "client_id", + "client_secret", + %{} + )} + end + ]} + ]) do + :ok + end + describe inspect(&Authorize.call/2) do - test_with_mock "successful retrieve", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> - {:ok, :token} - end, - retrieve_userinfo: fn :token, ProviderName, "client_id", "client_secret", %{} -> - {:ok, %{"sub" => "sub"}} - end do - opts = - AuthorizationCallback.init( - provider: ProviderName, - client_id: fn -> "client_id" end, - client_secret: "client_secret", - redirect_uri: "http://localhost:8080/oidc/return" - ) + test "successful retrieve" do + with_mocks [ + {Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + {:ok, :token} + end}, + {Oidcc.Userinfo, [], + retrieve: fn :token, _client_context, %{} -> + {:ok, %{"sub" => "sub"}} + end} + ] do + opts = + AuthorizationCallback.init( + provider: ProviderName, + client_id: fn -> "client_id" end, + client_secret: "client_secret", + redirect_uri: "http://localhost:8080/oidc/return" + ) - assert %{ - halted: false, - private: %{Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}}} - } = - "get" - |> conn("/", %{"code" => "code"}) - |> Plug.Test.init_test_session(%{ - Authorize.get_session_name() => %{ - nonce: "nonce", - peer_ip: {127, 0, 0, 1}, - useragent: "useragent", - pkce_verifier: "pkce_verifier" + assert %{ + halted: false, + private: %{ + Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}} } - }) - |> put_req_header("user-agent", "useragent") - |> AuthorizationCallback.call(opts) + } = + "get" + |> conn("/", %{"code" => "code"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 1}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: 0 + } + }) + |> put_req_header("user-agent", "useragent") + |> AuthorizationCallback.call(opts) + end end - test_with_mock "successful retrieve without userinfo", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + test_with_mock "successful retrieve without userinfo", %{}, Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> {:ok, :token} end do opts = @@ -75,7 +112,8 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do nonce: "nonce", peer_ip: {127, 0, 0, 1}, useragent: "useragent", - pkce_verifier: "pkce_verifier" + pkce_verifier: "pkce_verifier", + state_verifier: 0 } }) |> put_req_header("user-agent", "useragent") @@ -102,7 +140,8 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do nonce: "nonce", peer_ip: {127, 0, 0, 1}, useragent: "useragent", - pkce_verifier: "pkce_verifier" + pkce_verifier: "pkce_verifier", + state_verifier: 0 } }) |> put_req_header("user-agent", "other useragent") @@ -129,50 +168,57 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do nonce: "nonce", peer_ip: {127, 0, 0, 2}, useragent: "useragent", - pkce_verifier: "pkce_verifier" + pkce_verifier: "pkce_verifier", + state_verifier: 0 } }) |> put_req_header("user-agent", "useragent") |> AuthorizationCallback.call(opts) end - test_with_mock "allows mismatch if disabled", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> - {:ok, :token} - end, - retrieve_userinfo: fn :token, ProviderName, "client_id", "client_secret", %{} -> - {:ok, %{"sub" => "sub"}} - end do - opts = - AuthorizationCallback.init( - provider: ProviderName, - client_id: "client_id", - client_secret: "client_secret", - redirect_uri: "http://localhost:8080/oidc/return", - check_useragent: false, - check_peer_ip: false - ) + test "allows mismatch if disabled" do + with_mocks [ + {Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + {:ok, :token} + end}, + {Oidcc.Userinfo, [], + retrieve: fn :token, _client_context, %{} -> + {:ok, %{"sub" => "sub"}} + end} + ] do + opts = + AuthorizationCallback.init( + provider: ProviderName, + client_id: "client_id", + client_secret: "client_secret", + redirect_uri: "http://localhost:8080/oidc/return", + check_useragent: false, + check_peer_ip: false + ) - assert %{ - halted: false, - private: %{Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}}} - } = - "get" - |> conn("/", %{"code" => "code"}) - |> Plug.Test.init_test_session(%{ - Authorize.get_session_name() => %{ - nonce: "nonce", - peer_ip: {127, 0, 0, 2}, - useragent: "useragent", - pkce_verifier: "pkce_verifier" + assert %{ + halted: false, + private: %{ + Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}} } - }) - |> put_req_header("user-agent", "other useragent") - |> AuthorizationCallback.call(opts) + } = + "get" + |> conn("/", %{"code" => "code"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 2}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: 0 + } + }) + |> put_req_header("user-agent", "other useragent") + |> AuthorizationCallback.call(opts) + end end test "missing params" do @@ -197,57 +243,143 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do nonce: "nonce", peer_ip: {127, 0, 0, 1}, useragent: "useragent", - pkce_verifier: "pkce_verifier" + pkce_verifier: "pkce_verifier", + state_verifier: 0 } }) |> put_req_header("user-agent", "useragent") |> AuthorizationCallback.call(opts) end - test_with_mock "passes none alg with userinfo", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> - {:error, {:none_alg_used, :token}} - end, - retrieve_userinfo: fn :token, ProviderName, "client_id", "client_secret", %{} -> - {:ok, %{"sub" => "sub"}} - end do - opts = - AuthorizationCallback.init( - provider: ProviderName, - client_id: "client_id", - client_secret: "client_secret", - redirect_uri: "http://localhost:8080/oidc/return" - ) + test "state mismatch" do + with_mocks [ + {Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + {:ok, :token} + end}, + {Oidcc.Userinfo, [], + retrieve: fn :token, _client_context, %{} -> + {:ok, %{"sub" => "sub"}} + end} + ] do + opts = + AuthorizationCallback.init( + provider: ProviderName, + client_id: "client_id", + client_secret: "client_secret", + redirect_uri: "http://localhost:8080/oidc/return" + ) - assert %{ - halted: false, - private: %{Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}}} - } = - "get" - |> conn("/", %{"code" => "code"}) - |> Plug.Test.init_test_session(%{ - Authorize.get_session_name() => %{ - nonce: "nonce", - peer_ip: {127, 0, 0, 1}, - useragent: "useragent", - pkce_verifier: "pkce_verifier" + assert %{ + halted: false, + private: %{ + Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}} } - }) - |> put_req_header("user-agent", "useragent") - |> AuthorizationCallback.call(opts) + } = + "get" + |> conn("/", %{"code" => "code", "state" => "state"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 1}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: :erlang.phash2("state") + } + }) + |> put_req_header("user-agent", "useragent") + |> AuthorizationCallback.call(opts) + + assert %{ + halted: false, + private: %{Oidcc.Plug.AuthorizationCallback => {:error, :state_not_verified}} + } = + "get" + |> conn("/", %{"code" => "code", "state" => "state"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 1}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: 0 + } + }) + |> put_req_header("user-agent", "useragent") + |> AuthorizationCallback.call(opts) + + assert %{ + halted: false, + private: %{ + Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}} + } + } = + "get" + |> conn("/", %{"code" => "code"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 1}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: 0 + } + }) + |> put_req_header("user-agent", "useragent") + |> AuthorizationCallback.call(opts) + end + end + + test "passes none alg with userinfo" do + with_mocks [ + {Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + {:error, {:none_alg_used, :token}} + end}, + {Oidcc.Userinfo, [], + retrieve: fn :token, _client_context, %{} -> + {:ok, %{"sub" => "sub"}} + end} + ] do + opts = + AuthorizationCallback.init( + provider: ProviderName, + client_id: "client_id", + client_secret: "client_secret", + redirect_uri: "http://localhost:8080/oidc/return" + ) + + assert %{ + halted: false, + private: %{ + Oidcc.Plug.AuthorizationCallback => {:ok, {:token, %{"sub" => "sub"}}} + } + } = + "get" + |> conn("/", %{"code" => "code"}) + |> Plug.Test.init_test_session(%{ + Authorize.get_session_name() => %{ + nonce: "nonce", + peer_ip: {127, 0, 0, 1}, + useragent: "useragent", + pkce_verifier: "pkce_verifier", + state_verifier: 0 + } + }) + |> put_req_header("user-agent", "useragent") + |> AuthorizationCallback.call(opts) + end end end - test_with_mock "fails none alg without userinfo", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + test_with_mock "fails none alg without userinfo", %{}, Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> {:error, {:none_alg_used, :token}} end do opts = @@ -270,19 +402,18 @@ defmodule Oidcc.Plug.AuthorizationCallbackTest do nonce: "nonce", peer_ip: {127, 0, 0, 1}, useragent: "useragent", - pkce_verifier: "pkce_verifier" + pkce_verifier: "pkce_verifier", + state_verifier: 0 } }) |> put_req_header("user-agent", "useragent") |> AuthorizationCallback.call(opts) end - test_with_mock "relays errors", %{}, Oidcc, [], - retrieve_token: fn "code", - ProviderName, - "client_id", - "client_secret", - %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> + test_with_mock "relays errors", %{}, Oidcc.Token, [], + retrieve: fn "code", + _client_context, + %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> {:error, :provider_not_ready} end do opts = diff --git a/test/oidcc/plug/authorize_test.exs b/test/oidcc/plug/authorize_test.exs index fbb5acb..4e51a7c 100644 --- a/test/oidcc/plug/authorize_test.exs +++ b/test/oidcc/plug/authorize_test.exs @@ -4,15 +4,46 @@ defmodule Oidcc.Plug.AuthorizeTest do import Mock + alias Oidcc.ClientContext alias Oidcc.Plug.Authorize + alias Oidcc.ProviderConfiguration doctest Authorize + setup_with_mocks([ + {ClientContext, [:passthrough], + [ + from_configuration_worker: fn _provider, _client_id, _client_secret, _opts -> + {:ok, provider_configuration} = + ProviderConfiguration.decode_configuration(%{ + "issuer" => "https://example.com", + "authorization_endpoint" => "https://example.com/auth", + "jwks_uri" => "https://example.com/jwks", + "scopes_supported" => ["openid"], + "response_types_supported" => ["code"], + "subject_types_supported" => ["public"], + "id_token_signing_alg_values_supported" => ["RS256"] + }) + + jwks = JOSE.JWK.generate_key({:oct, 64}) + + {:ok, + ClientContext.from_manual( + provider_configuration, + jwks, + "client_id", + "client_secret", + %{} + )} + end + ]} + ]) do + :ok + end + describe inspect(&Authorize.call/2) do - test_with_mock "successful redirect", %{}, Oidcc, [], - create_redirect_url: fn ProviderName, - "client_id", - "client_secret", + test_with_mock "successful redirect", %{}, Oidcc.Authorization, [], + create_redirect_url: fn _client_context, %{redirect_uri: "http://localhost:8080/oidc/return", nonce: _nonce} -> {:ok, "http://example.com"} end do @@ -37,10 +68,8 @@ defmodule Oidcc.Plug.AuthorizeTest do assert ["http://example.com"] = get_resp_header(conn, "location") end - test_with_mock "error handling", %{}, Oidcc, [], - create_redirect_url: fn ProviderName, - "client_id", - "client_secret", + 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} end do