src

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

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 }