1// Package endpointcreds provides support for retrieving credentials from an 2// arbitrary HTTP endpoint. 3// 4// The credentials endpoint Provider can receive both static and refreshable 5// credentials that will expire. Credentials are static when an "Expiration" 6// value is not provided in the endpoint's response. 7// 8// Static credentials will never expire once they have been retrieved. The format 9// of the static credentials response: 10// { 11// "AccessKeyId" : "MUA...", 12// "SecretAccessKey" : "/7PC5om....", 13// } 14// 15// Refreshable credentials will expire within the "ExpiryWindow" of the Expiration 16// value in the response. The format of the refreshable credentials response: 17// { 18// "AccessKeyId" : "MUA...", 19// "SecretAccessKey" : "/7PC5om....", 20// "Token" : "AQoDY....=", 21// "Expiration" : "2016-02-25T06:03:31Z" 22// } 23// 24// Errors should be returned in the following format and only returned with 400 25// or 500 HTTP status codes. 26// { 27// "code": "ErrorCode", 28// "message": "Helpful error message." 29// } 30package endpointcreds 31 32import ( 33 "context" 34 "fmt" 35 "net/http" 36 37 "github.com/aws/aws-sdk-go-v2/aws" 38 "github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client" 39 "github.com/aws/smithy-go/middleware" 40) 41 42// ProviderName is the name of the credentials provider. 43const ProviderName = `CredentialsEndpointProvider` 44 45type getCredentialsAPIClient interface { 46 GetCredentials(context.Context, *client.GetCredentialsInput, ...func(*client.Options)) (*client.GetCredentialsOutput, error) 47} 48 49// Provider satisfies the aws.CredentialsProvider interface, and is a client to 50// retrieve credentials from an arbitrary endpoint. 51type Provider struct { 52 // The AWS Client to make HTTP requests to the endpoint with. The endpoint 53 // the request will be made to is provided by the aws.Config's 54 // EndpointResolver. 55 client getCredentialsAPIClient 56 57 options Options 58} 59 60// HTTPClient is a client for sending HTTP requests 61type HTTPClient interface { 62 Do(*http.Request) (*http.Response, error) 63} 64 65// Options is structure of configurable options for Provider 66type Options struct { 67 // Endpoint to retrieve credentials from. Required 68 Endpoint string 69 70 // HTTPClient to handle sending HTTP requests to the target endpoint. 71 HTTPClient HTTPClient 72 73 // Set of options to modify how the credentials operation is invoked. 74 APIOptions []func(*middleware.Stack) error 75 76 // The Retryer to be used for determining whether a failed requested should be retried 77 Retryer aws.Retryer 78 79 // Optional authorization token value if set will be used as the value of 80 // the Authorization header of the endpoint credential request. 81 AuthorizationToken string 82} 83 84// New returns a credentials Provider for retrieving AWS credentials 85// from arbitrary endpoint. 86func New(endpoint string, optFns ...func(*Options)) *Provider { 87 o := Options{ 88 Endpoint: endpoint, 89 } 90 91 for _, fn := range optFns { 92 fn(&o) 93 } 94 95 p := &Provider{ 96 client: client.New(client.Options{ 97 HTTPClient: o.HTTPClient, 98 Endpoint: o.Endpoint, 99 APIOptions: o.APIOptions, 100 Retryer: o.Retryer, 101 }), 102 options: o, 103 } 104 105 return p 106} 107 108// Retrieve will attempt to request the credentials from the endpoint the Provider 109// was configured for. And error will be returned if the retrieval fails. 110func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) { 111 resp, err := p.getCredentials(ctx) 112 if err != nil { 113 return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err) 114 } 115 116 creds := aws.Credentials{ 117 AccessKeyID: resp.AccessKeyID, 118 SecretAccessKey: resp.SecretAccessKey, 119 SessionToken: resp.Token, 120 Source: ProviderName, 121 } 122 123 if resp.Expiration != nil { 124 creds.CanExpire = true 125 creds.Expires = *resp.Expiration 126 } 127 128 return creds, nil 129} 130 131func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) { 132 return p.client.GetCredentials(ctx, &client.GetCredentialsInput{AuthorizationToken: p.options.AuthorizationToken}) 133} 134