From 5cc140bb955c0fd9f4979a04706d523026c916b9 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 11:45:34 +0100 Subject: [PATCH 01/22] Fetch jwks_uri from openid-configuration --- lib/prima_auth0_ex/jwks_strategy.ex | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/prima_auth0_ex/jwks_strategy.ex b/lib/prima_auth0_ex/jwks_strategy.ex index d88267bc..488483ef 100644 --- a/lib/prima_auth0_ex/jwks_strategy.ex +++ b/lib/prima_auth0_ex/jwks_strategy.ex @@ -11,7 +11,15 @@ defmodule PrimaAuth0Ex.JwksStrategy do Keyword.merge(opts, jwks_url: jwks_url()) end - defp jwks_url, do: base_url() <> "/.well-known/jwks.json" + defp jwks_url do + %HTTPoison.Response{status_code: status_code, body: meta_body} = Telepoison.get!(oidc_metadata_url()) + true = status_code in 200..299 + + meta_body + |> Jason.decode!() + |> Map.fetch!("jwks_uri") + end defp base_url, do: Config.server!(:auth0_base_url) + defp oidc_metadata_url, do: base_url() <> "/.well-known/openid-configuration" end From 7fd06eea670d1703b628df75419db3c9f5cce3d4 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:12:51 +0100 Subject: [PATCH 02/22] Add localauth0 to the docker-compose --- config/config.exs | 2 +- docker-compose.yml | 10 ++++++++++ lib/prima_auth0_ex/jwks_strategy.ex | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index e1d36e37..fd6ff8cb 100644 --- a/config/config.exs +++ b/config/config.exs @@ -3,7 +3,7 @@ import Config # Default client, for backwards compatibility config :prima_auth0_ex, :clients, default_client: [ - auth0_base_url: "https://your-auth0-provider.com", + auth0_base_url: "http://localauth0:3000", client_id: "", client_secret: "", cache_namespace: "my-service", diff --git a/docker-compose.yml b/docker-compose.yml index f72e3ce2..d7791467 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,7 @@ services: stdin_open: true depends_on: - redis + - localauth0 redis: image: public.ecr.aws/bitnami/redis:5.0 @@ -30,5 +31,14 @@ services: environment: - ALLOW_EMPTY_PASSWORD=yes + localauth0: + image: public.ecr.aws/c6i9l4r6/localauth0:0.6.2 + ports: + - 3000:3000 + environment: + LOCALAUTH0_CONFIG_PATH: /localauth0.toml + volumes: + - ./localauth0.toml:/localauth0.toml:ro + volumes: app: diff --git a/lib/prima_auth0_ex/jwks_strategy.ex b/lib/prima_auth0_ex/jwks_strategy.ex index 488483ef..0e5bc2a3 100644 --- a/lib/prima_auth0_ex/jwks_strategy.ex +++ b/lib/prima_auth0_ex/jwks_strategy.ex @@ -20,6 +20,10 @@ defmodule PrimaAuth0Ex.JwksStrategy do |> Map.fetch!("jwks_uri") end +<<<<<<< HEAD defp base_url, do: Config.server!(:auth0_base_url) defp oidc_metadata_url, do: base_url() <> "/.well-known/openid-configuration" +======= + defp oidc_metadata_url, do: PrimaAuth0Ex.Auth0Credentials.from_env().base_url <> "/.well-known/openid-configuration" +>>>>>>> 87a1c15 (Fix localauth0 configuration) end From b19456ca45d5aaea49ed8bea06b496dfce6c0a67 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:38:00 +0100 Subject: [PATCH 03/22] Integrate localauth0 into tests --- config/config.exs | 6 +++--- config/test.exs | 10 +++++----- localauth0.toml | 5 +++++ test/prima_auth0_ex/config_test.exs | 24 ++++++++++++------------ 4 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 localauth0.toml diff --git a/config/config.exs b/config/config.exs index fd6ff8cb..7312691e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -4,8 +4,8 @@ import Config config :prima_auth0_ex, :clients, default_client: [ auth0_base_url: "http://localauth0:3000", - client_id: "", - client_secret: "", + client_id: "client_id", + client_secret: "client_secret", cache_namespace: "my-service", token_check_interval: :timer.seconds(1), signature_check_interval: :timer.seconds(1) @@ -20,7 +20,7 @@ config :prima_auth0_ex, :redis, ssl_allow_wildcard_certificates: false config :prima_auth0_ex, :server, - auth0_base_url: "https://your-auth0-provider.com", + auth0_base_url: "http://localauth0:3000", ignore_signature: false, audience: "some-audience", issuer: "https://your-auth0-tenant.com", diff --git a/config/test.exs b/config/test.exs index 336127fd..abe706d6 100644 --- a/config/test.exs +++ b/config/test.exs @@ -8,10 +8,10 @@ config :prima_auth0_ex, token_service: TokenServiceMock config :prima_auth0_ex, :server, - auth0_base_url: "server", + auth0_base_url: "http://localauth0:3000", ignore_signature: false, audience: "server", - issuer: "server", + issuer: "https://your-auth0-tenant.com", first_jwks_fetch_sync: true config :prima_auth0_ex, :redis, @@ -22,9 +22,9 @@ config :prima_auth0_ex, :redis, config :prima_auth0_ex, :clients, default_client: [ - auth0_base_url: "default", - client_id: "default", - client_secret: "default", + auth0_base_url: "http://localauth0:3000", + client_id: "client_id", + client_secret: "client_secret", cache_namespace: "default", token_check_interval: :timer.seconds(1), signature_check_interval: :timer.seconds(1) diff --git a/localauth0.toml b/localauth0.toml new file mode 100644 index 00000000..ee357a37 --- /dev/null +++ b/localauth0.toml @@ -0,0 +1,5 @@ +issuer = "https://your-auth0-tenant.com" + +[[audience]] +name = "server" +permissions = [ "1st:perm", "2nd:perm", "some:permission"] diff --git a/test/prima_auth0_ex/config_test.exs b/test/prima_auth0_ex/config_test.exs index 1b4b4272..1763b40d 100644 --- a/test/prima_auth0_ex/config_test.exs +++ b/test/prima_auth0_ex/config_test.exs @@ -7,18 +7,18 @@ defmodule ConfigTest do test "by getting whole config" do config = Config.default_client() - assert config[:auth0_base_url] == "default" - assert config[:client_id] == "default" - assert config[:client_secret] == "default" + assert config[:auth0_base_url] == "http://localauth0:3000" + assert config[:client_id] == "client_id" + assert config[:client_secret] == "client_secret" assert config[:cache_namespace] == "default" assert config[:token_check_interval] == :timer.seconds(1) assert config[:signature_check_interval] == :timer.seconds(1) end test "by getting specific props" do - assert Config.default_client(:auth0_base_url) == "default" - assert Config.default_client(:client_id) == "default" - assert Config.default_client(:client_secret) == "default" + assert Config.default_client(:auth0_base_url) == "http://localauth0:3000" + assert Config.default_client(:client_id) == "client_id" + assert Config.default_client(:client_secret) == "client_secret" assert Config.default_client(:cache_namespace) == "default" assert Config.default_client(:token_check_interval) == :timer.seconds(1) assert Config.default_client(:signature_check_interval) == :timer.seconds(1) @@ -31,7 +31,7 @@ defmodule ConfigTest do test "bang version" do assert_raise KeyError, fn -> Config.default_client!(:non_existing_property) end - assert Config.default_client!(:client_id) == "default" + assert Config.default_client!(:client_id) == "client_id" end end @@ -81,23 +81,23 @@ defmodule ConfigTest do test "by getting whole config" do config = Config.server() - assert config[:auth0_base_url] == "server" + assert config[:auth0_base_url] == "http://localauth0:3000" assert config[:ignore_signature] == false assert config[:audience] == "server" - assert config[:issuer] == "server" + assert config[:issuer] == "https://your-auth0-tenant.com" assert config[:first_jwks_fetch_sync] == true end test "by getting specific props" do - assert Config.server(:auth0_base_url) == "server" + assert Config.server(:auth0_base_url) == "http://localauth0:3000" assert Config.server(:ignore_signature) == false assert Config.server(:audience) == "server" - assert Config.server(:issuer) == "server" + assert Config.server(:issuer) == "https://your-auth0-tenant.com" assert Config.server(:first_jwks_fetch_sync) == true end test "by getting specific props with a bang" do - assert Config.server!(:auth0_base_url) == "server" + assert Config.server!(:auth0_base_url) == "http://localauth0:3000" assert_raise KeyError, fn -> Config.server!(:non_existing_property) end end end From 6997285e15577694fe19d3ca8c5cd44983809c17 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:40:43 +0100 Subject: [PATCH 04/22] Fix VerifyAndValidateTokenTest --- test/integration/plug/verify_and_validate_token_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/plug/verify_and_validate_token_test.exs b/test/integration/plug/verify_and_validate_token_test.exs index f1d57f30..1379e1ec 100644 --- a/test/integration/plug/verify_and_validate_token_test.exs +++ b/test/integration/plug/verify_and_validate_token_test.exs @@ -22,7 +22,7 @@ defmodule PrimaAuth0Ex.Plug.VerifyAndValidateTokenTest do :get |> conn("/") |> put_req_header("authorization", "Bearer " <> token.jwt) - |> VerifyAndValidateToken.call(VerifyAndValidateToken.init([])) + |> VerifyAndValidateToken.call(VerifyAndValidateToken.init(required_permissions: ["1st:perm", "some:permission"])) refute conn.status == 401 end From 875e5e577a2470d15c079da42d6d7a06e99581e6 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:44:44 +0100 Subject: [PATCH 05/22] Do not exclude external tests by default --- test/test_helper.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_helper.exs b/test/test_helper.exs index a1e6929d..e077f2ba 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,3 @@ -ExUnit.configure(exclude: :external) ExUnit.start(capture_log: true) defmodule PrimaAuth0Ex.TestHelper do From af8998d4da5c6e7ab3eb3dbf7a495c810392ffdd Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:45:16 +0100 Subject: [PATCH 06/22] Remove mentions of external tests --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index c0023f1a..36093087 100644 --- a/README.md +++ b/README.md @@ -242,13 +242,6 @@ The test suite can be executed as follows: mix test ``` -By default tests that integrate with Auth0 are excluded. -To run them, configure your auth0 credentials and audience in `config/test.exs` and run: - -```bash -mix test --include external -``` - Always run formatter, linter and dialyzer before pushing changes: ```bash From 68cf6719562e7e07b63474144d7c33ff644a5006 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:40:20 +0100 Subject: [PATCH 07/22] Parse openid configuration in a dedicated module --- lib/prima_auth0_ex/jwks_strategy.ex | 19 +++---------------- lib/prima_auth0_ex/openid_configuration.ex | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 lib/prima_auth0_ex/openid_configuration.ex diff --git a/lib/prima_auth0_ex/jwks_strategy.ex b/lib/prima_auth0_ex/jwks_strategy.ex index 0e5bc2a3..f9817fe9 100644 --- a/lib/prima_auth0_ex/jwks_strategy.ex +++ b/lib/prima_auth0_ex/jwks_strategy.ex @@ -5,25 +5,12 @@ defmodule PrimaAuth0Ex.JwksStrategy do alias PrimaAuth0Ex.Config + alias PrimaAuth0Ex.OpenIDConfiguration use JokenJwks.DefaultStrategyTemplate def init_opts(opts) do - Keyword.merge(opts, jwks_url: jwks_url()) + jwks_url = Config.server(:auth0_base_url) |> OpenIDConfiguration.fetch() |> Map.fetch!(:jwks_uri) + Keyword.merge(opts, jwks_url: jwks_url) end - defp jwks_url do - %HTTPoison.Response{status_code: status_code, body: meta_body} = Telepoison.get!(oidc_metadata_url()) - true = status_code in 200..299 - - meta_body - |> Jason.decode!() - |> Map.fetch!("jwks_uri") - end - -<<<<<<< HEAD - defp base_url, do: Config.server!(:auth0_base_url) - defp oidc_metadata_url, do: base_url() <> "/.well-known/openid-configuration" -======= - defp oidc_metadata_url, do: PrimaAuth0Ex.Auth0Credentials.from_env().base_url <> "/.well-known/openid-configuration" ->>>>>>> 87a1c15 (Fix localauth0 configuration) end diff --git a/lib/prima_auth0_ex/openid_configuration.ex b/lib/prima_auth0_ex/openid_configuration.ex new file mode 100644 index 00000000..88fdfee6 --- /dev/null +++ b/lib/prima_auth0_ex/openid_configuration.ex @@ -0,0 +1,22 @@ +defmodule PrimaAuth0Ex.OpenIDConfiguration do + @moduledoc "Credentials to access Auth0" + + @type t :: %__MODULE__{issuer: String.t(), token_endpoint: String.t(), jwks_uri: String.t()} + + @struct_keys [:issuer, :token_endpoint, :jwks_uri] + defstruct @struct_keys + + @spec fetch(String.t()) :: __MODULE__.t() + def fetch(base_url) do + %HTTPoison.Response{status_code: status_code, body: meta_body} = base_url |> oidc_metadata_url |> Telepoison.get!() + true = status_code in 200..299 + + metadata = Jason.decode!(meta_body) + + metadata = Map.new(@struct_keys, fn key -> {key, metadata[Atom.to_string(key)]} end) + + struct!(__MODULE__, metadata) + end + + defp oidc_metadata_url(base_url), do: base_url <> "/.well-known/openid-configuration" +end From 6d90a1ee0b00d69a8fecc5935154352932c35380 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:42:40 +0100 Subject: [PATCH 08/22] Fetch token endpoint from the openid configuration endpoint --- .../token_provider/auth0_authorization_service.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex index b1bdaeba..b5f02b07 100644 --- a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex +++ b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex @@ -6,14 +6,15 @@ defmodule PrimaAuth0Ex.TokenProvider.Auth0AuthorizationService do @behaviour PrimaAuth0Ex.TokenProvider.AuthorizationService require Logger + alias PrimaAuth0Ex.OpenIDConfiguration alias PrimaAuth0Ex.TokenProvider.TokenInfo @auth0_token_api_path "/oauth/token" @impl PrimaAuth0Ex.TokenProvider.AuthorizationService def retrieve_token(credentials, audience) do + url = OpenIDConfiguration.fetch(credentials.base_url).token_endpoint request_body = body(credentials, audience) - url = credentials.base_url |> URI.merge(@auth0_token_api_path) |> URI.to_string() Logger.info("Requesting token to Auth0", client: credentials.client, From 671197c27ae84f33882d7be11cd63cef1333996a Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:43:03 +0100 Subject: [PATCH 09/22] Formatting --- lib/prima_auth0_ex/openid_configuration.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/prima_auth0_ex/openid_configuration.ex b/lib/prima_auth0_ex/openid_configuration.ex index 88fdfee6..fdb024ea 100644 --- a/lib/prima_auth0_ex/openid_configuration.ex +++ b/lib/prima_auth0_ex/openid_configuration.ex @@ -12,7 +12,6 @@ defmodule PrimaAuth0Ex.OpenIDConfiguration do true = status_code in 200..299 metadata = Jason.decode!(meta_body) - metadata = Map.new(@struct_keys, fn key -> {key, metadata[Atom.to_string(key)]} end) struct!(__MODULE__, metadata) From d9000fb48cdcc827a1730e2350d3634e1d5bb153 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:06:03 +0100 Subject: [PATCH 10/22] Fix authorization service tests not mocking the openid metadata endpoint --- lib/prima_auth0_ex/openid_configuration.ex | 4 +++- .../auth0_authorization_service_test.exs | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/prima_auth0_ex/openid_configuration.ex b/lib/prima_auth0_ex/openid_configuration.ex index fdb024ea..18a04b62 100644 --- a/lib/prima_auth0_ex/openid_configuration.ex +++ b/lib/prima_auth0_ex/openid_configuration.ex @@ -8,7 +8,9 @@ defmodule PrimaAuth0Ex.OpenIDConfiguration do @spec fetch(String.t()) :: __MODULE__.t() def fetch(base_url) do - %HTTPoison.Response{status_code: status_code, body: meta_body} = base_url |> oidc_metadata_url |> Telepoison.get!() + url = oidc_metadata_url(base_url) + %HTTPoison.Response{status_code: status_code, body: meta_body} = Telepoison.get!(url, accept: "application/json") + true = status_code in 200..299 metadata = Jason.decode!(meta_body) diff --git a/test/prima_auth0_ex/token_provider/auth0_authorization_service_test.exs b/test/prima_auth0_ex/token_provider/auth0_authorization_service_test.exs index a706dbb8..75b78aaf 100644 --- a/test/prima_auth0_ex/token_provider/auth0_authorization_service_test.exs +++ b/test/prima_auth0_ex/token_provider/auth0_authorization_service_test.exs @@ -19,6 +19,11 @@ defmodule PrimaAuth0Ex.TokenProvider.Auth0AuthorizationServiceTest do setup do bypass = Bypass.open() + + Bypass.stub(bypass, "GET", "/.well-known/openid-configuration", fn conn -> + Plug.Conn.resp(conn, 200, Jason.encode!(openid_configuration(bypass))) + end) + {:ok, bypass: bypass} end @@ -114,4 +119,22 @@ defmodule PrimaAuth0Ex.TokenProvider.Auth0AuthorizationServiceTest do end defp valid_auth0_response, do: ~s<{"access_token":"#{sample_token()}","expires_in":86400,"token_type":"Bearer"}> + + defp openid_configuration(bypass) do + %{ + issuer: "https://tenant.eu.auth0.com/", + authorization_endpoint: "http://localhost:#{bypass.port}/oauth/login", + token_endpoint: "http://localhost:#{bypass.port}/oauth/token", + jwks_uri: "http://localhost:#{bypass.port}/jwks.json", + response_types_supported: [ + "token id_token" + ], + subject_types_supported: [ + "public" + ], + id_token_signing_alg_values_supported: [ + "RS256" + ] + } + end end From 922eed9fafda6a26961b1714eb66c9e22a1731a4 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:15:02 +0100 Subject: [PATCH 11/22] Add docs to the OpenIDConfiguration module --- lib/prima_auth0_ex/openid_configuration.ex | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/prima_auth0_ex/openid_configuration.ex b/lib/prima_auth0_ex/openid_configuration.ex index 18a04b62..96a1d63f 100644 --- a/lib/prima_auth0_ex/openid_configuration.ex +++ b/lib/prima_auth0_ex/openid_configuration.ex @@ -1,14 +1,24 @@ defmodule PrimaAuth0Ex.OpenIDConfiguration do - @moduledoc "Credentials to access Auth0" + @moduledoc false + @doc """ + Module for fetching and parsing the [rfc8414](https://www.rfc-editor.org/rfc/rfc8414) openid metadata endpoint + + This allows auth0_ex to be agnostic of the actual openid server + """ @type t :: %__MODULE__{issuer: String.t(), token_endpoint: String.t(), jwks_uri: String.t()} @struct_keys [:issuer, :token_endpoint, :jwks_uri] defstruct @struct_keys + @doc """ + Fetches the openid metadata. + + Doesn't implement caching and always makes a http request, avoid calling this in hot code paths + """ @spec fetch(String.t()) :: __MODULE__.t() def fetch(base_url) do - url = oidc_metadata_url(base_url) + url = metadata_url(base_url) %HTTPoison.Response{status_code: status_code, body: meta_body} = Telepoison.get!(url, accept: "application/json") true = status_code in 200..299 @@ -19,5 +29,5 @@ defmodule PrimaAuth0Ex.OpenIDConfiguration do struct!(__MODULE__, metadata) end - defp oidc_metadata_url(base_url), do: base_url <> "/.well-known/openid-configuration" + defp metadata_url(base_url), do: base_url <> "/.well-known/openid-configuration" end From 05d4a84ef9f2be6f8ca65b10c4ff6e146a23b355 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:27:56 +0100 Subject: [PATCH 12/22] Format --- lib/prima_auth0_ex/jwks_strategy.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/prima_auth0_ex/jwks_strategy.ex b/lib/prima_auth0_ex/jwks_strategy.ex index f9817fe9..94d35aa0 100644 --- a/lib/prima_auth0_ex/jwks_strategy.ex +++ b/lib/prima_auth0_ex/jwks_strategy.ex @@ -12,5 +12,4 @@ defmodule PrimaAuth0Ex.JwksStrategy do jwks_url = Config.server(:auth0_base_url) |> OpenIDConfiguration.fetch() |> Map.fetch!(:jwks_uri) Keyword.merge(opts, jwks_url: jwks_url) end - end From f083e843dba3a184b23b0b97506dc119143a05a8 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:29:49 +0100 Subject: [PATCH 13/22] Linters --- .../token_provider/auth0_authorization_service.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex index b5f02b07..5cee9501 100644 --- a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex +++ b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex @@ -9,8 +9,6 @@ defmodule PrimaAuth0Ex.TokenProvider.Auth0AuthorizationService do alias PrimaAuth0Ex.OpenIDConfiguration alias PrimaAuth0Ex.TokenProvider.TokenInfo - @auth0_token_api_path "/oauth/token" - @impl PrimaAuth0Ex.TokenProvider.AuthorizationService def retrieve_token(credentials, audience) do url = OpenIDConfiguration.fetch(credentials.base_url).token_endpoint From 19075acf7d1c4d1b54c827f2c49eaab7ad5ea43c Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:39:11 +0100 Subject: [PATCH 14/22] Add changelog entry --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 966664af..b1b05106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 To migrate set `:prima_auth0_ex, :token_cache` to `EncryptedRedisTokenCache` or `NoopCache` +### Changed + +- Use the [rfc8414](https://www.rfc-editor.org/rfc/rfc8414) metadata endpoint to fetch information about the auth server + +This allows auth0_ex to be used with other compliant openid servers, like okta --- ## [0.6.4] - 2023-11-20 From 5a571a8aee1da1511efce8be384c34b32bead2b0 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:46:38 +0100 Subject: [PATCH 15/22] Punctuation --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b05106..bb910d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ To migrate set `:prima_auth0_ex, :token_cache` to `EncryptedRedisTokenCache` or - Use the [rfc8414](https://www.rfc-editor.org/rfc/rfc8414) metadata endpoint to fetch information about the auth server -This allows auth0_ex to be used with other compliant openid servers, like okta +This allows auth0_ex to be used with other compliant openid servers, like okta. --- ## [0.6.4] - 2023-11-20 From 9b657468871372f4f9ad625669171a7f3d7c7b80 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:52:50 +0100 Subject: [PATCH 16/22] Mention the need to update localauth0 in the changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb910d23..abd48c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ To migrate set `:prima_auth0_ex, :token_cache` to `EncryptedRedisTokenCache` or - Use the [rfc8414](https://www.rfc-editor.org/rfc/rfc8414) metadata endpoint to fetch information about the auth server This allows auth0_ex to be used with other compliant openid servers, like okta. + +Note that if you're using [localauth0](https://github.com/primait/localauth0), you will need to update to version 0.6.2 or later(public.ecr.aws/c6i9l4r6/localauth0:0.6.2). + --- ## [0.6.4] - 2023-11-20 From 8b4cadca929cda55605d2877cbbf77e96310d052 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:17:39 +0100 Subject: [PATCH 17/22] Add metadata parsing test --- .../openid_configuration_test.exs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/prima_auth0_ex/openid_configuration_test.exs diff --git a/test/prima_auth0_ex/openid_configuration_test.exs b/test/prima_auth0_ex/openid_configuration_test.exs new file mode 100644 index 00000000..73e22e3b --- /dev/null +++ b/test/prima_auth0_ex/openid_configuration_test.exs @@ -0,0 +1,43 @@ +defmodule PrimaAuth0Ex.OpenIDConfigurationTest do + use ExUnit.Case, async: true + + alias PrimaAuth0Ex.OpenIDConfiguration + + setup do + bypass = Bypass.open() + {:ok, bypass: bypass} + end + + test "Fetches and parses metadata from a server", %{bypass: bypass} do + config = openid_configuration(bypass) + Bypass.expect_once(bypass, "GET", "/.well-known/openid-configuration", fn conn -> + Plug.Conn.resp(conn, 200, Jason.encode!(config)) + end) + base_url = "http://localhost:#{bypass.port}" + + fetched = OpenIDConfiguration.fetch(base_url) + + assert fetched.issuer == config.issuer + assert fetched.token_endpoint == config.token_endpoint + assert fetched.jwks_uri == config.jwks_uri + + end + + defp openid_configuration(bypass) do + %{ + issuer: "https://tenant.eu.auth0.com/", + authorization_endpoint: "http://localhost:#{bypass.port}/oauth/login", + token_endpoint: "http://localhost:#{bypass.port}/oauth/token", + jwks_uri: "http://localhost:#{bypass.port}/jwks.json", + response_types_supported: [ + "token id_token" + ], + subject_types_supported: [ + "public" + ], + id_token_signing_alg_values_supported: [ + "RS256" + ] + } + end +end From 0814447ab898c14db8d5dbf9cc06c28dba320282 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:20:05 +0100 Subject: [PATCH 18/22] Format --- test/prima_auth0_ex/openid_configuration_test.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/prima_auth0_ex/openid_configuration_test.exs b/test/prima_auth0_ex/openid_configuration_test.exs index 73e22e3b..f6a437d8 100644 --- a/test/prima_auth0_ex/openid_configuration_test.exs +++ b/test/prima_auth0_ex/openid_configuration_test.exs @@ -10,17 +10,18 @@ defmodule PrimaAuth0Ex.OpenIDConfigurationTest do test "Fetches and parses metadata from a server", %{bypass: bypass} do config = openid_configuration(bypass) + Bypass.expect_once(bypass, "GET", "/.well-known/openid-configuration", fn conn -> Plug.Conn.resp(conn, 200, Jason.encode!(config)) end) + base_url = "http://localhost:#{bypass.port}" - + fetched = OpenIDConfiguration.fetch(base_url) assert fetched.issuer == config.issuer assert fetched.token_endpoint == config.token_endpoint assert fetched.jwks_uri == config.jwks_uri - end defp openid_configuration(bypass) do From 17b76490f4fcbcd06d91f4ecdc0801f10b6b7744 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Fri, 3 Nov 2023 14:19:31 +0100 Subject: [PATCH 19/22] fetch -> fetch! --- lib/prima_auth0_ex/jwks_strategy.ex | 2 +- lib/prima_auth0_ex/openid_configuration.ex | 13 ++++++++++--- .../token_provider/auth0_authorization_service.ex | 2 +- test/prima_auth0_ex/openid_configuration_test.exs | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/prima_auth0_ex/jwks_strategy.ex b/lib/prima_auth0_ex/jwks_strategy.ex index 94d35aa0..a8849ce6 100644 --- a/lib/prima_auth0_ex/jwks_strategy.ex +++ b/lib/prima_auth0_ex/jwks_strategy.ex @@ -9,7 +9,7 @@ defmodule PrimaAuth0Ex.JwksStrategy do use JokenJwks.DefaultStrategyTemplate def init_opts(opts) do - jwks_url = Config.server(:auth0_base_url) |> OpenIDConfiguration.fetch() |> Map.fetch!(:jwks_uri) + jwks_url = Config.server(:auth0_base_url) |> OpenIDConfiguration.fetch!() |> Map.fetch!(:jwks_uri) Keyword.merge(opts, jwks_url: jwks_url) end end diff --git a/lib/prima_auth0_ex/openid_configuration.ex b/lib/prima_auth0_ex/openid_configuration.ex index 96a1d63f..4f837cea 100644 --- a/lib/prima_auth0_ex/openid_configuration.ex +++ b/lib/prima_auth0_ex/openid_configuration.ex @@ -16,12 +16,19 @@ defmodule PrimaAuth0Ex.OpenIDConfiguration do Doesn't implement caching and always makes a http request, avoid calling this in hot code paths """ - @spec fetch(String.t()) :: __MODULE__.t() - def fetch(base_url) do + @spec fetch!(String.t()) :: __MODULE__.t() + def fetch!(base_url) do url = metadata_url(base_url) %HTTPoison.Response{status_code: status_code, body: meta_body} = Telepoison.get!(url, accept: "application/json") - true = status_code in 200..299 + unless status_code in 200..299 do + raise """ + Failed to retrieve the openid-configuration from #{url}, server sent ${status_code}: + #{meta_body} + + This is most likely caused by an incorrect base_url. + """ + end metadata = Jason.decode!(meta_body) metadata = Map.new(@struct_keys, fn key -> {key, metadata[Atom.to_string(key)]} end) diff --git a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex index 5cee9501..de0ab809 100644 --- a/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex +++ b/lib/prima_auth0_ex/token_provider/auth0_authorization_service.ex @@ -11,7 +11,7 @@ defmodule PrimaAuth0Ex.TokenProvider.Auth0AuthorizationService do @impl PrimaAuth0Ex.TokenProvider.AuthorizationService def retrieve_token(credentials, audience) do - url = OpenIDConfiguration.fetch(credentials.base_url).token_endpoint + url = OpenIDConfiguration.fetch!(credentials.base_url).token_endpoint request_body = body(credentials, audience) Logger.info("Requesting token to Auth0", diff --git a/test/prima_auth0_ex/openid_configuration_test.exs b/test/prima_auth0_ex/openid_configuration_test.exs index f6a437d8..98454fbf 100644 --- a/test/prima_auth0_ex/openid_configuration_test.exs +++ b/test/prima_auth0_ex/openid_configuration_test.exs @@ -17,7 +17,7 @@ defmodule PrimaAuth0Ex.OpenIDConfigurationTest do base_url = "http://localhost:#{bypass.port}" - fetched = OpenIDConfiguration.fetch(base_url) + fetched = OpenIDConfiguration.fetch!(base_url) assert fetched.issuer == config.issuer assert fetched.token_endpoint == config.token_endpoint From 3f719407ebb2ad64727729870bd60dbc28234082 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:17:34 +0100 Subject: [PATCH 20/22] Use memory cache by default --- lib/prima_auth0_ex/token_cache/token_cache.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/prima_auth0_ex/token_cache/token_cache.ex b/lib/prima_auth0_ex/token_cache/token_cache.ex index 285b7489..693242a3 100644 --- a/lib/prima_auth0_ex/token_cache/token_cache.ex +++ b/lib/prima_auth0_ex/token_cache/token_cache.ex @@ -22,7 +22,7 @@ defmodule PrimaAuth0Ex.TokenCache do end def get_configured_cache_provider do - cache_provider = Config.token_cache(NoopCache) + cache_provider = Config.token_cache(MemoryCache) with builtin_cache_provider <- Module.concat(PrimaAuth0Ex.TokenCache, cache_provider), {:module, ^builtin_cache_provider} <- Code.ensure_compiled(builtin_cache_provider) do From 929c4e49aaa5183cb8faeabeee223bec043f548a Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:49:37 +0100 Subject: [PATCH 21/22] Update CHANGELOG --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abd48c44..572ff8a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Deprecate the `:prima_auth0_ex, :redis, :enabled` option in favor of `:prima_auth0_ex, :token_cache` -To migrate set `:prima_auth0_ex, :token_cache` to `EncryptedRedisTokenCache` or `NoopCache` +To migrate set `:prima_auth0_ex, :token_cache` to `EncryptedRedisTokenCache`, `NoopCache` or `MemoryCache`(default) + +- Tokens are now cached by default using the MemoryCache backend + +You can disable it by setting `:prima_auth0_ex, :token_cache` to `NoopCache`. + +- Use the [rfc8414](https://www.rfc-editor.org/rfc/rfc8414) metadata endpoint to fetch information about the auth server. + +This allows auth0_ex to be used with other compliant openid servers, like okta. + +Note that if you're using [localauth0](https://github.com/primait/localauth0), you will need to update to version 0.6.2 or later(public.ecr.aws/c6i9l4r6/localauth0:0.6.2). ### Changed From 201079ff1310ec141298f2da7f3924878dfd9875 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:58:19 +0100 Subject: [PATCH 22/22] Fix localauth0 missing in CI --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 991b4dc5..62f4bc4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,14 +11,30 @@ on: jobs: ci: runs-on: ubuntu-latest - container: - image: elixir:1.13 env: MIX_ENV: test steps: + - uses: erlef/setup-beam@v1 + with: + elixir-version: 1.13 + otp-version: 24 + # Check out the code. - name: Checkout uses: actions/checkout@v3 + + - name: Restart localuth0 + # Restart localauth0 after checking out so it can load the config + run: docker restart localauth0 + + - name: Add hosts entries + run: | + echo " + 127.0.0.1 localauth0 + 127.0.0.1 redis + " | sudo tee /etc/hosts + + # Define how to cache deps. Restores existing cache if present. - name: Cache deps id: cache-deps @@ -53,10 +69,6 @@ jobs: run: | mix deps.clean --all mix clean - - name: Elixir setup - run: | - mix local.hex --force - mix local.rebar --force - name: Deps get run: mix deps.get - name: Dependencies Check @@ -85,6 +97,16 @@ jobs: ports: - 6379:6379 + localauth0: + image: public.ecr.aws/c6i9l4r6/localauth0:0.6.2 + ports: + - 3000:3000 + env: + LOCALAUTH0_CONFIG_PATH: /repo/localauth0.toml + volumes: + - ./:/repo:ro + options: --name localauth0 + alls-green: if: always() needs: