github.com/openshift-online/ocm-sdk-go@v0.1.473/authentication/handler.go (about)

     1  /*
     2  Copyright (c) 2019 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8    http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package authentication
    18  
    19  import (
    20  	"context"
    21  	"crypto/rsa"
    22  	"crypto/tls"
    23  	"crypto/x509"
    24  	"encoding/base64"
    25  	"encoding/json"
    26  	"fmt"
    27  	"io"
    28  	"math/big"
    29  	"net/http"
    30  	"net/url"
    31  	"os"
    32  	"regexp"
    33  	"strings"
    34  	"sync"
    35  	"time"
    36  
    37  	"github.com/golang-jwt/jwt/v4"
    38  	"gopkg.in/yaml.v3"
    39  
    40  	"github.com/openshift-online/ocm-sdk-go/errors"
    41  	"github.com/openshift-online/ocm-sdk-go/logging"
    42  )
    43  
    44  // HandlerBuilder contains the data and logic needed to create a new authentication handler. Don't
    45  // create objects of this type directly, use the NewHandler function instead.
    46  type HandlerBuilder struct {
    47  	logger       logging.Logger
    48  	publicPaths  []string
    49  	keysFiles    []string
    50  	keysURLs     []string
    51  	keysCAs      *x509.CertPool
    52  	keysInsecure bool
    53  	aclFiles     []string
    54  	service      string
    55  	error        string
    56  	operationID  func(*http.Request) string
    57  	tolerance    time.Duration
    58  	cookie       string
    59  	next         http.Handler
    60  }
    61  
    62  // Handler is an HTTP handler that checks authentication using the JWT tokens from the authorization
    63  // header.
    64  type Handler struct {
    65  	logger        logging.Logger
    66  	publicPaths   []*regexp.Regexp
    67  	tokenParser   *jwt.Parser
    68  	keysFiles     []string
    69  	keysURLs      []string
    70  	keysClient    *http.Client
    71  	keys          *sync.Map
    72  	lastKeyReload time.Time
    73  	aclItems      map[string]*regexp.Regexp
    74  	service       string
    75  	error         string
    76  	operationID   func(*http.Request) string
    77  	tolerance     time.Duration
    78  	cookie        string
    79  	next          http.Handler
    80  }
    81  
    82  // NewHandler creates a builder that can then be configured and used to create authentication
    83  // handlers.
    84  func NewHandler() *HandlerBuilder {
    85  	return &HandlerBuilder{
    86  		cookie: defaultCookie,
    87  	}
    88  }
    89  
    90  // Logger sets the logger that the middleware will use to send messages to the log. This is
    91  // mandatory.
    92  func (b *HandlerBuilder) Logger(value logging.Logger) *HandlerBuilder {
    93  	b.logger = value
    94  	return b
    95  }
    96  
    97  // Public sets a regular expression that defines the parts of the URL space that considered public,
    98  // and therefore require no authentication. This method may be called multiple times and then all
    99  // the given regular expressions will be used to check what parts of the URL space are public.
   100  func (b *HandlerBuilder) Public(value string) *HandlerBuilder {
   101  	b.publicPaths = append(b.publicPaths, value)
   102  	return b
   103  }
   104  
   105  // KeysFile sets the location of a file containing a JSON web key set that will be used to verify
   106  // the signatures of the tokens. The keys from this file will be loaded when a token is received
   107  // containing an unknown key identifier.
   108  //
   109  // At least one keys file or one keys URL is mandatory.
   110  func (b *HandlerBuilder) KeysFile(value string) *HandlerBuilder {
   111  	if value != "" {
   112  		b.keysFiles = append(b.keysFiles, value)
   113  	}
   114  	return b
   115  }
   116  
   117  // KeysURL sets the URL containing a JSON web key set that will be used to verify the signatures of
   118  // the tokens. The keys from these URLs will be loaded when a token is received containing an
   119  // unknown key identifier.
   120  //
   121  // At least one keys file or one keys URL is mandatory.
   122  func (b *HandlerBuilder) KeysURL(value string) *HandlerBuilder {
   123  	if value != "" {
   124  		b.keysURLs = append(b.keysURLs, value)
   125  	}
   126  	return b
   127  }
   128  
   129  // KeysCAs sets the certificate authorities that will be trusted when verifying the certificate of
   130  // the web server where keys are loaded from.
   131  func (b *HandlerBuilder) KeysCAs(value *x509.CertPool) *HandlerBuilder {
   132  	b.keysCAs = value
   133  	return b
   134  }
   135  
   136  // KeysInsecure sets the flag that indicates that the certificate of the web server where the keys
   137  // are loaded from should not be checked. The default is false and changing it to true makes the
   138  // token verification insecure, so refrain from doing that in security sensitive environments.
   139  func (b *HandlerBuilder) KeysInsecure(value bool) *HandlerBuilder {
   140  	b.keysInsecure = value
   141  	return b
   142  }
   143  
   144  // ACLFile sets a file that contains items of the access control list. This should be a YAML file
   145  // with the following format:
   146  //
   147  //   - claim: email
   148  //     pattern: ^.*@redhat\.com$
   149  //
   150  //   - claim: sub
   151  //     pattern: ^f:b3f7b485-7184-43c8-8169-37bd6d1fe4aa:myuser$
   152  //
   153  // The claim field is the name of the claim of the JWT token that will be checked. The pattern field
   154  // is a regular expression. If the claim matches the regular expression then access will be allowed.
   155  //
   156  // If the ACL is empty then access will be allowed to all JWT tokens.
   157  //
   158  // If the ACL has at least one item then access will be allowed only to tokens that match at least
   159  // one of the items.
   160  func (b *HandlerBuilder) ACLFile(value string) *HandlerBuilder {
   161  	if value != "" {
   162  		b.aclFiles = append(b.aclFiles, value)
   163  	}
   164  	return b
   165  }
   166  
   167  // Next sets the HTTP handler that will be called when the authentication handler has authenticated
   168  // correctly the request. This is mandatory.
   169  func (b *HandlerBuilder) Next(value http.Handler) *HandlerBuilder {
   170  	b.next = value
   171  	return b
   172  }
   173  
   174  // Service sets the identifier of the service that will be used to generate error codes. For
   175  // example, if the value is `my_service` then the JSON for error responses will be like this:
   176  //
   177  //	{
   178  //		"kind": "Error",
   179  //		"id": "401",
   180  //		"href": "/api/clusters_mgmt/v1/errors/401",
   181  //		"code": "MY-SERVICE-401",
   182  //		"reason": "Bearer token is expired"
   183  //	}
   184  //
   185  // When this isn't explicitly provided the value will be extracted from the second segment of the
   186  // request path. For example, if the request URL is `/api/clusters_mgmt/v1/cluster` the value will
   187  // be `clusters_mgmt`.
   188  func (b *HandlerBuilder) Service(value string) *HandlerBuilder {
   189  	b.service = value
   190  	return b
   191  }
   192  
   193  // Error sets the error identifier that will be used to generate JSON error responses. For example,
   194  // if the value is `123` then the JSON for error responses will be like this:
   195  //
   196  //	{
   197  //		"kind": "Error",
   198  //		"id": "11",
   199  //		"href": "/api/clusters_mgmt/v1/errors/11",
   200  //		"code": "CLUSTERS-MGMT-11",
   201  //		"reason": "Bearer token is expired"
   202  //	}
   203  //
   204  // When this isn't explicitly provided the value will be `401`. Note that changing this doesn't
   205  // change the HTTP response status, that will always be 401.
   206  func (b *HandlerBuilder) Error(value string) *HandlerBuilder {
   207  	b.error = value
   208  	return b
   209  }
   210  
   211  // OperationID sets a function that will be called each time an error is detected, passing the
   212  // details of the request that caused the error. The value returned by the function will be included
   213  // in the `operation_id` field of the JSON error response. For example, if the function returns
   214  // `123` the generated JSON error response will be like this:
   215  //
   216  //	{
   217  //		"kind": "Error",
   218  //		"id": "401",
   219  //		"href": "/api/clusters_mgmt/v1/errors/401",
   220  //		"code": "CLUSTERS-MGMT-401",
   221  //		"reason": "Bearer token is expired".
   222  //		"operation_id": "123"
   223  //	}
   224  //
   225  // For example, if the operation identifier is available in an HTTP header named `X-Operation-ID`
   226  // then the handler can be configured like this to use it:
   227  //
   228  //	handler, err := authentication.NewHandler().
   229  //		Logger(logger).
   230  //		KeysURL("https://...").
   231  //		OperationID(func(r *http.Request) string {
   232  //			return r.Header.Get("X-Operation-ID")
   233  //		}).
   234  //		Next(next).
   235  //		Build()
   236  //	if err != nil {
   237  //		...
   238  //	}
   239  //
   240  // If the function returns an empty string then the `operation_id` field will not be added.
   241  //
   242  // By default there is no function configured for this, so no `operation_id` field will be added.
   243  func (b *HandlerBuilder) OperationID(value func(r *http.Request) string) *HandlerBuilder {
   244  	b.operationID = value
   245  	return b
   246  }
   247  
   248  // Tolerance sets the maximum time that a token will be considered valid after it has expired. For
   249  // example, to accept requests with tokens that have expired up to five minutes ago:
   250  //
   251  //	handler, err := authentication.NewHandler().
   252  //		Logger(logger).
   253  //		KeysURL("https://...").
   254  //		Tolerance(5 * time.Minute).
   255  //		Next(next).
   256  //		Build()
   257  //	if err != nil {
   258  //		...
   259  //	}
   260  //
   261  // The default value is zero tolerance.
   262  func (b *HandlerBuilder) Tolerance(value time.Duration) *HandlerBuilder {
   263  	b.tolerance = value
   264  	return b
   265  }
   266  
   267  // Cookie sets the name of the cookie where the bearer token will be extracted from when the
   268  // `Authorization` header isn't present. The default is `cs_jwt`.
   269  func (b *HandlerBuilder) Cookie(value string) *HandlerBuilder {
   270  	b.cookie = value
   271  	return b
   272  }
   273  
   274  // Build uses the data stored in the builder to create a new authentication handler.
   275  func (b *HandlerBuilder) Build() (handler *Handler, err error) {
   276  	// Check parameters:
   277  	if b.logger == nil {
   278  		err = fmt.Errorf("logger is mandatory")
   279  		return
   280  	}
   281  	if b.tolerance < 0 {
   282  		err = fmt.Errorf("tolerance must be zero or positive")
   283  		return
   284  	}
   285  	if b.next == nil {
   286  		err = fmt.Errorf("next handler is mandatory")
   287  		return
   288  	}
   289  
   290  	// Check that there is at least one keys source:
   291  	if len(b.keysFiles)+len(b.keysURLs) == 0 {
   292  		err = fmt.Errorf("at least one keys file or one keys URL must be configured")
   293  		return
   294  	}
   295  
   296  	// Check that all the configured keys files exist:
   297  	for _, file := range b.keysFiles {
   298  		var info os.FileInfo
   299  		info, err = os.Stat(file)
   300  		if err != nil {
   301  			err = fmt.Errorf("keys file '%s' doesn't exist: %w", file, err)
   302  			return
   303  		}
   304  		if !info.Mode().IsRegular() {
   305  			err = fmt.Errorf("keys file '%s' isn't a regular file", file)
   306  			return
   307  		}
   308  	}
   309  
   310  	// Check that all the configured keys URLs are valid HTTPS URLs:
   311  	for _, addr := range b.keysURLs {
   312  		var parsed *url.URL
   313  		parsed, err = url.Parse(addr)
   314  		if err != nil {
   315  			err = fmt.Errorf("keys URL '%s' isn't a valid URL: %w", addr, err)
   316  			return
   317  		}
   318  		if !strings.EqualFold(parsed.Scheme, "https") {
   319  			err = fmt.Errorf(
   320  				"keys URL '%s' doesn't use the HTTPS protocol: %w",
   321  				addr, err,
   322  			)
   323  		}
   324  	}
   325  
   326  	// Create the HTTP client that will be used to load the keys:
   327  	keysClient := &http.Client{
   328  		Transport: &http.Transport{
   329  			TLSClientConfig: &tls.Config{
   330  				RootCAs:            b.keysCAs,
   331  				InsecureSkipVerify: b.keysInsecure, // nolint
   332  			},
   333  		},
   334  	}
   335  
   336  	// Try to compile the regular expressions that define the parts of the URL space that are
   337  	// public:
   338  	public := make([]*regexp.Regexp, len(b.publicPaths))
   339  	for i, expr := range b.publicPaths {
   340  		public[i], err = regexp.Compile(expr)
   341  		if err != nil {
   342  			return
   343  		}
   344  	}
   345  
   346  	// Create the bearer token parser:
   347  	tokenParser := &jwt.Parser{}
   348  
   349  	// Make copies of the lists of keys files and URLs:
   350  	keysFiles := make([]string, len(b.keysFiles))
   351  	copy(keysFiles, b.keysFiles)
   352  	keysURLs := make([]string, len(b.keysURLs))
   353  	copy(keysURLs, b.keysURLs)
   354  
   355  	// Create the initial empty map of keys:
   356  	keys := &sync.Map{}
   357  
   358  	// Load the ACL files:
   359  	aclItems := map[string]*regexp.Regexp{}
   360  	for _, file := range b.aclFiles {
   361  		err = b.loadACLFile(file, aclItems)
   362  		if err != nil {
   363  			return
   364  		}
   365  	}
   366  
   367  	// Create and populate the object:
   368  	handler = &Handler{
   369  		logger:      b.logger,
   370  		publicPaths: public,
   371  		tokenParser: tokenParser,
   372  		keysFiles:   keysFiles,
   373  		keysURLs:    keysURLs,
   374  		keysClient:  keysClient,
   375  		keys:        keys,
   376  		aclItems:    aclItems,
   377  		service:     b.service,
   378  		error:       b.error,
   379  		operationID: b.operationID,
   380  		tolerance:   b.tolerance,
   381  		cookie:      b.cookie,
   382  		next:        b.next,
   383  	}
   384  
   385  	return
   386  }
   387  
   388  // aclItem is the type used to read a single ACL item from a YAML document.
   389  type aclItem struct {
   390  	Claim   string `yaml:"claim"`
   391  	Pattern string `yaml:"pattern"`
   392  }
   393  
   394  // loadACLFile loads the given ACL file into the given map of ACL items.
   395  func (b *HandlerBuilder) loadACLFile(file string, items map[string]*regexp.Regexp) error {
   396  	// Load the YAML data:
   397  	yamlData, err := os.ReadFile(file) // nolint
   398  	if err != nil {
   399  		return err
   400  	}
   401  
   402  	// Parse the YAML data:
   403  	var listData []aclItem
   404  	err = yaml.Unmarshal(yamlData, &listData)
   405  	if err != nil {
   406  		return err
   407  	}
   408  
   409  	// Process the items:
   410  	for _, itemData := range listData {
   411  		items[itemData.Claim], err = regexp.Compile(itemData.Pattern)
   412  		if err != nil {
   413  			return err
   414  		}
   415  	}
   416  
   417  	return nil
   418  }
   419  
   420  // ServeHTTP is the implementation of the HTTP handler interface.
   421  func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   422  	// Get the context:
   423  	ctx := r.Context()
   424  
   425  	// Check if the requested path is public, and skip authentication if it is:
   426  	for _, expr := range h.publicPaths {
   427  		if expr.MatchString(r.URL.Path) {
   428  			h.next.ServeHTTP(w, r)
   429  			return
   430  		}
   431  	}
   432  
   433  	// Try to extract the credentials from the `Authorization` header:
   434  	var bearer string
   435  	header := r.Header.Get("Authorization")
   436  	if header != "" {
   437  		matches := bearerRE.FindStringSubmatch(header)
   438  		if len(matches) != 3 {
   439  			h.sendError(
   440  				w, r,
   441  				"Authorization header '%s' is malformed",
   442  				header,
   443  			)
   444  			return
   445  		}
   446  		scheme := matches[1]
   447  		if !strings.EqualFold(scheme, "Bearer") {
   448  			h.sendError(
   449  				w, r,
   450  				"Authentication type '%s' isn't supported",
   451  				scheme,
   452  			)
   453  			return
   454  		}
   455  		bearer = matches[2]
   456  	}
   457  
   458  	// If it wasn't possible to extract the credentials from the `Authorization` header then try
   459  	// to get them from the cookies:
   460  	if bearer == "" && h.cookie != "" {
   461  		for _, cookie := range r.Cookies() {
   462  			if cookie.Name == h.cookie {
   463  				bearer = cookie.Value
   464  			}
   465  		}
   466  	}
   467  
   468  	// Report an error if after tying headers and cookies we still don't have credentials:
   469  	if bearer == "" {
   470  		if h.cookie != "" {
   471  			h.sendError(
   472  				w, r,
   473  				"Request doesn't contain the 'Authorization' header or "+
   474  					"the '%s' cookie",
   475  				h.cookie,
   476  			)
   477  		} else {
   478  			h.sendError(
   479  				w, r,
   480  				"Request doesn't contain the 'Authorization' header",
   481  			)
   482  		}
   483  		return
   484  	}
   485  
   486  	// Use the JWT library to verify that the token is correctly signed and that the basic
   487  	// claims are correct:
   488  	token, claims, ok := h.checkToken(w, r, bearer)
   489  	if !ok {
   490  		return
   491  	}
   492  
   493  	// The library that we use considers tokens valid if the claims that it checks don't exist,
   494  	// but we want to reject those tokens, so we need to do some additional validations:
   495  	ok = h.checkClaims(w, r, claims)
   496  	if !ok {
   497  		return
   498  	}
   499  
   500  	// Check if the claims match at least one of the ACL items:
   501  	ok = h.checkACL(w, r, claims)
   502  	if !ok {
   503  		return
   504  	}
   505  
   506  	// Add the token to the context:
   507  	ctx = ContextWithToken(ctx, token.object)
   508  	r = r.WithContext(ctx)
   509  
   510  	// Call the next handler:
   511  	h.next.ServeHTTP(w, r)
   512  }
   513  
   514  // selectKey selects the key that should be used to verify the given token.
   515  func (h *Handler) selectKey(ctx context.Context, token *jwt.Token) (key interface{}, err error) {
   516  	// Get the key identifier:
   517  	value, ok := token.Header["kid"]
   518  	if !ok {
   519  		err = fmt.Errorf("token doesn't have a 'kid' field in the header")
   520  		return
   521  	}
   522  	kid, ok := value.(string)
   523  	if !ok {
   524  		err = fmt.Errorf(
   525  			"token has a 'kid' field, but it is a %T instead of a string",
   526  			value,
   527  		)
   528  		return
   529  	}
   530  
   531  	// Get the key for that key identifier. If there is no such key and we didn't reload keys
   532  	// recently then we try to reload them now.
   533  	key, ok = h.keys.Load(kid)
   534  	if !ok && time.Since(h.lastKeyReload) > 1*time.Minute {
   535  		err = h.loadKeys(ctx)
   536  		if err != nil {
   537  			return
   538  		}
   539  		h.lastKeyReload = time.Now()
   540  		key, ok = h.keys.Load(kid)
   541  	}
   542  	if !ok {
   543  		err = fmt.Errorf("there is no key for key identifier '%s'", kid)
   544  		return
   545  	}
   546  
   547  	return
   548  }
   549  
   550  // keyData is the type used to read a single key from a JSON document.
   551  type keyData struct {
   552  	Kid string `json:"kid"`
   553  	Kty string `json:"kty"`
   554  	Alg string `json:"alg"`
   555  	Use string `json:"use"`
   556  	N   string `json:"n"`
   557  	E   string `json:"e"`
   558  }
   559  
   560  // setData is the type used to read a collection of keys from a JSON document.
   561  type setData struct {
   562  	Keys []keyData `json:"keys"`
   563  }
   564  
   565  // loadKeys loads the JSON web key set from the URLs specified in the configuration.
   566  func (h *Handler) loadKeys(ctx context.Context) error {
   567  	// Load keys from the files given in the configuration:
   568  	for _, keysFile := range h.keysFiles {
   569  		h.logger.Info(ctx, "Loading keys from file '%s'", keysFile)
   570  		err := h.loadKeysFile(ctx, keysFile)
   571  		if err != nil {
   572  			h.logger.Error(ctx, "Can't load keys from file '%s': %v", keysFile, err)
   573  		}
   574  	}
   575  
   576  	// Load keys from URLs given in the configuration:
   577  	for _, keysURL := range h.keysURLs {
   578  		h.logger.Info(ctx, "Loading keys from URL '%s'", keysURL)
   579  		err := h.loadKeysURL(ctx, keysURL)
   580  		if err != nil {
   581  			h.logger.Error(ctx, "Can't load keys from URL '%s': %v", keysURL, err)
   582  		}
   583  	}
   584  
   585  	return nil
   586  }
   587  
   588  // loadKeysFile loads a JSON we key set from a file.
   589  func (h *Handler) loadKeysFile(ctx context.Context, file string) error {
   590  	reader, err := os.Open(file) // nolint
   591  	if err != nil {
   592  		return err
   593  	}
   594  	return h.readKeys(ctx, reader)
   595  }
   596  
   597  // loadKeysURL loads a JSON we key set from an URL.
   598  func (h *Handler) loadKeysURL(ctx context.Context, addr string) error {
   599  	request, err := http.NewRequest(http.MethodGet, addr, nil)
   600  	if err != nil {
   601  		return err
   602  	}
   603  	request = request.WithContext(ctx)
   604  	response, err := h.keysClient.Do(request)
   605  	if err != nil {
   606  		return err
   607  	}
   608  	defer func() {
   609  		err := response.Body.Close()
   610  		if err != nil {
   611  			h.logger.Error(
   612  				ctx,
   613  				"Can't close response body for request to '%s': %v",
   614  				addr, err,
   615  			)
   616  		}
   617  	}()
   618  	return h.readKeys(ctx, response.Body)
   619  }
   620  
   621  // readKeys reads the keys from JSON web key set available in the given reader.
   622  func (h *Handler) readKeys(ctx context.Context, reader io.Reader) error {
   623  	// Read the JSON data:
   624  	jsonData, err := io.ReadAll(reader)
   625  	if err != nil {
   626  		return err
   627  	}
   628  
   629  	// Parse the JSON data:
   630  	var setData setData
   631  	err = json.Unmarshal(jsonData, &setData)
   632  	if err != nil {
   633  		return err
   634  	}
   635  
   636  	// Convert the key data to actual keys that can be used to verify the signatures of the
   637  	// tokens:
   638  	for _, keyData := range setData.Keys {
   639  		if h.logger.DebugEnabled() {
   640  			h.logger.Debug(ctx, "Value of 'kid' is '%s'", keyData.Kid)
   641  			h.logger.Debug(ctx, "Value of 'kty' is '%s'", keyData.Kty)
   642  			h.logger.Debug(ctx, "Value of 'alg' is '%s'", keyData.Alg)
   643  			h.logger.Debug(ctx, "Value of 'e' is '%s'", keyData.E)
   644  			h.logger.Debug(ctx, "Value of 'n' is '%s'", keyData.N)
   645  		}
   646  		if keyData.Kid == "" {
   647  			h.logger.Error(ctx, "Can't read key because 'kid' is empty")
   648  			continue
   649  		}
   650  		if keyData.Kty == "" {
   651  			h.logger.Error(
   652  				ctx,
   653  				"Can't read key '%s' because 'kty' is empty",
   654  				keyData.Kid,
   655  			)
   656  			continue
   657  		}
   658  		if keyData.Alg == "" {
   659  			h.logger.Error(
   660  				ctx,
   661  				"Can't read key '%s' because 'alg' is empty",
   662  				keyData.Kid,
   663  			)
   664  			continue
   665  		}
   666  		if keyData.E == "" {
   667  			h.logger.Error(
   668  				ctx,
   669  				"Can't read key '%s' because 'e' is empty",
   670  				keyData.Kid,
   671  			)
   672  			continue
   673  		}
   674  		if keyData.E == "" {
   675  			h.logger.Error(
   676  				ctx,
   677  				"Can't read key '%s' because 'n' is empty",
   678  				keyData.Kid,
   679  			)
   680  			continue
   681  		}
   682  		var key interface{}
   683  		key, err = h.parseKey(keyData)
   684  		if err != nil {
   685  			h.logger.Error(
   686  				ctx,
   687  				"Key '%s' will be ignored because it can't be parsed",
   688  				keyData.Kid,
   689  			)
   690  			continue
   691  		}
   692  		h.keys.Store(keyData.Kid, key)
   693  		h.logger.Info(ctx, "Loaded key '%s'", keyData.Kid)
   694  	}
   695  
   696  	return nil
   697  }
   698  
   699  // parseKey converts the key data loaded from the JSON document to an actual key that can be used
   700  // to verify the signatures of tokens.
   701  func (h *Handler) parseKey(data keyData) (key interface{}, err error) {
   702  	// Check key type:
   703  	if data.Kty != "RSA" {
   704  		err = fmt.Errorf("key type '%s' isn't supported", data.Kty)
   705  		return
   706  	}
   707  
   708  	// Decode the e and n values:
   709  	nb, err := base64.RawURLEncoding.DecodeString(data.N)
   710  	if err != nil {
   711  		return
   712  	}
   713  	eb, err := base64.RawURLEncoding.DecodeString(data.E)
   714  	if err != nil {
   715  		return
   716  	}
   717  
   718  	// Create the key:
   719  	key = &rsa.PublicKey{
   720  		N: new(big.Int).SetBytes(nb),
   721  		E: int(new(big.Int).SetBytes(eb).Int64()),
   722  	}
   723  
   724  	return
   725  }
   726  
   727  // checkToken checks if the token is valid. If it is valid it returns the parsed token, the
   728  // claims and true. If it isn't valid it sends an error response to the client and returns false.
   729  func (h *Handler) checkToken(w http.ResponseWriter, r *http.Request,
   730  	bearer string) (token *tokenInfo, claims jwt.MapClaims, ok bool) {
   731  	// Get the context:
   732  	ctx := r.Context()
   733  
   734  	// Parse the token:
   735  	claims = jwt.MapClaims{}
   736  	object, err := h.tokenParser.ParseWithClaims(
   737  		bearer, claims,
   738  		func(token *jwt.Token) (key interface{}, err error) {
   739  			return h.selectKey(ctx, token)
   740  		},
   741  	)
   742  	token = &tokenInfo{
   743  		text:   bearer,
   744  		object: object,
   745  	}
   746  	if err != nil {
   747  		switch typed := err.(type) {
   748  		case *jwt.ValidationError:
   749  			switch {
   750  			case typed.Errors&jwt.ValidationErrorMalformed != 0:
   751  				h.sendError(
   752  					w, r,
   753  					"Bearer token is malformed",
   754  				)
   755  				ok = false
   756  			case typed.Errors&jwt.ValidationErrorUnverifiable != 0:
   757  				h.sendError(
   758  					w, r,
   759  					"Bearer token can't be verified",
   760  				)
   761  				ok = false
   762  			case typed.Errors&jwt.ValidationErrorSignatureInvalid != 0:
   763  				h.sendError(
   764  					w, r,
   765  					"Signature of bearer token isn't valid",
   766  				)
   767  				ok = false
   768  			case typed.Errors&jwt.ValidationErrorExpired != 0:
   769  				// When the token is expired according to the JWT library we may
   770  				// still want to accept it if we have a configured tolerance:
   771  				if h.tolerance > 0 {
   772  					var remaining time.Duration
   773  					_, remaining, err = tokenRemaining(token, time.Now())
   774  					if err != nil {
   775  						h.logger.Error(
   776  							ctx,
   777  							"Can't check token duration: %v",
   778  							err,
   779  						)
   780  						remaining = 0
   781  					}
   782  					if -remaining <= h.tolerance {
   783  						ok = true
   784  					} else {
   785  						h.sendError(
   786  							w, r,
   787  							"Bearer token is expired",
   788  						)
   789  						ok = false
   790  					}
   791  				} else {
   792  					h.sendError(
   793  						w, r,
   794  						"Bearer token is expired",
   795  					)
   796  					ok = false
   797  				}
   798  			case typed.Errors&jwt.ValidationErrorIssuedAt != 0:
   799  				h.sendError(
   800  					w, r,
   801  					"Bearer token was issued in the future",
   802  				)
   803  				ok = false
   804  			case typed.Errors&jwt.ValidationErrorNotValidYet != 0:
   805  				h.sendError(
   806  					w, r,
   807  					"Bearer token isn't valid yet",
   808  				)
   809  				ok = false
   810  			default:
   811  				h.sendError(
   812  					w, r,
   813  					"Bearer token isn't valid",
   814  				)
   815  				ok = false
   816  			}
   817  		default:
   818  			h.sendError(
   819  				w, r,
   820  				"Bearer token is malformed",
   821  			)
   822  			ok = false
   823  		}
   824  		return
   825  	}
   826  	ok = true
   827  	return
   828  }
   829  
   830  // checkClaims checks that the required claims are present and that they have valid values. If
   831  // something is wrong it sends an error response to the client and returns false.
   832  func (h *Handler) checkClaims(w http.ResponseWriter, r *http.Request,
   833  	claims jwt.MapClaims) bool {
   834  	// The `typ` claim is optional, but if it exists the value must be `Bearer`:
   835  	value, ok := claims["typ"]
   836  	if ok {
   837  		typ, ok := value.(string)
   838  		if !ok {
   839  			h.sendError(
   840  				w, r,
   841  				"Bearer token type claim contains incorrect string value '%v'",
   842  				value,
   843  			)
   844  			return false
   845  		}
   846  		if !strings.EqualFold(typ, "Bearer") {
   847  			h.sendError(
   848  				w, r,
   849  				"Bearer token type '%s' isn't allowed",
   850  				typ,
   851  			)
   852  			return false
   853  		}
   854  	}
   855  
   856  	// Check the format of the issue and expiration date claims:
   857  	_, ok = h.checkTimeClaim(w, r, claims, "iat")
   858  	if !ok {
   859  		return false
   860  	}
   861  	_, ok = h.checkTimeClaim(w, r, claims, "exp")
   862  	if !ok {
   863  		return false
   864  	}
   865  
   866  	// Make sure that the impersonation flag claim doesn't exist, or is `false`:
   867  	value, ok = claims["impersonated"]
   868  	if ok {
   869  		flag, ok := value.(bool)
   870  		if !ok {
   871  			h.sendError(
   872  				w, r,
   873  				"Impersonation claim contains incorrect boolean value '%v'",
   874  				value,
   875  			)
   876  			return false
   877  		}
   878  		if flag {
   879  			h.sendError(
   880  				w, r,
   881  				"Impersonation isn't allowed",
   882  			)
   883  			return false
   884  		}
   885  	}
   886  	return true
   887  }
   888  
   889  // checkTimeClaim checks that the given claim exists and that the value is a time. If it doesn't
   890  // exist or it has a wrong type it sends an error response to the client and returns false. If it
   891  // exists it returns its value and true.
   892  func (h *Handler) checkTimeClaim(w http.ResponseWriter, r *http.Request,
   893  	claims jwt.MapClaims, name string) (result time.Time, ok bool) {
   894  	value, ok := h.checkClaim(w, r, claims, name)
   895  	if !ok {
   896  		return
   897  	}
   898  	seconds, ok := value.(float64)
   899  	if !ok {
   900  		h.sendError(
   901  			w, r,
   902  			"Bearer token claim '%s' contains incorrect time value '%v'",
   903  			name, value,
   904  		)
   905  		return
   906  	}
   907  	result = time.Unix(int64(seconds), 0)
   908  	return
   909  }
   910  
   911  // checkClaim checks that the given claim exists. If it doesn't exist it sends an error response to
   912  // the client and returns false. If it exists it returns its value and true.
   913  func (h *Handler) checkClaim(w http.ResponseWriter, r *http.Request, claims jwt.MapClaims,
   914  	name string) (value interface{}, ok bool) {
   915  	value, ok = claims[name]
   916  	if !ok {
   917  		h.sendError(
   918  			w, r,
   919  			"Bearer token doesn't contain required claim '%s'",
   920  			name,
   921  		)
   922  		return
   923  	}
   924  	return
   925  }
   926  
   927  // checkACL checks if the given set of claims match at least one of the items of the access control
   928  // list. If there is no match it sends an error response to the client and returns false. If there
   929  // is a match or the ACL is empty it returns true.
   930  func (h *Handler) checkACL(w http.ResponseWriter, r *http.Request, claims jwt.MapClaims) bool {
   931  	// If there are no ACL items we consider that there are no restrictions, therefore we
   932  	// return true immediately:
   933  	if len(h.aclItems) == 0 {
   934  		return true
   935  	}
   936  
   937  	// Check all the ACL items:
   938  	for claim, pattern := range h.aclItems {
   939  		value, ok := claims[claim]
   940  		if !ok {
   941  			continue
   942  		}
   943  		text, ok := value.(string)
   944  		if !ok {
   945  			continue
   946  		}
   947  		if pattern.MatchString(text) {
   948  			return true
   949  		}
   950  	}
   951  
   952  	// No match, so the access is denied:
   953  	h.sendError(
   954  		w, r,
   955  		"Access denied",
   956  	)
   957  	return false
   958  }
   959  
   960  // sendError sends an error response to the client with the given status code and with a message
   961  // compossed using the given format and arguments as the fmt.Sprintf function does.
   962  func (h *Handler) sendError(w http.ResponseWriter, r *http.Request, format string, args ...interface{}) {
   963  	// Get the context:
   964  	ctx := r.Context()
   965  
   966  	// Prepare the body:
   967  	segments := strings.Split(r.URL.Path, "/")
   968  	realm := ""
   969  	builder := errors.NewError()
   970  	id := h.error
   971  	if id == "" {
   972  		id = fmt.Sprintf("%d", http.StatusUnauthorized)
   973  	}
   974  	builder.ID(id)
   975  	if len(segments) >= 4 {
   976  		service := h.service
   977  		if h.service == "" {
   978  			service = segments[2]
   979  		}
   980  		version := segments[3]
   981  		builder.HREF(fmt.Sprintf(
   982  			"/%s/%s/%s/errors/%s",
   983  			segments[1], segments[2], segments[3], id,
   984  		))
   985  		builder.Code(fmt.Sprintf(
   986  			"%s-%s",
   987  			strings.ToUpper(strings.ReplaceAll(service, "_", "-")),
   988  			id,
   989  		))
   990  		realm = fmt.Sprintf("%s/%s", service, version)
   991  	}
   992  	builder.Reason(fmt.Sprintf(format, args...))
   993  	if h.operationID != nil {
   994  		operationID := h.operationID(r)
   995  		if operationID != "" {
   996  			builder.OperationID(operationID)
   997  		}
   998  	}
   999  	body, err := builder.Build()
  1000  	if err != nil {
  1001  		h.logger.Error(ctx, "Can't build error response: %v", err)
  1002  		errors.SendPanic(w, r)
  1003  	}
  1004  
  1005  	// Send the response:
  1006  	w.Header().Set("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\"", realm))
  1007  	w.Header().Set("Content-Type", "application/json")
  1008  	w.WriteHeader(http.StatusUnauthorized)
  1009  	err = errors.MarshalError(body, w)
  1010  	if err != nil {
  1011  		h.logger.Error(
  1012  			r.Context(),
  1013  			"Can't send response body for request '%s': %v",
  1014  			r.URL.Path,
  1015  			err,
  1016  		)
  1017  	}
  1018  }
  1019  
  1020  // Regular expression used to extract the bearer token from the authorization header:
  1021  var bearerRE = regexp.MustCompile(`^([a-zA-Z0-9]+)\s+(.*)$`)
  1022  
  1023  // Name of the cookie used to extract the bearer token when the `Authorization` header isn't
  1024  // part of the request
  1025  var defaultCookie = "cs_jwt"