improvement(OIDC): Adjust dsl of OIDC reflect assent requirements (#538)

The OIDC implementation of assent requires the base_url to be set and ignores the different *_url attributes. At favours the returned configuration from the openid_configuration_uri. To not configure some unused attributes they're removed.
This commit is contained in:
Martin Schut 2024-01-04 23:20:13 +01:00 committed by James Harton
parent bcab91e745
commit 8721c01b4c
Signed by: james
GPG key ID: 90E82DAA13F624F4
5 changed files with 19 additions and 16 deletions

View file

@ -76,10 +76,8 @@ all the same configuration options should you need them.
| Name | Type | Default | Docs | | Name | Type | Default | Docs |
|------|------|---------|------| |------|------|---------|------|
| [`client_id`](#authentication-strategies-oidc-client_id){: #authentication-strategies-oidc-client_id .spark-required} | `(any, any -> any) \| module \| String.t` | | The OAuth2 client ID. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. | | [`client_id`](#authentication-strategies-oidc-client_id){: #authentication-strategies-oidc-client_id .spark-required} | `(any, any -> any) \| module \| String.t` | | The OAuth2 client ID. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`authorize_url`](#authentication-strategies-oidc-authorize_url){: #authentication-strategies-oidc-authorize_url .spark-required} | `(any, any -> any) \| module \| String.t` | | The API url to the OAuth2 authorize endpoint, relative to `site`, e.g `authorize_url fn _, _ -> {:ok, "https://exampe.com/authorize"} end`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. | | [`base_url`](#authentication-strategies-oidc-base_url){: #authentication-strategies-oidc-base_url .spark-required} | `(any, any -> any) \| module \| String.t` | | The base URL of the OAuth2 server - including the leading protocol (ie `https://`). Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`token_url`](#authentication-strategies-oidc-token_url){: #authentication-strategies-oidc-token_url .spark-required} | `(any, any -> any) \| module \| String.t` | | The API url to access the token endpoint, relative to `site`, e.g `token_url fn _, _ -> {:ok, "https://example.com/oauth_token"} end`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`redirect_uri`](#authentication-strategies-oidc-redirect_uri){: #authentication-strategies-oidc-redirect_uri .spark-required} | `(any, any -> any) \| module \| String.t` | | The callback URI *base*. Not the whole URI back to the callback endpoint, but the URI to your `AuthPlug`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. | | [`redirect_uri`](#authentication-strategies-oidc-redirect_uri){: #authentication-strategies-oidc-redirect_uri .spark-required} | `(any, any -> any) \| module \| String.t` | | The callback URI *base*. Not the whole URI back to the callback endpoint, but the URI to your `AuthPlug`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`base_url`](#authentication-strategies-oidc-base_url){: #authentication-strategies-oidc-base_url } | `(any, any -> any) \| module \| String.t` | | The base URL of the OAuth2 server - including the leading protocol (ie `https://`). Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`site`](#authentication-strategies-oidc-site){: #authentication-strategies-oidc-site } | `(any, any -> any) \| module \| String.t` | | Deprecated: Use `base_url` instead. | | [`site`](#authentication-strategies-oidc-site){: #authentication-strategies-oidc-site } | `(any, any -> any) \| module \| String.t` | | Deprecated: Use `base_url` instead. |
| [`auth_method`](#authentication-strategies-oidc-auth_method){: #authentication-strategies-oidc-auth_method } | `nil \| :client_secret_basic \| :client_secret_post \| :client_secret_jwt \| :private_key_jwt` | `:client_secret_post` | The authentication strategy used, optional. If not set, no authentication will be used during the access token request. | | [`auth_method`](#authentication-strategies-oidc-auth_method){: #authentication-strategies-oidc-auth_method } | `nil \| :client_secret_basic \| :client_secret_post \| :client_secret_jwt \| :private_key_jwt` | `:client_secret_post` | The authentication strategy used, optional. If not set, no authentication will be used during the access token request. |
| [`client_secret`](#authentication-strategies-oidc-client_secret){: #authentication-strategies-oidc-client_secret } | `(any, any -> any) \| module \| String.t` | | The OAuth2 client secret. Required if :auth_method is `:client_secret_basic`, `:client_secret_post` or `:client_secret_jwt`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. | | [`client_secret`](#authentication-strategies-oidc-client_secret){: #authentication-strategies-oidc-client_secret } | `(any, any -> any) \| module \| String.t` | | The OAuth2 client secret. Required if :auth_method is `:client_secret_basic`, `:client_secret_post` or `:client_secret_jwt`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |

View file

@ -84,12 +84,18 @@ defmodule AshAuthentication.Strategy.OAuth2.Plug do
|> Map.take(@raw_config_attrs) |> Map.take(@raw_config_attrs)
|> Map.put(:http_adapter, {Finch, supervisor: AshAuthentication.Finch}) |> Map.put(:http_adapter, {Finch, supervisor: AshAuthentication.Finch})
with {:ok, config} <- add_secret_value(config, strategy, :authorize_url), with {:ok, config} <- add_secret_value(config, strategy, :base_url),
{:ok, config} <- add_secret_value(config, strategy, :client_id), {:ok, config} <- add_secret_value(config, strategy, :authorize_url, !!strategy.base_url),
{:ok, config} <- add_secret_value(config, strategy, :client_secret), {:ok, config} <- add_secret_value(config, strategy, :client_id, !!strategy.base_url),
{:ok, config} <- add_secret_value(config, strategy, :base_url), {:ok, config} <- add_secret_value(config, strategy, :client_secret, !!strategy.base_url),
{:ok, config} <- add_secret_value(config, strategy, :token_url), {:ok, config} <- add_secret_value(config, strategy, :token_url, !!strategy.base_url),
{:ok, config} <- add_secret_value(config, strategy, :user_url, !!strategy.authorize_url), {:ok, config} <-
add_secret_value(
config,
strategy,
:user_url,
!!strategy.authorize_url || !!strategy.base_url
),
{:ok, redirect_uri} <- build_redirect_uri(strategy), {:ok, redirect_uri} <- build_redirect_uri(strategy),
{:ok, jwt_algorithm} <- {:ok, jwt_algorithm} <-
Info.authentication_tokens_signing_algorithm(strategy.resource) do Info.authentication_tokens_signing_algorithm(strategy.resource) do

View file

@ -1,6 +1,8 @@
defmodule AshAuthentication.Strategy.Oidc.Dsl do defmodule AshAuthentication.Strategy.Oidc.Dsl do
@moduledoc false @moduledoc false
import Spark.Options.Helpers, only: [make_required!: 2]
alias AshAuthentication.Strategy.{Custom, OAuth2} alias AshAuthentication.Strategy.{Custom, OAuth2}
@doc false @doc false
@ -24,6 +26,9 @@ defmodule AshAuthentication.Strategy.Oidc.Dsl do
defp patch_schema do defp patch_schema do
OAuth2.dsl() OAuth2.dsl()
|> Map.get(:schema, []) |> Map.get(:schema, [])
|> make_required!(:base_url)
|> Keyword.delete(:authorize_url)
|> Keyword.delete(:token_url)
|> Keyword.delete(:user_url) |> Keyword.delete(:user_url)
|> Keyword.merge( |> Keyword.merge(
openid_configuration_uri: [ openid_configuration_uri: [

View file

@ -9,13 +9,9 @@ defmodule AshAuthentication.Strategy.Oidc.Verifier do
@doc false @doc false
@spec verify(OAuth2.t(), map) :: :ok | {:error, Exception.t()} @spec verify(OAuth2.t(), map) :: :ok | {:error, Exception.t()}
def verify(strategy, _dsl_state) do def verify(strategy, _dsl_state) do
with :ok <- validate_secret(strategy, :authorize_url), with :ok <- validate_secret(strategy, :client_id),
:ok <- validate_secret(strategy, :client_id),
:ok <- validate_secret(strategy, :client_secret), :ok <- validate_secret(strategy, :client_secret),
:ok <- validate_secret(strategy, :redirect_uri),
:ok <- validate_secret(strategy, :base_url), :ok <- validate_secret(strategy, :base_url),
:ok <- validate_secret(strategy, :token_url),
:ok <- validate_secret(strategy, :user_url, [nil]),
:ok <- validate_secret(strategy, :nonce, [true, false]) do :ok <- validate_secret(strategy, :nonce, [true, false]) do
if strategy.auth_method == :private_key_jwt do if strategy.auth_method == :private_key_jwt do
validate_secret(strategy, :private_key) validate_secret(strategy, :private_key)

View file

@ -245,12 +245,10 @@ defmodule Example.User do
oidc do oidc do
authorization_params scope: "openid profile email phone address" authorization_params scope: "openid profile email phone address"
authorize_url &get_config/2
client_id &get_config/2 client_id &get_config/2
client_secret &get_config/2 client_secret &get_config/2
redirect_uri &get_config/2 redirect_uri &get_config/2
base_url &get_config/2 base_url &get_config/2
token_url &get_config/2
end end
end end
end end