diff --git a/elixir/apps/domain/lib/domain/auth/adapter/openid_connect/directory_sync.ex b/elixir/apps/domain/lib/domain/auth/adapter/openid_connect/directory_sync.ex index ea9c2cf07..fea3f25fc 100644 --- a/elixir/apps/domain/lib/domain/auth/adapter/openid_connect/directory_sync.ex +++ b/elixir/apps/domain/lib/domain/auth/adapter/openid_connect/directory_sync.ex @@ -140,12 +140,12 @@ defmodule Domain.Auth.Adapter.OpenIDConnect.DirectorySync do ) finish_time = System.monotonic_time(:millisecond) - Logger.info("Finished syncing in #{time_taken(start_time, finish_time)}") + Logger.info("Finished syncing #{adapter} providers in #{time_taken(start_time, finish_time)}") end defp sync_provider(module, provider) do start_time = System.monotonic_time(:millisecond) - Logger.debug("Syncing provider") + Logger.info("Syncing provider: #{provider.id}") if Domain.Accounts.idp_sync_enabled?(provider.account) do {:ok, pid} = Task.Supervisor.start_link() diff --git a/elixir/apps/domain/lib/domain/auth/adapters/okta/api_client.ex b/elixir/apps/domain/lib/domain/auth/adapters/okta/api_client.ex index 1c5f899e8..5664cbce8 100644 --- a/elixir/apps/domain/lib/domain/auth/adapters/okta/api_client.ex +++ b/elixir/apps/domain/lib/domain/auth/adapters/okta/api_client.ex @@ -1,6 +1,7 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do use Supervisor require Logger + alias Domain.Auth.Provider @pool_name __MODULE__.Finch @@ -29,7 +30,9 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do [conn_opts: [transport_opts: transport_opts]] end - def list_users(endpoint, api_token) do + def list_users(%Provider{} = provider) do + endpoint = provider.adapter_config["api_base_url"] + uri = URI.parse("#{endpoint}/api/v1/users") |> URI.append_query( @@ -42,7 +45,7 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do {"Content-Type", "application/json; okta-response=omitCredentials,omitCredentialsLinks"} ] - with {:ok, users} <- list_all(uri, headers, api_token) do + with {:ok, users} <- list_all(uri, headers, provider) do active_users = Enum.filter(users, fn user -> user["status"] == "ACTIVE" @@ -52,7 +55,9 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do end end - def list_groups(endpoint, api_token) do + def list_groups(%Provider{} = provider) do + endpoint = provider.adapter_config["api_base_url"] + uri = URI.parse("#{endpoint}/api/v1/groups") |> URI.append_query( @@ -63,10 +68,12 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do headers = [] - list_all(uri, headers, api_token) + list_all(uri, headers, provider) end - def list_group_members(endpoint, api_token, group_id) do + def list_group_members(%Provider{} = provider, group_id) do + endpoint = provider.adapter_config["api_base_url"] + uri = URI.parse("#{endpoint}/api/v1/groups/#{group_id}/users") |> URI.append_query( @@ -77,7 +84,7 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do headers = [] - with {:ok, members} <- list_all(uri, headers, api_token) do + with {:ok, members} <- list_all(uri, headers, provider) do enabled_members = Enum.filter(members, fn member -> member["status"] == "ACTIVE" @@ -87,14 +94,14 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do end end - defp list_all(uri, headers, api_token, acc \\ []) do - case list(uri, headers, api_token) do + defp list_all(uri, headers, provider, acc \\ []) do + case list(uri, headers, provider) do {:ok, list, nil} -> {:ok, List.flatten(Enum.reverse([list | acc]))} {:ok, list, next_page_uri} -> URI.parse(next_page_uri) - |> list_all(headers, api_token, [list | acc]) + |> list_all(headers, provider, [list | acc]) {:error, reason} -> {:error, reason} @@ -108,7 +115,8 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do end # TODO: Need to catch 401/403 specifically when error message is in header - defp list(uri, headers, api_token) do + defp list(uri, headers, %Provider{} = provider) do + api_token = fetch_latest_access_token(provider) headers = headers ++ [{"Authorization", "Bearer #{api_token}"}] request = Finch.build(:get, uri, headers) @@ -135,17 +143,21 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do {:error, :invalid_response} - {:ok, %Finch.Response{body: raw_body, status: status}} when status in 400..499 -> + {:ok, %Finch.Response{body: raw_body, status: status, headers: headers}} + when status in 400..499 -> Logger.error("API request failed with 4xx status #{status}", response: inspect(response) ) case Jason.decode(raw_body) do {:ok, json_response} -> + # Errors are in JSON body {:error, {status, json_response}} _error -> - {:error, {status, response}} + # Errors should be in www-authenticate header + error_map = parse_headers_for_errors(headers) + {:error, {status, error_map}} end {:ok, %Finch.Response{status: status}} when status in 500..599 -> @@ -190,4 +202,82 @@ defmodule Domain.Auth.Adapters.Okta.APIClient do end defp parse_link_header(nil), do: nil + + defp parse_headers_for_errors(headers) do + headers + |> Enum.find({}, fn {key, _val} -> key == "www-authenticate" end) + |> parse_error_header() + end + + defp parse_error_header({"www-authenticate", errors}) do + String.split(errors, ",") + |> Enum.map(&String.trim/1) + |> Enum.filter(&String.starts_with?(&1, "error")) + |> Enum.map(&String.replace(&1, "\"", "")) + |> Enum.map(&String.split(&1, "=")) + |> Enum.into(%{}, fn [k, v] -> {k, v} end) + end + + defp parse_error_header(_) do + Logger.info("No www-authenticate header present") + %{"error" => "unknown", "error_message" => "no www-authenticate header present"} + end + + defp fetch_latest_access_token(provider) do + access_token = provider.adapter_state["access_token"] + + if access_token_active?(access_token) do + access_token + else + # Fetch provider from DB and return latest access token + {:ok, provider} = Domain.Auth.fetch_active_provider_by_id(provider.id) + provider.adapter_state["access_token"] + end + end + + defp access_token_active?(token) do + current_time = DateTime.utc_now() + + with {:ok, exp} <- fetch_exp(token), + {:ok, timestamp_time} <- DateTime.from_unix(exp) do + case DateTime.compare(current_time, timestamp_time) do + :lt -> + time_diff = DateTime.diff(timestamp_time, current_time) + time_diff >= 2 * 60 + + _gt_or_eq -> + false + end + else + {:error, msg} when is_binary(msg) -> + Logger.info(msg) + false + + unknown_error -> + Logger.warning("Error while checking access token expiration", + unknown_error: inspect(unknown_error) + ) + + false + end + end + + defp fetch_exp(token) do + with {:ok, decoded_jwt} <- parse_jwt(token), + fields when not is_nil(fields) <- decoded_jwt.fields, + exp when is_integer(exp) <- fields["exp"] do + {:ok, exp} + else + {:error, reason} -> {:error, reason} + _ -> {:error, "exp field is missing or invalid"} + end + end + + defp parse_jwt(token) do + {:ok, JOSE.JWT.peek(token)} + rescue + ArgumentError -> {:error, "Could not parse token"} + Jason.DecodeError -> {:error, "Could not decode token json"} + _ -> {:error, "Unknown error while parsing jwt"} + end end diff --git a/elixir/apps/domain/lib/domain/auth/adapters/okta/jobs/sync_directory.ex b/elixir/apps/domain/lib/domain/auth/adapters/okta/jobs/sync_directory.ex index 8ad459083..a8588a42e 100644 --- a/elixir/apps/domain/lib/domain/auth/adapters/okta/jobs/sync_directory.ex +++ b/elixir/apps/domain/lib/domain/auth/adapters/okta/jobs/sync_directory.ex @@ -24,21 +24,18 @@ defmodule Domain.Auth.Adapters.Okta.Jobs.SyncDirectory do end def gather_provider_data(provider, task_supervisor_pid) do - endpoint = provider.adapter_config["api_base_url"] - access_token = provider.adapter_state["access_token"] - async_results = DirectorySync.run_async_requests(task_supervisor_pid, users: fn -> - Okta.APIClient.list_users(endpoint, access_token) + Okta.APIClient.list_users(provider) end, groups: fn -> - Okta.APIClient.list_groups(endpoint, access_token) + Okta.APIClient.list_groups(provider) end ) with {:ok, %{users: users, groups: groups}} <- async_results, - {:ok, membership_tuples} <- list_membership_tuples(endpoint, access_token, groups) do + {:ok, membership_tuples} <- list_membership_tuples(provider, groups) do identities_attrs = map_identity_attrs(users) actor_groups_attrs = map_group_attrs(groups) {:ok, {identities_attrs, actor_groups_attrs, membership_tuples}} @@ -66,10 +63,10 @@ defmodule Domain.Auth.Adapters.Okta.Jobs.SyncDirectory do end end - defp list_membership_tuples(endpoint, access_token, groups) do + defp list_membership_tuples(provider, groups) do OpenTelemetry.Tracer.with_span "sync_provider.fetch_data.memberships" do Enum.reduce_while(groups, {:ok, []}, fn group, {:ok, tuples} -> - case Okta.APIClient.list_group_members(endpoint, access_token, group["id"]) do + case Okta.APIClient.list_group_members(provider, group["id"]) do {:ok, members} -> tuples = Enum.map(members, &{"G:" <> group["id"], &1["id"]}) ++ tuples {:cont, {:ok, tuples}} diff --git a/elixir/apps/domain/test/domain/auth/adapters/okta/api_client_test.exs b/elixir/apps/domain/test/domain/auth/adapters/okta/api_client_test.exs index 79d9b66c6..1af567b72 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/okta/api_client_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/okta/api_client_test.exs @@ -1,16 +1,50 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do - use ExUnit.Case, async: true + use Domain.DataCase, async: true alias Domain.Mocks.OktaDirectory import Domain.Auth.Adapters.Okta.APIClient - describe "list_users/1" do - test "returns list of users" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" - OktaDirectory.mock_users_list_endpoint(bypass, 200) + setup do + jwk = %{ + "kty" => "oct", + "k" => :jose_base64url.encode("super_secret_key") + } - assert {:ok, users} = list_users(api_base_url, api_token) + jws = %{ + "alg" => "HS256" + } + + claims = %{ + "sub" => "1234567890", + "name" => "FooBar", + "iat" => DateTime.utc_now() |> DateTime.to_unix(), + "exp" => DateTime.utc_now() |> DateTime.add(3600, :second) |> DateTime.to_unix() + } + + {_, jwt} = + JOSE.JWT.sign(jwk, jws, claims) + |> JOSE.JWS.compact() + + account = Fixtures.Accounts.create_account() + + {provider, bypass} = + Fixtures.Auth.start_and_create_okta_provider( + account: account, + access_token: jwt + ) + + %{ + account: account, + provider: provider, + bypass: bypass + } + end + + describe "list_users/1" do + test "returns list of users", %{provider: provider, bypass: bypass} do + OktaDirectory.mock_users_list_endpoint(bypass, 200) + api_token = provider.adapter_state["access_token"] + + assert {:ok, users} = list_users(provider) assert length(users) == 2 for user <- users do @@ -32,88 +66,78 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do assert Plug.Conn.get_req_header(conn, "authorization") == ["Bearer #{api_token}"] end - test "returns error when Okta API is down" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns error when Okta API is down", %{provider: provider, bypass: bypass} do Bypass.down(bypass) - assert list_users(api_base_url, api_token) == + assert list_users(provider) == {:error, %Mint.TransportError{reason: :econnrefused}} end - test "returns invalid_response when api responds with unexpected 2xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns invalid_response when api responds with unexpected 2xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_users_list_endpoint(bypass, 201) - assert list_users(api_base_url, api_token) == {:error, :invalid_response} + assert list_users(provider) == {:error, :invalid_response} end - test "returns invalid_response when api responds with unexpected 3xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns invalid_response when api responds with unexpected 3xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_users_list_endpoint(bypass, 301) - assert list_users(api_base_url, api_token) == {:error, :invalid_response} + assert list_users(provider) == {:error, :invalid_response} end - test "returns error when api responds with 4xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" - + test "returns error when api responds with 4xx status", %{provider: provider, bypass: bypass} do OktaDirectory.mock_users_list_endpoint( bypass, 400, Jason.encode!(%{"error" => %{"code" => 400, "message" => "Bad Request"}}) ) - assert list_users(api_base_url, api_token) == + assert list_users(provider) == {:error, {400, %{"error" => %{"code" => 400, "message" => "Bad Request"}}}} end - test "returns retry_later when api responds with 5xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns retry_later when api responds with 5xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_users_list_endpoint(bypass, 500) - assert list_users(api_base_url, api_token) == {:error, :retry_later} + assert list_users(provider) == {:error, :retry_later} end - test "returns invalid_response when api responds with unexpected data format" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" - + test "returns invalid_response when api responds with unexpected data format", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_users_list_endpoint( bypass, 200, Jason.encode!(%{"invalid" => "format"}) ) - assert list_users(api_base_url, api_token) == {:error, :invalid_response} + assert list_users(provider) == {:error, :invalid_response} end - test "returns error when api responds with invalid JSON" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns error when api responds with invalid JSON", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_users_list_endpoint(bypass, 200, "invalid json") assert {:error, %Jason.DecodeError{data: "invalid json"}} = - list_users(api_base_url, api_token) + list_users(provider) end end describe "list_groups/1" do - test "returns list of groups" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns list of groups", %{provider: provider, bypass: bypass} do OktaDirectory.mock_groups_list_endpoint(bypass, 200) + api_token = provider.adapter_state["access_token"] - assert {:ok, groups} = list_groups(api_base_url, api_token) + assert {:ok, groups} = list_groups(provider) assert length(groups) == 4 for group <- groups do @@ -134,90 +158,80 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do assert Plug.Conn.get_req_header(conn, "authorization") == ["Bearer #{api_token}"] end - test "returns error when Okta API is down" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns error when Okta API is down", %{provider: provider, bypass: bypass} do Bypass.down(bypass) - assert list_groups(api_base_url, api_token) == + assert list_groups(provider) == {:error, %Mint.TransportError{reason: :econnrefused}} end - test "returns invalid_response when api responds with unexpected 2xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns invalid_response when api responds with unexpected 2xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_groups_list_endpoint(bypass, 201) - assert list_groups(api_base_url, api_token) == {:error, :invalid_response} + assert list_groups(provider) == {:error, :invalid_response} end - test "returns invalid_response when api responds with unexpected 3xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns invalid_response when api responds with unexpected 3xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_groups_list_endpoint(bypass, 301) - assert list_groups(api_base_url, api_token) == {:error, :invalid_response} + assert list_groups(provider) == {:error, :invalid_response} end - test "returns error when api responds with 4xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" - + test "returns error when api responds with 4xx status", %{provider: provider, bypass: bypass} do OktaDirectory.mock_groups_list_endpoint( bypass, 400, Jason.encode!(%{"error" => %{"code" => 400, "message" => "Bad Request"}}) ) - assert list_groups(api_base_url, api_token) == + assert list_groups(provider) == {:error, {400, %{"error" => %{"code" => 400, "message" => "Bad Request"}}}} end - test "returns retry_later when api responds with 5xx status" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns retry_later when api responds with 5xx status", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_groups_list_endpoint(bypass, 500) - assert list_groups(api_base_url, api_token) == {:error, :retry_later} + assert list_groups(provider) == {:error, :retry_later} end - test "returns invalid_response when api responds with unexpected data format" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" - + test "returns invalid_response when api responds with unexpected data format", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_groups_list_endpoint( bypass, 200, Jason.encode!(%{"invalid" => "format"}) ) - assert list_groups(api_base_url, api_token) == {:error, :invalid_response} + assert list_groups(provider) == {:error, :invalid_response} end - test "returns error when api responds with invalid JSON" do - api_token = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + test "returns error when api responds with invalid JSON", %{ + provider: provider, + bypass: bypass + } do OktaDirectory.mock_groups_list_endpoint(bypass, 200, "invalid json") assert {:error, %Jason.DecodeError{data: "invalid json"}} = - list_groups(api_base_url, api_token) + list_groups(provider) end end describe "list_group_members/1" do - test "returns list of group members" do - api_token = Ecto.UUID.generate() + test "returns list of group members", %{provider: provider, bypass: bypass} do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint(bypass, group_id, 200) + api_token = provider.adapter_state["access_token"] - assert {:ok, members} = list_group_members(api_base_url, api_token, group_id) + assert {:ok, members} = list_group_members(provider, group_id) assert length(members) == 2 @@ -232,41 +246,36 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do assert Plug.Conn.get_req_header(conn, "authorization") == ["Bearer #{api_token}"] end - test "returns error when Okta API is down" do - api_token = Ecto.UUID.generate() + test "returns error when Okta API is down", %{provider: provider, bypass: bypass} do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" Bypass.down(bypass) - assert list_group_members(api_base_url, api_token, group_id) == + assert list_group_members(provider, group_id) == {:error, %Mint.TransportError{reason: :econnrefused}} end - test "returns invalid_response when api responds with unexpected 2xx status" do - api_token = Ecto.UUID.generate() + test "returns invalid_response when api responds with unexpected 2xx status", %{ + provider: provider, + bypass: bypass + } do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" + OktaDirectory.mock_group_members_list_endpoint(bypass, group_id, 201) - assert list_group_members(api_base_url, api_token, group_id) == {:error, :invalid_response} + assert list_group_members(provider, group_id) == {:error, :invalid_response} end - test "returns invalid_response when api responds with unexpected 3xx status" do - api_token = Ecto.UUID.generate() + test "returns invalid_response when api responds with unexpected 3xx status", %{ + provider: provider, + bypass: bypass + } do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint(bypass, group_id, 301) - assert list_group_members(api_base_url, api_token, group_id) == {:error, :invalid_response} + assert list_group_members(provider, group_id) == {:error, :invalid_response} end - test "returns error when api responds with 4xx status" do - api_token = Ecto.UUID.generate() + test "returns error when api responds with 4xx status", %{provider: provider, bypass: bypass} do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint( bypass, @@ -275,24 +284,24 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do Jason.encode!(%{"error" => %{"code" => 400, "message" => "Bad Request"}}) ) - assert list_group_members(api_base_url, api_token, group_id) == + assert list_group_members(provider, group_id) == {:error, {400, %{"error" => %{"code" => 400, "message" => "Bad Request"}}}} end - test "returns retry_later when api responds with 5xx status" do - api_token = Ecto.UUID.generate() + test "returns retry_later when api responds with 5xx status", %{ + provider: provider, + bypass: bypass + } do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint(bypass, group_id, 500) - assert list_group_members(api_base_url, api_token, group_id) == {:error, :retry_later} + assert list_group_members(provider, group_id) == {:error, :retry_later} end - test "returns invalid_response when api responds with unexpected data format" do - api_token = Ecto.UUID.generate() + test "returns invalid_response when api responds with unexpected data format", %{ + provider: provider, + bypass: bypass + } do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint( bypass, @@ -301,14 +310,14 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do Jason.encode!(%{"invalid" => "data"}) ) - assert list_group_members(api_base_url, api_token, group_id) == {:error, :invalid_response} + assert list_group_members(provider, group_id) == {:error, :invalid_response} end - test "returns error when api responds with invalid JSON" do - api_token = Ecto.UUID.generate() + test "returns error when api responds with invalid JSON", %{ + provider: provider, + bypass: bypass + } do group_id = Ecto.UUID.generate() - bypass = Bypass.open() - api_base_url = "http://localhost:#{bypass.port}/" OktaDirectory.mock_group_members_list_endpoint( bypass, @@ -318,7 +327,7 @@ defmodule Domain.Auth.Adapters.Okta.APIClientTest do ) assert {:error, %Jason.DecodeError{data: "invalid json"}} = - list_group_members(api_base_url, api_token, group_id) + list_group_members(provider, group_id) end end end diff --git a/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs b/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs index 1c4364628..ab74c2f72 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs @@ -6,10 +6,30 @@ defmodule Domain.Auth.Adapters.Okta.Jobs.SyncDirectoryTest do describe "execute/1" do setup do + jwk = %{ + "kty" => "oct", + "k" => :jose_base64url.encode("super_secret_key") + } + + jws = %{ + "alg" => "HS256" + } + + claims = %{ + "sub" => "1234567890", + "name" => "FooBar", + "iat" => DateTime.utc_now() |> DateTime.to_unix(), + "exp" => DateTime.utc_now() |> DateTime.add(3600, :second) |> DateTime.to_unix() + } + + {_, jwt} = + JOSE.JWT.sign(jwk, jws, claims) + |> JOSE.JWS.compact() + account = Fixtures.Accounts.create_account() {provider, bypass} = - Fixtures.Auth.start_and_create_okta_provider(account: account) + Fixtures.Auth.start_and_create_okta_provider(account: account, access_token: jwt) %{ bypass: bypass, diff --git a/elixir/apps/domain/test/support/fixtures/auth.ex b/elixir/apps/domain/test/support/fixtures/auth.ex index f6574ee0e..a842ba41b 100644 --- a/elixir/apps/domain/test/support/fixtures/auth.ex +++ b/elixir/apps/domain/test/support/fixtures/auth.ex @@ -302,12 +302,17 @@ defmodule Domain.Fixtures.Auth do Fixtures.Accounts.create_account(assoc_attrs) end) + {access_token, attrs} = + pop_assoc_fixture(attrs, :access_token, & &1) + {:ok, provider} = Auth.create_provider(account, attrs) + access_token = access_token || "OIDC_ACCESS_TOKEN" + update!(provider, disabled_at: nil, adapter_state: %{ - "access_token" => "OIDC_ACCESS_TOKEN", + "access_token" => access_token, "refresh_token" => "OIDC_REFRESH_TOKEN", "expires_at" => DateTime.utc_now() |> DateTime.add(1, :day), "claims" => "openid email profile offline_access"