refactor(portal): Use appropriate access token for Google IdP (#8478)

Why:

* Previously, when running a directory sync with the Google Workspace
IdP adapter, if a service account had been configured but there was a
problem getting an access token for the service account, the sync job
would fall back to using a personal access token. We no longer want to
rely on any personal access token once a service account has been
configured. This commit will make sure that if a service account is
configured there is no way to fall back to any personal access token.


Fixes #8409
This commit is contained in:
Brian Manifold
2025-03-18 09:46:08 -07:00
committed by GitHub
parent 883c38cd3c
commit e14e5c4008
4 changed files with 47 additions and 37 deletions

View File

@@ -71,7 +71,39 @@ defmodule Domain.Auth.Adapters.GoogleWorkspace do
OpenIDConnect.sign_out(provider, identity, redirect_url)
end
def fetch_service_account_token(%Provider{} = provider) do
def fetch_access_token(%Provider{} = provider) do
case GoogleWorkspace.fetch_service_account_access_token(provider) do
{:ok, access_token} ->
{:ok, access_token}
{:error, :missing_service_account_key} ->
{:ok, provider.adapter_state["access_token"]}
{:error, {401, _response} = reason} ->
Logger.warning("401 while fetching service account access token",
account_id: provider.account_id,
account_slug: provider.account.slug,
provider_id: provider.id,
provider_adapter: provider.adapter,
reason: inspect(reason)
)
{:error, reason}
{:error, reason} ->
Logger.error("Failed to fetch service account access token",
account_id: provider.account_id,
account_slug: provider.account.slug,
provider_id: provider.id,
provider_adapter: provider.adapter,
reason: inspect(reason)
)
{:error, reason}
end
end
def fetch_service_account_access_token(%Provider{} = provider) do
key = provider.adapter_config["service_account_json_key"]
sub = provider.adapter_state["userinfo"]["sub"]

View File

@@ -23,36 +23,16 @@ defmodule Domain.Auth.Adapters.GoogleWorkspace.Jobs.SyncDirectory do
end
def gather_provider_data(provider, task_supervisor_pid) do
access_token =
with {:ok, access_token} <- GoogleWorkspace.fetch_service_account_token(provider) do
access_token
else
{:error, :missing_service_account_key} ->
provider.adapter_state["access_token"]
case GoogleWorkspace.fetch_access_token(provider) do
{:ok, access_token} ->
gather_directory_data(task_supervisor_pid, access_token)
{:error, {401, _response} = reason} ->
Logger.warning("Failed to fetch service account token",
account_id: provider.account_id,
account_slug: provider.account.slug,
provider_id: provider.id,
provider_adapter: provider.adapter,
reason: inspect(reason)
)
provider.adapter_state["access_token"]
{:error, reason} ->
Logger.error("Failed to fetch service account token",
reason: inspect(reason),
account_id: provider.account_id,
account_slug: provider.account.slug,
provider_id: provider.id,
provider_adapter: provider.adapter
)
provider.adapter_state["access_token"]
end
{:error, reason} ->
{:error, "Failed to fetch access token", inspect(reason)}
end
end
defp gather_directory_data(task_supervisor_pid, access_token) do
async_results =
DirectorySync.run_async_requests(task_supervisor_pid,
users: fn ->

View File

@@ -48,7 +48,7 @@ defmodule Domain.Auth.Adapters.GoogleWorkspace.Jobs.SyncDirectoryTest do
%{req_headers: [{"authorization", "Bearer GOOGLE_0AUTH_ACCESS_TOKEN"} | _]}}
end
test "uses admin user token as a fallback when service account is not configured" do
test "does not use admin user token when service account is set" do
bypass = Bypass.open()
GoogleWorkspaceDirectory.override_token_endpoint("http://localhost:#{bypass.port}/")
@@ -65,14 +65,12 @@ defmodule Domain.Auth.Adapters.GoogleWorkspace.Jobs.SyncDirectoryTest do
end)
GoogleWorkspaceDirectory.override_endpoint_url("http://localhost:#{bypass.port}/")
GoogleWorkspaceDirectory.mock_groups_list_endpoint(bypass, [])
GoogleWorkspaceDirectory.mock_organization_units_list_endpoint(bypass, [])
GoogleWorkspaceDirectory.mock_users_list_endpoint(bypass, [])
{:ok, pid} = Task.Supervisor.start_link()
assert execute(%{task_supervisor: pid}) == :ok
assert_receive {:bypass_request,
refute_receive {:bypass_request,
%{req_headers: [{"authorization", "Bearer OIDC_ACCESS_TOKEN"} | _]}}
end

View File

@@ -139,7 +139,7 @@ defmodule Domain.Auth.Adapters.GoogleWorkspaceTest do
end
end
describe "fetch_service_account_token/1" do
describe "fetch_service_account_access_token/1" do
test "generates a valid JWT" do
Bypass.open()
|> Mocks.GoogleWorkspaceDirectory.mock_token_endpoint()
@@ -147,7 +147,7 @@ defmodule Domain.Auth.Adapters.GoogleWorkspaceTest do
{provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider()
provider = Repo.get!(Auth.Provider, provider.id)
assert fetch_service_account_token(provider) == {:ok, "GOOGLE_0AUTH_ACCESS_TOKEN"}
assert fetch_service_account_access_token(provider) == {:ok, "GOOGLE_0AUTH_ACCESS_TOKEN"}
end
test "returns error when API is not available" do
@@ -158,7 +158,7 @@ defmodule Domain.Auth.Adapters.GoogleWorkspaceTest do
{provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider()
provider = Repo.get!(Auth.Provider, provider.id)
assert fetch_service_account_token(provider) ==
assert fetch_service_account_access_token(provider) ==
{:error, %Mint.TransportError{reason: :econnrefused}}
end
end