Skip to content

Commit cfc39bb

Browse files
committed
feat: Add ADC support for ExternalAccountAuthorizedUserCredential.
1 parent be44dd0 commit cfc39bb

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
lines changed

Src/Support/Google.Apis.Auth.Tests/OAuth2/DefaultCredentialProviderTests.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ public class DefaultCredentialProviderTests
8181
private const string ImpersonatedServiceAccountCredentialFileName = "impersonated_service_account_credential.json";
8282
private const string ImpersonatedServiceAccountCredentialUniverseDomainFileName = "impersonated_service_account_credential_universe_domain.json";
8383
private const string ImpersonatedServiceAccountCredentialUniverseDomainDifferentFileName = "impersonated_service_account_credential_universe_domain_different.json";
84-
private const string RecursiveImpersonatedServiceAccountCredentialName = "recursive_impersonated_service_account_credential.json";
84+
private const string RecursiveImpersonatedServiceAccountCredentialFileName = "recursive_impersonated_service_account_credential.json";
85+
private const string ExternalAccountAuthorizedUserCredentialFileName = "external_account_authorized_user_credential.json";
8586

8687
public DefaultCredentialProviderTests()
8788
{
@@ -380,13 +381,33 @@ public async Task GetDefaultCredential_ImpersonatedCredential_UniverseDomain_Dif
380381
[Fact]
381382
public async Task GetDefaultCredential_RecursiveImpersonatedCredential_FromEnvironmentVariable()
382383
{
383-
credentialProvider.SetEnvironmentVariable(CredentialEnvironmentVariable, RecursiveImpersonatedServiceAccountCredentialName);
384+
credentialProvider.SetEnvironmentVariable(CredentialEnvironmentVariable, RecursiveImpersonatedServiceAccountCredentialFileName);
384385

385386
await Assert.ThrowsAsync<InvalidOperationException>(() => credentialProvider.GetDefaultCredentialAsync());
386387
}
387388

388389
#endregion
389390

391+
#region ExternalAccountAuthorizedUserCredential
392+
393+
[Fact]
394+
public async Task GetDefaultCredential_ExternalAccountAuthorizedUserCredential_FromEnvironmentVariable()
395+
{
396+
credentialProvider.SetEnvironmentVariable(CredentialEnvironmentVariable, ExternalAccountAuthorizedUserCredentialFileName);
397+
398+
var credential = await credentialProvider.GetDefaultCredentialAsync();
399+
400+
var externalCredential = Assert.IsType<ExternalAccountAuthorizedUserCredential>(credential.UnderlyingCredential);
401+
Assert.Equal("refreshToken", externalCredential.RefreshToken);
402+
Assert.Equal("https://sts.googleapis.com/v1/oauthtoken", externalCredential.TokenServerUrl);
403+
Assert.Equal("clientId", externalCredential.ClientId);
404+
Assert.Equal("clientSecret", externalCredential.ClientSecret);
405+
Assert.Equal("//iam.googleapis.com/locations/global/workforcePools/poolId/providers/providerId", externalCredential.Audience);
406+
Assert.Equal("https://sts.googleapis.com/v1/introspect", externalCredential.TokenInfoUrl);
407+
}
408+
409+
#endregion
410+
390411
#region Invalid Cases
391412

392413
/// <summary>Environment variable points to a non existant credential file.</summary>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "external_account_authorized_user",
3+
"audience": "//iam.googleapis.com/locations/global/workforcePools/poolId/providers/providerId",
4+
"refresh_token": "refreshToken",
5+
"token_url": "https://sts.googleapis.com/v1/oauthtoken",
6+
"token_info_url": "https://sts.googleapis.com/v1/introspect",
7+
"client_id": "clientId",
8+
"client_secret": "clientSecret"
9+
}

Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ internal GoogleCredential CreateDefaultCredentialFromParameters(JsonCredentialPa
216216
JsonCredentialParameters.ServiceAccountCredentialType => GoogleCredential.FromServiceAccountCredential(CreateServiceAccountCredentialFromParameters(credentialParameters)),
217217
JsonCredentialParameters.ExternalAccountCredentialType => new GoogleCredential(CreateExternalCredentialFromParameters(credentialParameters)),
218218
JsonCredentialParameters.ImpersonatedServiceAccountCredentialType => new GoogleCredential(CreateImpersonatedServiceAccountCredentialFromParameters(credentialParameters)),
219+
JsonCredentialParameters.ExternalAccountAuthorizedUserCredentialType => new GoogleCredential(CreateExternalAccountAuthorizedUserCredentialFromParemeters(credentialParameters)),
219220
_ => throw new InvalidOperationException($"Error creating credential from JSON or JSON parameters. Unrecognized credential type {credentialParameters.Type}."),
220221
};
221222

@@ -378,6 +379,29 @@ private ImpersonatedCredential CreateImpersonatedServiceAccountCredentialFromPar
378379
return impersonatedCredential;
379380
}
380381

382+
private ExternalAccountAuthorizedUserCredential CreateExternalAccountAuthorizedUserCredentialFromParemeters(JsonCredentialParameters parameters)
383+
{
384+
if (parameters.Type != JsonCredentialParameters.ExternalAccountAuthorizedUserCredentialType
385+
|| string.IsNullOrEmpty(parameters.TokenUrl)
386+
|| string.IsNullOrEmpty(parameters.RefreshToken)
387+
|| string.IsNullOrEmpty(parameters.ClientId)
388+
|| string.IsNullOrEmpty(parameters.ClientSecret))
389+
{
390+
throw new InvalidOperationException("JSON data does not represent a valid external account authorized user credential.");
391+
}
392+
393+
var initializer = new ExternalAccountAuthorizedUserCredential.Initializer(
394+
parameters.TokenUrl, parameters.RefreshToken, parameters.ClientId, parameters.ClientSecret)
395+
{
396+
Audience = parameters.Audience,
397+
QuotaProject = parameters.QuotaProject,
398+
TokenInfoUrl = parameters.TokenInfoUrl,
399+
UniverseDomain = parameters.UniverseDomain,
400+
};
401+
402+
return new ExternalAccountAuthorizedUserCredential(initializer);
403+
}
404+
381405
/// <summary>
382406
/// Returns platform-specific well known credential file path. This file is created by
383407
/// <a href="https://cloud.google.com/sdk/gcloud/reference/auth/login">gcloud auth login</a>

Src/Support/Google.Apis.Auth/OAuth2/JsonCredentialParameters.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public class JsonCredentialParameters
5252
/// </summary>
5353
public const string ExternalAccountCredentialType = "external_account";
5454

55+
/// <summary>
56+
/// OAuth2 credentials sourced using external identities through Workforce Identity Federation.
57+
/// Obtaining the initial access and refresh token can be done through the Google Cloud CLI.
58+
/// </summary>
59+
public const string ExternalAccountAuthorizedUserCredentialType = "external_account_authorized_user";
60+
5561
/// <summary>Type of the credential.</summary>
5662
[JsonProperty("type")]
5763
public string Type { get; set; }
@@ -174,6 +180,12 @@ public class JsonCredentialParameters
174180
[JsonProperty("token_url")]
175181
public string TokenUrl { get; set; }
176182

183+
/// <summary>
184+
/// Endpoint to retrieve information related to an external account authorized user, like email, username, etc.
185+
/// </summary>
186+
[JsonProperty("token_info_url")]
187+
public string TokenInfoUrl { get; set; }
188+
177189
/// <summary>
178190
/// The GCP project number to be used for Workforce Pools
179191
/// external credentials.

0 commit comments

Comments
 (0)