middleware.go 3.5 KB

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