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"