middleware.go (3535B)
1 package bearer 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/aws/smithy-go/middleware" 8 smithyhttp "github.com/aws/smithy-go/transport/http" 9 ) 10 11 // Message is the middleware stack's request transport message value. 12 type Message interface{} 13 14 // Signer provides an interface for implementations to decorate a request 15 // message with a bearer token. The signer is responsible for validating the 16 // message type is compatible with the signer. 17 type Signer interface { 18 SignWithBearerToken(context.Context, Token, Message) (Message, error) 19 } 20 21 // AuthenticationMiddleware provides the Finalize middleware step for signing 22 // an request message with a bearer token. 23 type AuthenticationMiddleware struct { 24 signer Signer 25 tokenProvider TokenProvider 26 } 27 28 // AddAuthenticationMiddleware helper adds the AuthenticationMiddleware to the 29 // middleware Stack in the Finalize step with the options provided. 30 func AddAuthenticationMiddleware(s *middleware.Stack, signer Signer, tokenProvider TokenProvider) error { 31 return s.Finalize.Add( 32 NewAuthenticationMiddleware(signer, tokenProvider), 33 middleware.After, 34 ) 35 } 36 37 // NewAuthenticationMiddleware returns an initialized AuthenticationMiddleware. 38 func NewAuthenticationMiddleware(signer Signer, tokenProvider TokenProvider) *AuthenticationMiddleware { 39 return &AuthenticationMiddleware{ 40 signer: signer, 41 tokenProvider: tokenProvider, 42 } 43 } 44 45 const authenticationMiddlewareID = "BearerTokenAuthentication" 46 47 // ID returns the resolver identifier 48 func (m *AuthenticationMiddleware) ID() string { 49 return authenticationMiddlewareID 50 } 51 52 // HandleFinalize implements the FinalizeMiddleware interface in order to 53 // update the request with bearer token authentication. 54 func (m *AuthenticationMiddleware) HandleFinalize( 55 ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, 56 ) ( 57 out middleware.FinalizeOutput, metadata middleware.Metadata, err error, 58 ) { 59 token, err := m.tokenProvider.RetrieveBearerToken(ctx) 60 if err != nil { 61 return out, metadata, fmt.Errorf("failed AuthenticationMiddleware wrap message, %w", err) 62 } 63 64 signedMessage, err := m.signer.SignWithBearerToken(ctx, token, in.Request) 65 if err != nil { 66 return out, metadata, fmt.Errorf("failed AuthenticationMiddleware sign message, %w", err) 67 } 68 69 in.Request = signedMessage 70 return next.HandleFinalize(ctx, in) 71 } 72 73 // SignHTTPSMessage provides a bearer token authentication implementation that 74 // will sign the message with the provided bearer token. 75 // 76 // Will fail if the message is not a smithy-go HTTP request or the request is 77 // not HTTPS. 78 type SignHTTPSMessage struct{} 79 80 // NewSignHTTPSMessage returns an initialized signer for HTTP messages. 81 func NewSignHTTPSMessage() *SignHTTPSMessage { 82 return &SignHTTPSMessage{} 83 } 84 85 // SignWithBearerToken returns a copy of the HTTP request with the bearer token 86 // added via the "Authorization" header, per RFC 6750, https://datatracker.ietf.org/doc/html/rfc6750. 87 // 88 // Returns an error if the request's URL scheme is not HTTPS, or the request 89 // message is not an smithy-go HTTP Request pointer type. 90 func (SignHTTPSMessage) SignWithBearerToken(ctx context.Context, token Token, message Message) (Message, error) { 91 req, ok := message.(*smithyhttp.Request) 92 if !ok { 93 return nil, fmt.Errorf("expect smithy-go HTTP Request, got %T", message) 94 } 95 96 if !req.IsHTTPS() { 97 return nil, fmt.Errorf("bearer token with HTTP request requires HTTPS") 98 } 99 100 reqClone := req.Clone() 101 reqClone.Header.Set("Authorization", "Bearer "+token.Value) 102 103 return reqClone, nil 104 }