or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

auth-handlers.mdclient-credentials.mdcore-oauth2.mdendpoints.mdgoogle-auth.mdgoogle-downscope.mdgoogle-external-account.mdindex.mdjira-oauth.mdjwt-jws.md
tile.json

google-external-account.mddocs/

Google External Account

Workload identity federation and workforce identity federation for accessing Google Cloud from external identity providers including AWS, Azure, and OIDC-compatible providers.

Package

import "golang.org/x/oauth2/google/externalaccount"

Overview

The externalaccount package enables Google Cloud authentication using credentials from external identity providers:

  • AWS IAM credentials
  • Azure Active Directory
  • OIDC identity providers
  • File-based credentials
  • URL-based credential sources
  • Executable-based credential sources

This eliminates the need for service account keys and enables secure workload identity federation.

Creating External Account Token Sources

func NewTokenSource(ctx context.Context, conf Config) (oauth2.TokenSource, error)

Returns an external account TokenSource using the provided configuration.

Configuration

type Config struct {
	// Audience is the STS audience containing the resource name for the workload
	// identity pool or workforce pool and provider identifier (required)
	Audience string

	// SubjectTokenType is the STS token type based on OAuth2.0 token exchange spec (required)
	SubjectTokenType string

	// TokenURL is the STS token exchange endpoint (optional)
	TokenURL string

	// TokenInfoURL is the token_info endpoint (optional)
	TokenInfoURL string

	// ServiceAccountImpersonationURL is the URL for service account impersonation (optional)
	ServiceAccountImpersonationURL string

	// ServiceAccountImpersonationLifetimeSeconds is the impersonation token lifetime
	// Default: 3600 (optional)
	ServiceAccountImpersonationLifetimeSeconds int

	// ClientSecret is required if token_info endpoint is used (optional)
	ClientSecret string

	// ClientID is required with ClientSecret (optional)
	ClientID string

	// CredentialSource contains information to retrieve the token (optional)
	CredentialSource *CredentialSource

	// QuotaProjectID is injected by gCloud (optional)
	QuotaProjectID string

	// Scopes contains desired scopes for the returned access token (optional)
	Scopes []string

	// WorkforcePoolUserProject is the workforce pool user project number (optional)
	WorkforcePoolUserProject string

	// SubjectTokenSupplier for OIDC/SAML credentials (optional)
	SubjectTokenSupplier SubjectTokenSupplier

	// AwsSecurityCredentialsSupplier for AWS credentials (optional)
	AwsSecurityCredentialsSupplier AwsSecurityCredentialsSupplier

	// UniverseDomain is the default service domain (optional)
	UniverseDomain string
}

Credential Sources

type CredentialSource struct {
	// File is the location for file sourced credentials (optional)
	File string `json:"file"`

	// URL is the URL to call for URL sourced credentials (optional)
	URL string `json:"url"`

	// Headers are headers to attach to URL requests
	Headers map[string]string `json:"headers"`

	// Executable configuration for executable sourced credentials (optional)
	Executable *ExecutableConfig `json:"executable"`

	// EnvironmentID for AWS sourced credentials (should start with "AWS") (optional)
	EnvironmentID string `json:"environment_id"`

	// RegionURL is the metadata URL to retrieve region from for EC2 AWS credentials
	RegionURL string `json:"region_url"`

	// RegionalCredVerificationURL is the AWS regional credential verification URL
	RegionalCredVerificationURL string `json:"regional_cred_verification_url"`

	// IMDSv2SessionTokenURL is the URL to retrieve session token for IMDSv2 in AWS
	IMDSv2SessionTokenURL string `json:"imdsv2_session_token_url"`

	// Format is the format type for the subject token (for File and URL sources)
	Format Format `json:"format"`
}

Executable Config

type ExecutableConfig struct {
	// Command is the full command to run (must be absolute path) (required)
	Command string `json:"command"`

	// TimeoutMillis is timeout duration in milliseconds (default: 30000) (optional)
	TimeoutMillis *int `json:"timeout_millis"`

	// OutputFile is absolute path to output file for caching (optional)
	OutputFile string `json:"output_file"`
}

Format

type Format struct {
	// Type should be either "text" or "json"
	Type string `json:"type"`

	// SubjectTokenFieldName is required for JSON format
	SubjectTokenFieldName string `json:"subject_token_field_name"`
}

Token Suppliers

Subject Token Supplier

type SubjectTokenSupplier interface {
	// SubjectToken returns a valid subject token or error
	// The token source does not cache, so implement caching in the supplier
	SubjectToken(ctx context.Context, options SupplierOptions) (string, error)
}

AWS Security Credentials Supplier

type AwsSecurityCredentialsSupplier interface {
	// AwsRegion returns the AWS region or error
	AwsRegion(ctx context.Context, options SupplierOptions) (string, error)

	// AwsSecurityCredentials returns valid AWS security credentials or error
	// Implement caching in the supplier to prevent multiple requests
	AwsSecurityCredentials(ctx context.Context, options SupplierOptions) (*AwsSecurityCredentials, error)
}

AWS Security Credentials

type AwsSecurityCredentials struct {
	// AccessKeyID is the AWS Access Key ID (required)
	AccessKeyID string `json:"AccessKeyID"`

	// SecretAccessKey is the AWS Secret Access Key (required)
	SecretAccessKey string `json:"SecretAccessKey"`

	// SessionToken is the AWS Session token for temporary credentials (optional)
	SessionToken string `json:"Token"`
}

Supplier Options

type SupplierOptions struct {
	// Audience is the requested audience for the external account credential
	Audience string

	// SubjectTokenType is the requested subject token type
	SubjectTokenType string
}

Example: AWS Workload Identity

package main

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"log"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google/externalaccount"
)

func main() {
	ctx := context.Background()

	// Load external account configuration from JSON file
	data, err := ioutil.ReadFile("aws-credentials-config.json")
	if err != nil {
		log.Fatal(err)
	}

	var config externalaccount.Config
	if err := json.Unmarshal(data, &config); err != nil {
		log.Fatal(err)
	}

	// Create token source
	ts, err := externalaccount.NewTokenSource(ctx, config)
	if err != nil {
		log.Fatal(err)
	}

	// Create authenticated client
	client := oauth2.NewClient(ctx, ts)

	// Use client for Google Cloud API requests
	resp, err := client.Get("https://cloudresourcemanager.googleapis.com/v1/projects")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	// ...
}

Example: Custom Subject Token Supplier

type CustomTokenSupplier struct {
	// Custom fields
}

func (s *CustomTokenSupplier) SubjectToken(ctx context.Context, options externalaccount.SupplierOptions) (string, error) {
	// Fetch token from your identity provider
	token, err := fetchTokenFromProvider(ctx)
	if err != nil {
		return "", err
	}
	return token, nil
}

// Use custom supplier
config := externalaccount.Config{
	Audience:             "//iam.googleapis.com/projects/PROJECT_NUM/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
	SubjectTokenType:     "urn:ietf:params:oauth:token-type:jwt",
	TokenURL:             "https://sts.googleapis.com/v1/token",
	SubjectTokenSupplier: &CustomTokenSupplier{},
	Scopes:               []string{"https://www.googleapis.com/auth/cloud-platform"},
}

ts, err := externalaccount.NewTokenSource(ctx, config)

Example: AWS Security Credentials Supplier

type AWSCredsSupplier struct {
	region      string
	credentials *externalaccount.AwsSecurityCredentials
	mu          sync.Mutex
}

func (s *AWSCredsSupplier) AwsRegion(ctx context.Context, options externalaccount.SupplierOptions) (string, error) {
	return s.region, nil
}

func (s *AWSCredsSupplier) AwsSecurityCredentials(ctx context.Context, options externalaccount.SupplierOptions) (*externalaccount.AwsSecurityCredentials, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Implement caching logic
	if s.credentials != nil && !s.isExpired() {
		return s.credentials, nil
	}

	// Fetch new credentials from AWS
	creds, err := s.fetchAWSCredentials(ctx)
	if err != nil {
		return nil, err
	}

	s.credentials = creds
	return creds, nil
}

// Use supplier
config := externalaccount.Config{
	Audience:                       "//iam.googleapis.com/projects/PROJECT_NUM/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
	SubjectTokenType:               "urn:ietf:params:aws:token-type:aws4_request",
	TokenURL:                       "https://sts.googleapis.com/v1/token",
	AwsSecurityCredentialsSupplier: &AWSCredsSupplier{region: "us-east-1"},
	Scopes:                         []string{"https://www.googleapis.com/auth/cloud-platform"},
}

Configuration File Format

External account configurations are typically stored in JSON files:

{
  "type": "external_account",
  "audience": "//iam.googleapis.com/projects/PROJECT_NUM/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
  "token_url": "https://sts.googleapis.com/v1/token",
  "credential_source": {
    "file": "/path/to/token/file"
  },
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SA_EMAIL:generateAccessToken"
}

Common Subject Token Types

// JWT tokens
"urn:ietf:params:oauth:token-type:jwt"

// ID tokens
"urn:ietf:params:oauth:token-type:id_token"

// SAML 2.0 assertions
"urn:ietf:params:oauth:token-type:saml2"

// AWS signatures
"urn:ietf:params:aws:token-type:aws4_request"

Security Considerations

  1. Validate Configurations: Always validate external credential configurations from untrusted sources
  2. Secure Token Storage: Protect subject tokens and credential files
  3. Implement Caching: Cache credentials in suppliers to prevent excessive requests
  4. Rotate Credentials: Implement regular credential rotation
  5. Monitor Access: Enable audit logging for external account access
  6. Scope Limitation: Request minimum necessary scopes
  7. Use Service Account Impersonation: For additional access control layer

Error Handling

ts, err := externalaccount.NewTokenSource(ctx, config)
if err != nil {
	log.Fatalf("Failed to create token source: %v", err)
}

token, err := ts.Token()
if err != nil {
	if authErr, ok := err.(*google.AuthenticationError); ok {
		if authErr.Temporary() {
			// Retry
		}
	}
	log.Fatalf("Failed to get token: %v", err)
}

Documentation