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 }