package config import ( "context" "fmt" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/credentials/ssocreds" "github.com/aws/aws-sdk-go-v2/service/ssooidc" smithybearer "github.com/aws/smithy-go/auth/bearer" ) // resolveBearerAuthToken extracts a token provider from the config sources. // // If an explicit bearer authentication token provider is not found the // resolver will fallback to resolving token provider via other config sources // such as SharedConfig. func resolveBearerAuthToken(ctx context.Context, cfg *aws.Config, configs configs) error { found, err := resolveBearerAuthTokenProvider(ctx, cfg, configs) if found || err != nil { return err } return resolveBearerAuthTokenProviderChain(ctx, cfg, configs) } // resolveBearerAuthTokenProvider extracts the first instance of // BearerAuthTokenProvider from the config sources. // // The resolved BearerAuthTokenProvider will be wrapped in a cache to ensure // the Token is only refreshed when needed. This also protects the // TokenProvider so it can be used concurrently. // // Config providers used: // * bearerAuthTokenProviderProvider func resolveBearerAuthTokenProvider(ctx context.Context, cfg *aws.Config, configs configs) (bool, error) { tokenProvider, found, err := getBearerAuthTokenProvider(ctx, configs) if !found || err != nil { return false, err } cfg.BearerAuthTokenProvider, err = wrapWithBearerAuthTokenCache( ctx, configs, tokenProvider) if err != nil { return false, err } return true, nil } func resolveBearerAuthTokenProviderChain(ctx context.Context, cfg *aws.Config, configs configs) (err error) { _, sharedConfig, _ := getAWSConfigSources(configs) var provider smithybearer.TokenProvider if sharedConfig.SSOSession != nil { provider, err = resolveBearerAuthSSOTokenProvider( ctx, cfg, sharedConfig.SSOSession, configs) } if err == nil && provider != nil { cfg.BearerAuthTokenProvider, err = wrapWithBearerAuthTokenCache( ctx, configs, provider) } return err } func resolveBearerAuthSSOTokenProvider(ctx context.Context, cfg *aws.Config, session *SSOSession, configs configs) (*ssocreds.SSOTokenProvider, error) { ssoTokenProviderOptionsFn, found, err := getSSOTokenProviderOptions(ctx, configs) if err != nil { return nil, fmt.Errorf("failed to get SSOTokenProviderOptions from config sources, %w", err) } var optFns []func(*ssocreds.SSOTokenProviderOptions) if found { optFns = append(optFns, ssoTokenProviderOptionsFn) } cachePath, err := ssocreds.StandardCachedTokenFilepath(session.Name) if err != nil { return nil, fmt.Errorf("failed to get SSOTokenProvider's cache path, %w", err) } client := ssooidc.NewFromConfig(*cfg) provider := ssocreds.NewSSOTokenProvider(client, cachePath, optFns...) return provider, nil } // wrapWithBearerAuthTokenCache will wrap provider with an smithy-go // bearer/auth#TokenCache with the provided options if the provider is not // already a TokenCache. func wrapWithBearerAuthTokenCache( ctx context.Context, cfgs configs, provider smithybearer.TokenProvider, optFns ...func(*smithybearer.TokenCacheOptions), ) (smithybearer.TokenProvider, error) { _, ok := provider.(*smithybearer.TokenCache) if ok { return provider, nil } tokenCacheConfigOptions, optionsFound, err := getBearerAuthTokenCacheOptions(ctx, cfgs) if err != nil { return nil, err } opts := make([]func(*smithybearer.TokenCacheOptions), 0, 2+len(optFns)) opts = append(opts, func(o *smithybearer.TokenCacheOptions) { o.RefreshBeforeExpires = 5 * time.Minute o.RetrieveBearerTokenTimeout = 30 * time.Second }) opts = append(opts, optFns...) if optionsFound { opts = append(opts, tokenCacheConfigOptions) } return smithybearer.NewTokenCache(provider, opts...), nil }