Skip to content

[googleapis_auth] Support external_account credentials for Workload Identity Federation#727

Merged
kevmoo merged 1 commit intofeature_ADCfrom
wif
Feb 28, 2026
Merged

[googleapis_auth] Support external_account credentials for Workload Identity Federation#727
kevmoo merged 1 commit intofeature_ADCfrom
wif

Conversation

@kevmoo
Copy link
Collaborator

@kevmoo kevmoo commented Feb 28, 2026

Features:

  • Add support for external_account type in Application Default Credentials (ADC).
  • Add StsAuthClient and clientViaStsTokenExchange to handle token exchange with Google SecurityToken Service.
  • Implement file-sourced and url-sourced credentials for STS exchange.

Tests:

  • Add sts_auth_client_test.dart to verify token exchange functionality.
  • Update adc_test.dart to verify external_account flow.

…dentity Federation

Features:
- Add support for `external_account` type in Application Default Credentials (ADC).
- Add `StsAuthClient` and `clientViaStsTokenExchange` to handle token exchange with Google SecurityToken Service.
- Implement file-sourced and url-sourced credentials for STS exchange.

Tests:
- Add `sts_auth_client_test.dart` to verify token exchange functionality.
- Update `adc_test.dart` to verify `external_account` flow.
@kevmoo kevmoo changed the base branch from master to feature_ADC February 28, 2026 02:26
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the authentication capabilities of the googleapis_auth library by integrating support for impersonated service accounts and external account credentials, particularly for Workload Identity Federation. These changes allow for more flexible and secure authentication flows, enabling external workloads to access Google Cloud resources without directly managing service account keys. Additionally, it refines credential handling by ensuring proper propagation of quota project IDs.

Highlights

  • Application Default Credentials (ADC) Enhancement: Added support for impersonated_service_account source files within Application Default Credentials (ADC), allowing clients to obtain tokens for impersonated service accounts.
  • Workload Identity Federation (WIF) Support: Implemented support for external_account source files in Application Default Credentials (ADC), enabling Workload Identity Federation using the Google Security Token Service (STS).
  • Quota Project ID Propagation: Ensured that quota_project_id is now propagated correctly for Service Account credentials within Application Default Credentials (ADC).

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • googleapis_auth/CHANGELOG.md
    • Added support for impersonated_service_account source files in Application Default Credentials (ADC).
    • Added support for external_account source files for Workload Identity Federation using Google Security Token Service in Application Default Credentials (ADC).
    • Added propagation of quota_project_id for Service Account credentials in Application Default Credentials (ADC).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@kevmoo kevmoo changed the title wif [googleapis_auth] Support external_account credentials for Workload Identity Federation Feb 28, 2026
@github-actions
Copy link

PR Health

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
googleapis_auth Non-Breaking 2.1.0 2.2.0-wip 2.2.0-wip ✔️

This check can be disabled by tagging the PR with skip-breaking-check.

Unused Dependencies ✔️
Package Status
googleapis_auth ✔️ All dependencies utilized correctly.

For details on how to fix these, see dependency_validator.

This check can be disabled by tagging the PR with skip-unused-dependencies-check.

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

License Headers ✔️
//
Files
no missing headers

All source files should start with a license header.

This check can be disabled by tagging the PR with skip-license-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for external_account credentials for Workload Identity Federation, which is a valuable addition. The implementation introduces a new StsAuthClient to handle the STS token exchange and integrates it into the Application Default Credentials flow. The changes are well-tested. I have one suggestion to improve the robustness of token retrieval from the credential source.

Comment on lines +119 to +157
Future<String> _getSubjectToken() async {
if (_credentialSource.containsKey('file')) {
final fileField = _credentialSource['file'] as String;
return await File(fileField).readAsString();
} else if (_credentialSource.containsKey('url')) {
final url = _credentialSource['url'] as String;
final headers = _credentialSource['headers'] as Map<String, dynamic>?;
final format = _credentialSource['format'] as Map<String, dynamic>?;

final parsedHeaders = headers?.map(
(key, value) => MapEntry(key, value.toString()),
);

final response = await baseClient.get(
Uri.parse(url),
headers: parsedHeaders,
);

if (response.statusCode != 200) {
throw Exception(
'Failed to retrieve subject token from URL: $url. '
'Status code: ${response.statusCode}, Body: ${response.body}',
);
}

var token = response.body;

if (format != null && format['type'] == 'json') {
final json = jsonDecode(token) as Map<String, dynamic>;
final subjectTokenFieldName =
format['subject_token_field_name'] as String;
token = json[subjectTokenFieldName] as String;
}
return token;
}
throw UnsupportedError(
'Unsupported credential source type. Must provide file or url.',
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _getSubjectToken method uses unsafe casts (as) which can lead to runtime errors if the credential_source map has an unexpected structure. It would be more robust to use pattern matching to safely access and cast values from the map. Additionally, throwing a generic Exception for HTTP failures is not ideal; a more specific exception like ServerRequestFailedException would be more consistent with the rest of the codebase and provide better error handling for consumers.

  Future<String> _getSubjectToken() async {
    final source = _credentialSource;
    if (source case {'file': final String file}) {
      return await File(file).readAsString();
    } else if (source case {'url': final String url}) {
      final headers = switch (source['headers']) {
        final Map<String, dynamic> h =>
          h.map((key, value) => MapEntry(key, value.toString())),
        _ => null,
      };

      final response = await baseClient.get(
        Uri.parse(url),
        headers: headers,
      );

      if (response.statusCode != 200) {
        throw ServerRequestFailedException(
          'Failed to retrieve subject token from URL: $url. '
          'Status code: ${response.statusCode}',
          responseContent: response.body,
          statusCode: response.statusCode,
        );
      }

      var token = response.body;

      if (source['format']
          case {
            'type': 'json',
            'subject_token_field_name': final String fieldName
          }) {
        final json = jsonDecode(token) as Map<String, dynamic>;
        if (json[fieldName] case final String subjectToken) {
          token = subjectToken;
        } else {
          throw ArgumentError(
            'Subject token field "$fieldName" not found in JSON response.',
          );
        }
      }
      return token;
    }
    throw UnsupportedError(
      'Unsupported credential source type. Must provide file or url.',
    );
  }

@kevmoo kevmoo merged commit a83d1a4 into feature_ADC Feb 28, 2026
32 of 34 checks passed
@kevmoo kevmoo deleted the wif branch February 28, 2026 18:38
Copy link
Collaborator Author

@kevmoo kevmoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the feedback regarding pattern matching in _getSubjectToken and exception types. Thanks for the suggestions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant