src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sso_credentials_provider.go (4628B)


      1 package ssocreds
      2 
      3 import (
      4 	"context"
      5 	"time"
      6 
      7 	"github.com/aws/aws-sdk-go-v2/aws"
      8 	"github.com/aws/aws-sdk-go-v2/internal/sdk"
      9 	"github.com/aws/aws-sdk-go-v2/service/sso"
     10 )
     11 
     12 // ProviderName is the name of the provider used to specify the source of
     13 // credentials.
     14 const ProviderName = "SSOProvider"
     15 
     16 // GetRoleCredentialsAPIClient is a API client that implements the
     17 // GetRoleCredentials operation.
     18 type GetRoleCredentialsAPIClient interface {
     19 	GetRoleCredentials(context.Context, *sso.GetRoleCredentialsInput, ...func(*sso.Options)) (
     20 		*sso.GetRoleCredentialsOutput, error,
     21 	)
     22 }
     23 
     24 // Options is the Provider options structure.
     25 type Options struct {
     26 	// The Client which is configured for the AWS Region where the AWS SSO user
     27 	// portal is located.
     28 	Client GetRoleCredentialsAPIClient
     29 
     30 	// The AWS account that is assigned to the user.
     31 	AccountID string
     32 
     33 	// The role name that is assigned to the user.
     34 	RoleName string
     35 
     36 	// The URL that points to the organization's AWS Single Sign-On (AWS SSO)
     37 	// user portal.
     38 	StartURL string
     39 
     40 	// The filepath the cached token will be retrieved from. If unset Provider will
     41 	// use the startURL to determine the filepath at.
     42 	//
     43 	//    ~/.aws/sso/cache/<sha1-hex-encoded-startURL>.json
     44 	//
     45 	// If custom cached token filepath is used, the Provider's startUrl
     46 	// parameter will be ignored.
     47 	CachedTokenFilepath string
     48 
     49 	// Used by the SSOCredentialProvider if a token configuration
     50 	// profile is used in the shared config
     51 	SSOTokenProvider *SSOTokenProvider
     52 }
     53 
     54 // Provider is an AWS credential provider that retrieves temporary AWS
     55 // credentials by exchanging an SSO login token.
     56 type Provider struct {
     57 	options Options
     58 
     59 	cachedTokenFilepath string
     60 }
     61 
     62 // New returns a new AWS Single Sign-On (AWS SSO) credential provider. The
     63 // provided client is expected to be configured for the AWS Region where the
     64 // AWS SSO user portal is located.
     65 func New(client GetRoleCredentialsAPIClient, accountID, roleName, startURL string, optFns ...func(options *Options)) *Provider {
     66 	options := Options{
     67 		Client:    client,
     68 		AccountID: accountID,
     69 		RoleName:  roleName,
     70 		StartURL:  startURL,
     71 	}
     72 
     73 	for _, fn := range optFns {
     74 		fn(&options)
     75 	}
     76 
     77 	return &Provider{
     78 		options:             options,
     79 		cachedTokenFilepath: options.CachedTokenFilepath,
     80 	}
     81 }
     82 
     83 // Retrieve retrieves temporary AWS credentials from the configured Amazon
     84 // Single Sign-On (AWS SSO) user portal by exchanging the accessToken present
     85 // in ~/.aws/sso/cache. However, if a token provider configuration exists
     86 // in the shared config, then we ought to use the token provider rather then
     87 // direct access on the cached token.
     88 func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
     89 	var accessToken *string
     90 	if p.options.SSOTokenProvider != nil {
     91 		token, err := p.options.SSOTokenProvider.RetrieveBearerToken(ctx)
     92 		if err != nil {
     93 			return aws.Credentials{}, err
     94 		}
     95 		accessToken = &token.Value
     96 	} else {
     97 		if p.cachedTokenFilepath == "" {
     98 			cachedTokenFilepath, err := StandardCachedTokenFilepath(p.options.StartURL)
     99 			if err != nil {
    100 				return aws.Credentials{}, &InvalidTokenError{Err: err}
    101 			}
    102 			p.cachedTokenFilepath = cachedTokenFilepath
    103 		}
    104 
    105 		tokenFile, err := loadCachedToken(p.cachedTokenFilepath)
    106 		if err != nil {
    107 			return aws.Credentials{}, &InvalidTokenError{Err: err}
    108 		}
    109 
    110 		if tokenFile.ExpiresAt == nil || sdk.NowTime().After(time.Time(*tokenFile.ExpiresAt)) {
    111 			return aws.Credentials{}, &InvalidTokenError{}
    112 		}
    113 		accessToken = &tokenFile.AccessToken
    114 	}
    115 
    116 	output, err := p.options.Client.GetRoleCredentials(ctx, &sso.GetRoleCredentialsInput{
    117 		AccessToken: accessToken,
    118 		AccountId:   &p.options.AccountID,
    119 		RoleName:    &p.options.RoleName,
    120 	})
    121 	if err != nil {
    122 		return aws.Credentials{}, err
    123 	}
    124 
    125 	return aws.Credentials{
    126 		AccessKeyID:     aws.ToString(output.RoleCredentials.AccessKeyId),
    127 		SecretAccessKey: aws.ToString(output.RoleCredentials.SecretAccessKey),
    128 		SessionToken:    aws.ToString(output.RoleCredentials.SessionToken),
    129 		CanExpire:       true,
    130 		Expires:         time.Unix(0, output.RoleCredentials.Expiration*int64(time.Millisecond)).UTC(),
    131 		Source:          ProviderName,
    132 	}, nil
    133 }
    134 
    135 // InvalidTokenError is the error type that is returned if loaded token has
    136 // expired or is otherwise invalid. To refresh the SSO session run AWS SSO
    137 // login with the corresponding profile.
    138 type InvalidTokenError struct {
    139 	Err error
    140 }
    141 
    142 func (i *InvalidTokenError) Unwrap() error {
    143 	return i.Err
    144 }
    145 
    146 func (i *InvalidTokenError) Error() string {
    147 	const msg = "the SSO session has expired or is invalid"
    148 	if i.Err == nil {
    149 		return msg
    150 	}
    151 	return msg + ": " + i.Err.Error()
    152 }