fix(portal): Fetch latest Okta access_token before API call (#8745)

Why:

* The Okta IdP sync job needs to make sure it is always using the latest
access token available. If not, there is the possibility for the job to
take too long to complete and the access token that the job started with
might time out. This commit updates the Okta API client to always check
and make sure it is using the latest access token for each request to
the Okta API.
This commit is contained in:
Brian Manifold
2025-04-11 14:25:07 -07:00
committed by GitHub
parent a87db29453
commit bed6a60056
6 changed files with 272 additions and 151 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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}}

View File

@@ -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

View File

@@ -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,

View File

@@ -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"