...

Source file src/github.com/MicahParks/keyfunc/options.go

Documentation: github.com/MicahParks/keyfunc

     1  package keyfunc
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"time"
    11  )
    12  
    13  // ErrInvalidHTTPStatusCode indicates that the HTTP status code is invalid.
    14  var ErrInvalidHTTPStatusCode = errors.New("invalid HTTP status code")
    15  
    16  // Options represents the configuration options for a JWKS.
    17  //
    18  // If RefreshInterval and or RefreshUnknownKID is not nil, then a background goroutine will be launched to refresh the
    19  // remote JWKS under the specified circumstances.
    20  //
    21  // When using a background refresh goroutine, make sure to use RefreshRateLimit if paired with RefreshUnknownKID. Also
    22  // make sure to end the background refresh goroutine with the JWKS.EndBackground method when it's no longer needed.
    23  type Options struct {
    24  	// Client is the HTTP client used to get the JWKS via HTTP.
    25  	Client *http.Client
    26  
    27  	// Ctx is the context for the keyfunc's background refresh. When the context expires or is canceled, the background
    28  	// goroutine will end.
    29  	Ctx context.Context
    30  
    31  	// GivenKeys is a map of JWT key IDs, `kid`, to their given keys. If the JWKS has a background refresh goroutine,
    32  	// these values persist across JWKS refreshes. By default, if the remote JWKS resource contains a key with the same
    33  	// `kid` any given keys with the same `kid` will be overwritten by the keys from the remote JWKS. Use the
    34  	// GivenKIDOverride option to flip this behavior.
    35  	GivenKeys map[string]GivenKey
    36  
    37  	// GivenKIDOverride will make a GivenKey override any keys with the same ID (`kid`) in the remote JWKS. The is only
    38  	// effectual if GivenKeys is provided.
    39  	GivenKIDOverride bool
    40  
    41  	// JWKUseWhitelist is a whitelist of JWK `use` parameter values that will restrict what keys can be returned for
    42  	// jwt.Keyfunc. The assumption is that jwt.Keyfunc is only used for JWT signature verification.
    43  	// The default behavior is to only return a JWK if its `use` parameter has the value `"sig"`, an empty string, or if
    44  	// the parameter was omitted entirely.
    45  	JWKUseWhitelist []JWKUse
    46  
    47  	// JWKUseNoWhitelist overrides the JWKUseWhitelist field and its default behavior. If set to true, all JWKs will be
    48  	// returned regardless of their `use` parameter value.
    49  	JWKUseNoWhitelist bool
    50  
    51  	// RefreshErrorHandler is a function that consumes errors that happen during a JWKS refresh. This is only effectual
    52  	// if a background refresh goroutine is active.
    53  	RefreshErrorHandler ErrorHandler
    54  
    55  	// RefreshInterval is the duration to refresh the JWKS in the background via a new HTTP request. If this is not nil,
    56  	// then a background goroutine will be used to refresh the JWKS once per the given interval. Make sure to call the
    57  	// JWKS.EndBackground method to end this goroutine when it's no longer needed.
    58  	RefreshInterval time.Duration
    59  
    60  	// RefreshRateLimit limits the rate at which refresh requests are granted. Only one refresh request can be queued
    61  	// at a time any refresh requests received while there is already a queue are ignored. It does not make sense to
    62  	// have RefreshInterval's value shorter than this.
    63  	RefreshRateLimit time.Duration
    64  
    65  	// RefreshTimeout is the duration for the context timeout used to create the HTTP request for a refresh of the JWKS.
    66  	// This defaults to one minute. This is used for the HTTP request and any background goroutine refreshes.
    67  	RefreshTimeout time.Duration
    68  
    69  	// RefreshUnknownKID indicates that the JWKS refresh request will occur every time a kid that isn't cached is seen.
    70  	// This is done through a background goroutine. Without specifying a RefreshInterval a malicious client could
    71  	// self-sign X JWTs, send them to this service, then cause potentially high network usage proportional to X. Make
    72  	// sure to call the JWKS.EndBackground method to end this goroutine when it's no longer needed.
    73  	RefreshUnknownKID bool
    74  
    75  	// RequestFactory creates HTTP requests for the remote JWKS resource located at the given url. For example, an
    76  	// HTTP header could be added to indicate a User-Agent.
    77  	RequestFactory func(ctx context.Context, url string) (*http.Request, error)
    78  
    79  	// ResponseExtractor consumes a *http.Response and produces the raw JSON for the JWKS. By default, the
    80  	// ResponseExtractorStatusOK function is used. The default behavior changed in v1.4.0.
    81  	ResponseExtractor func(ctx context.Context, resp *http.Response) (json.RawMessage, error)
    82  }
    83  
    84  // ResponseExtractorStatusOK is meant to be used as the ResponseExtractor field for Options. It confirms that response
    85  // status code is 200 OK and returns the raw JSON from the response body.
    86  func ResponseExtractorStatusOK(ctx context.Context, resp *http.Response) (json.RawMessage, error) {
    87  	//goland:noinspection GoUnhandledErrorResult
    88  	defer resp.Body.Close()
    89  	if resp.StatusCode != http.StatusOK {
    90  		return nil, fmt.Errorf("%w: %d", ErrInvalidHTTPStatusCode, resp.StatusCode)
    91  	}
    92  	return io.ReadAll(resp.Body)
    93  }
    94  
    95  // ResponseExtractorStatusAny is meant to be used as the ResponseExtractor field for Options. It returns the raw JSON
    96  // from the response body regardless of the response status code.
    97  func ResponseExtractorStatusAny(ctx context.Context, resp *http.Response) (json.RawMessage, error) {
    98  	//goland:noinspection GoUnhandledErrorResult
    99  	defer resp.Body.Close()
   100  	return io.ReadAll(resp.Body)
   101  }
   102  
   103  // applyOptions applies the given options to the given JWKS.
   104  func applyOptions(jwks *JWKS, options Options) {
   105  	if options.Ctx != nil {
   106  		jwks.ctx, jwks.cancel = context.WithCancel(options.Ctx)
   107  	}
   108  
   109  	if options.GivenKeys != nil {
   110  		jwks.givenKeys = make(map[string]GivenKey)
   111  		for kid, key := range options.GivenKeys {
   112  			jwks.givenKeys[kid] = key
   113  		}
   114  	}
   115  
   116  	if !options.JWKUseNoWhitelist {
   117  		jwks.jwkUseWhitelist = make(map[JWKUse]struct{})
   118  		for _, use := range options.JWKUseWhitelist {
   119  			jwks.jwkUseWhitelist[use] = struct{}{}
   120  		}
   121  	}
   122  
   123  	jwks.client = options.Client
   124  	jwks.givenKIDOverride = options.GivenKIDOverride
   125  	jwks.refreshErrorHandler = options.RefreshErrorHandler
   126  	jwks.refreshInterval = options.RefreshInterval
   127  	jwks.refreshRateLimit = options.RefreshRateLimit
   128  	jwks.refreshTimeout = options.RefreshTimeout
   129  	jwks.refreshUnknownKID = options.RefreshUnknownKID
   130  	jwks.requestFactory = options.RequestFactory
   131  	jwks.responseExtractor = options.ResponseExtractor
   132  }
   133  

View as plain text