Skip to content

Commit

Permalink
Support direct workload identity federation and more credential_sourc…
Browse files Browse the repository at this point in the history
…e options (#175)
  • Loading branch information
mcrumm authored Sep 22, 2024
1 parent a95e143 commit 9b98b51
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 3 deletions.
18 changes: 15 additions & 3 deletions lib/goth/token.ex
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,20 @@ defmodule Goth.Token do
{url, audience}
end

defp subject_token_from_credential_source(%{"file" => file, "format" => %{"type" => "text"}}) do
defp subject_token_from_credential_source(%{"file" => file, "format" => format}) do
binary = File.read!(file)

case format do
%{"type" => "text"} ->
binary

%{"type" => "json", "subject_token_field_name" => field} ->
binary |> Jason.decode!() |> Map.fetch!(field)
end
end

# the default file type if not specified is "text"
defp subject_token_from_credential_source(%{"file" => file}) do
File.read!(file)
end

Expand All @@ -402,9 +415,8 @@ defmodule Goth.Token do

defp handle_workload_identity_response(
{:ok, %{status: 200, body: body}},
%{source: {:workload_identity, credentials}} = config
%{source: {:workload_identity, %{"service_account_impersonation_url" => url}}} = config
) do
url = Map.get(credentials, "service_account_impersonation_url")
%{"access_token" => token, "token_type" => type} = Jason.decode!(body)

headers = [{"content-type", "text/json"}, {"Authorization", "#{type} #{token}"}]
Expand Down
13 changes: 13 additions & 0 deletions test/data/test-credentials-direct-workload-identity-json.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"audience": "//iam.googleapis.com/projects/my-project/locations/global/workloadIdentityPools/my-cluster/providers/my-provider",
"credential_source": {
"file": "test/data/workload-identity-token.json",
"format": {
"type": "json",
"subject_token_field_name": "token"
}
},
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"type": "external_account"
}
9 changes: 9 additions & 0 deletions test/data/test-credentials-direct-workload-identity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"audience": "//iam.googleapis.com/projects/my-project/locations/global/workloadIdentityPools/my-cluster/providers/my-provider",
"credential_source": {
"file": "test/data/workload-identity-token"
},
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"type": "external_account"
}
3 changes: 3 additions & 0 deletions test/data/workload-identity-token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"token": "workload-identity-token"
}
48 changes: 48 additions & 0 deletions test/goth/token_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,54 @@ defmodule Goth.TokenTest do
assert token.scope == nil
end

test "fetch/1 from direct workload identity" do
token_bypass = Bypass.open()

Bypass.expect(token_bypass, fn conn ->
assert conn.request_path == "/v1/token"

body = ~s|{"access_token":"dummy","expires_in":599,"token_type":"Bearer"}|
Plug.Conn.resp(conn, 200, body)
end)

credentials =
File.read!("test/data/test-credentials-direct-workload-identity.json")
|> Jason.decode!()
|> Map.put("token_url", "http://localhost:#{token_bypass.port}/v1/token")

config = %{
source: {:workload_identity, credentials}
}

{:ok, token} = Goth.Token.fetch(config)
assert token.token == "dummy"
assert token.scope == nil
end

test "fetch/1 from direct workload identity, json format" do
token_bypass = Bypass.open()

Bypass.expect(token_bypass, fn conn ->
assert conn.request_path == "/v1/token"

body = ~s|{"access_token":"dummy","expires_in":599,"token_type":"Bearer"}|
Plug.Conn.resp(conn, 200, body)
end)

credentials =
File.read!("test/data/test-credentials-direct-workload-identity-json.json")
|> Jason.decode!()
|> Map.put("token_url", "http://localhost:#{token_bypass.port}/v1/token")

config = %{
source: {:workload_identity, credentials}
}

{:ok, token} = Goth.Token.fetch(config)
assert token.token == "dummy"
assert token.scope == nil
end

defp random_service_account_credentials do
%{
"private_key" => random_private_key(),
Expand Down

0 comments on commit 9b98b51

Please sign in to comment.