github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/http/fast/middleware/jwt/auth_jwt.go (about) 1 package jwt 2 3 import ( 4 "crypto/rsa" 5 "errors" 6 "github.com/angenalZZZ/gofunc/f" 7 "github.com/angenalZZZ/gofunc/g" 8 "github.com/angenalZZZ/gofunc/http/fast" 9 "github.com/dgrijalva/jwt-go" 10 "io/ioutil" 11 "net/http" 12 "strings" 13 "time" 14 ) 15 16 // Config defines the config for jwt middleware 17 // provides a Json-Web-Token authentication implementation. On failure, a 401 HTTP response 18 // is returned. On success, the wrapped middleware is called, and the userID is made available as c.Get("userID").(string). 19 // Users can get a token by posting a json request to LoginHandler. The token then needs to be passed in 20 // the Authentication header. Example: Authorization:Bearer XXX_TOKEN_XXX 21 type Config struct { 22 // Filter defines a function to skip middleware. 23 // Optional. Default: nil 24 Filter func(*fast.Ctx) bool 25 26 // Realm name to display to the user. Required. 27 Realm string 28 29 // signing algorithm - possible values are HS256, HS384, HS512 30 // Optional, default is HS256. 31 SigningAlgorithm string 32 33 // Secret key used for signing. Required. 34 Key []byte 35 36 // Duration that a jwt token is valid. Optional, defaults to one hour. 37 Timeout time.Duration 38 39 // This field allows clients to refresh their token until MaxRefresh has passed. 40 // Note that clients can refresh their token in the last moment of MaxRefresh. 41 // This means that the maximum validity timespan for a token is TokenTime + MaxRefresh. 42 // Optional, defaults to 0 meaning not refreshable. 43 MaxRefresh time.Duration 44 45 // Callback function that should perform the authentication of the user based on login info. 46 // Must return user data as user identifier, it will be stored in Claim Array. Required. 47 // Check error (e) to determine the appropriate error message. 48 Authenticator func(*fast.Ctx) (interface{}, error) 49 50 // Callback function that should perform the authorization of the authenticated user. Called 51 // only after an authentication success. Must return true on success, false on failure. 52 // Optional, default to success. 53 Authorization func(*fast.Ctx, interface{}) bool 54 55 // Callback function that will be called during login. 56 // Using this function it is possible to add additional payload data to the webtoken. 57 // The data is then made available during requests via c.Get("JWT_PAYLOAD"). 58 // Note that the payload is not encrypted. 59 // The attributes mentioned on jwt.io can't be used as keys for the map. 60 // Optional, by default no additional data will be set. 61 PayloadFunc func(data interface{}) g.Map 62 63 // User can define own Unauthorized func. 64 Unauthorized func(*fast.Ctx, int, string) 65 66 // User can define own LoginResponse func. 67 LoginResponse func(*fast.Ctx, int, string, time.Time) 68 69 // User can define own LogoutResponse func. 70 LogoutResponse func(*fast.Ctx, int) 71 72 // User can define own RefreshResponse func. 73 RefreshResponse func(*fast.Ctx, int, string, time.Time) 74 75 // Set the identity handler function 76 IdentityHandler func(*fast.Ctx) interface{} 77 78 // Set the identity key 79 IdentityKey string 80 81 // TokenLookup is a string in the form of "<source>:<name>" that is used 82 // to extract token from the request. 83 // Optional. Default value "header:Authorization". 84 // Possible values: 85 // - "header:<name>" 86 // - "query:<name>" 87 // - "cookie:<name>" 88 TokenLookup string 89 90 // TokenHeadName is a string in the header. Default value is "Bearer" 91 TokenHeadName string 92 93 // TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens. 94 TimeFunc func() time.Time 95 96 // HTTP Status messages for when something in the JWT middleware fails. 97 // Check error (e) to determine the appropriate error message. 98 HTTPStatusMessageFunc func(c *fast.Ctx, e error) string 99 100 // Private key file for asymmetric algorithms 101 PriKeyFile string 102 103 // Public key file for asymmetric algorithms 104 PubKeyFile string 105 106 // Private key 107 priKey *rsa.PrivateKey 108 109 // Public key 110 pubKey *rsa.PublicKey 111 112 // Optionally return the token as a cookie 113 // Optional Default: false 114 SendCookie bool 115 116 // Allow insecure cookies for development over http 117 // Optional Default: false :: HTTPS environments 118 SecureCookie bool 119 120 // Allow cookies to be accessed client side for development 121 // Optional Default: false :: JS can't modify 122 CookieHTTPOnly bool 123 124 // Allow cookie domain change for development 125 CookieDomain string 126 127 // SendAuthorization allow return authorization header for every request 128 // Optional Default: false 129 SendAuthorization bool 130 131 // Disable abort() of context. 132 // Optional Default: false 133 DisabledAbort bool 134 135 // CookieName allow cookie name change for development 136 // Optional Default: jwt 137 CookieName string 138 } 139 140 // New middleware. 141 /* demo: 142 identityKey := "id" 143 cfg := jwt.Config{ 144 Realm: "api", 145 Key: []byte("96E79218"), 146 Timeout: time.Hour * 24, 147 MaxRefresh: time.Hour * 24 * 7, 148 IdentityKey: identityKey, 149 Filter: func(c *fast.Ctx) bool { 150 return c.Get("IsAnonymous") != nil 151 }, 152 PayloadFunc: func(data interface{}) jwt.MapClaims { 153 if v, ok := data.(*User); ok { 154 return jwt.MapClaims{ 155 identityKey: v.UserName, 156 } 157 } 158 return jwt.MapClaims{} 159 }, 160 IdentityHandler: func(c *fast.Ctx) interface{} { 161 claims := jwt.ExtractClaims(c) 162 return &User{ 163 UserName: claims[identityKey].(string), 164 } 165 }, 166 Authenticator: func(c *fast.Ctx) (interface{}, error) { 167 var loginVal login 168 if err := c.BodyParser(&loginVals); err != nil { 169 return "", jwt.ErrMissingLoginValues 170 } 171 userID := loginVal.Username 172 password := loginVal.Password 173 174 if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") { 175 return &User{ 176 UserName: userID, 177 LastName: "LastName", 178 FirstName: "FirstName", 179 }, nil 180 } 181 182 return nil, jwt.ErrFailedAuthentication 183 }, 184 Authorization: func(c *fast.Ctx, data interface{}) bool { 185 if v, ok := data.(*User); ok && v.UserName == "admin" { 186 return true 187 } 188 189 return false 190 }, 191 Unauthorized: func(c *fast.Ctx, code int, message string) { 192 c.JSON(code, data.Map{ 193 "code": code, 194 "message": message, 195 }) 196 }, 197 // TokenLookup is a string in the form of "<source>:<name>" that is used 198 // to extract token from the request. 199 // Optional. Default value "header:Authorization". 200 // Possible values: 201 // - "header:<name>" 202 // - "query:<name>" 203 // - "cookie:<name>" 204 // - "param:<name>" 205 TokenLookup: "header: Authorization, query: token, cookie: jwt", 206 // TokenLookup: "query:token", 207 // TokenLookup: "cookie:token", 208 209 // TokenHeadName is a string in the header. Default value is "Bearer" 210 TokenHeadName: "Bearer", 211 212 // TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens. 213 TimeFunc: time.Now, 214 } 215 app.POST("/login", cfg.LoginHandler) // step: get the token 216 auth := app.Group("/auth").Use(jwt.New(cfg)) // step: app.Use(jwt.New(cfg)) 217 auth.GET("/refresh_token", cfg.RefreshHandler) // refresh token 218 */ 219 func New(config ...Config) func(*fast.Ctx) { 220 // Init config 221 var cfg Config 222 if len(config) > 0 { 223 cfg = config[0] 224 } 225 f.Must(cfg.Init()) 226 // implement function 227 return func(c *fast.Ctx) { 228 // Filter request to skip middleware 229 if cfg.Filter != nil && cfg.Filter(c) { 230 c.Next() 231 return 232 } 233 // internal implement 234 cfg.middlewareImpl(c) 235 } 236 } 237 238 var ( 239 // ErrMissingSecretKey indicates Secret key is required 240 ErrMissingSecretKey = errors.New("secret key is required") 241 242 // ErrForbidden when HTTP status 403 is given 243 ErrForbidden = errors.New("you don't have permission to access this resource") 244 245 // ErrMissingAuthenticatorFunc indicates Authenticator is required 246 ErrMissingAuthenticatorFunc = errors.New("Config.Authenticator func is undefined") 247 248 // ErrMissingLoginValues indicates a user tried to authenticate without username or password 249 ErrMissingLoginValues = errors.New("missing Username or Password") 250 251 // ErrFailedAuthentication indicates authentication failed, could be faulty username or password 252 ErrFailedAuthentication = errors.New("incorrect Username or Password") 253 254 // ErrFailedTokenCreation indicates JWT Token failed to create, reason unknown 255 ErrFailedTokenCreation = errors.New("failed to create JWT Token") 256 257 // ErrExpiredToken indicates JWT token has expired. Can't refresh. 258 ErrExpiredToken = errors.New("token is expired") 259 260 // ErrEmptyAuthHeader can be thrown if authing with a HTTP header, the Auth header needs to be set 261 ErrEmptyAuthHeader = errors.New("auth header is empty") 262 263 // ErrMissingExpField missing exp field in token 264 ErrMissingExpField = errors.New("missing exp field") 265 266 // ErrWrongFormatOfExp field must be float64 format 267 ErrWrongFormatOfExp = errors.New("exp must be float64 format") 268 269 // ErrInvalidAuthHeader indicates auth header is invalid, could for example have the wrong Realm name 270 ErrInvalidAuthHeader = errors.New("auth header is invalid") 271 272 // ErrEmptyQueryToken can be thrown if authing with URL Query, the query token variable is empty 273 ErrEmptyQueryToken = errors.New("query token is empty") 274 275 // ErrEmptyCookieToken can be thrown if authing with a cookie, the token cookie is empty 276 ErrEmptyCookieToken = errors.New("cookie token is empty") 277 278 // ErrEmptyParamToken can be thrown if authing with parameter in path, the parameter in path is empty 279 ErrEmptyParamToken = errors.New("parameter token is empty") 280 281 // ErrInvalidSigningAlgorithm indicates signing algorithm is invalid, needs to be HS256, HS384, HS512, RS256, RS384 or RS512 282 ErrInvalidSigningAlgorithm = errors.New("invalid signing algorithm") 283 284 // ErrNoPrivKeyFile indicates that the given private key is unreadable 285 ErrNoPrivKeyFile = errors.New("private key file unreadable") 286 287 // ErrNoPubKeyFile indicates that the given public key is unreadable 288 ErrNoPubKeyFile = errors.New("public key file unreadable") 289 290 // ErrInvalidPrivKey indicates that the given private key is invalid 291 ErrInvalidPrivKey = errors.New("private key invalid") 292 293 // ErrInvalidPubKey indicates the the given public key is invalid 294 ErrInvalidPubKey = errors.New("public key invalid") 295 296 // IdentityKey default identity key 297 IdentityKey = "identity" 298 ) 299 300 func (mw *Config) readKeys() error { 301 err := mw.privateKey() 302 if err != nil { 303 return err 304 } 305 err = mw.publicKey() 306 if err != nil { 307 return err 308 } 309 return nil 310 } 311 312 func (mw *Config) privateKey() error { 313 keyData, err := ioutil.ReadFile(mw.PriKeyFile) 314 if err != nil { 315 return ErrNoPrivKeyFile 316 } 317 key, err := jwt.ParseRSAPrivateKeyFromPEM(keyData) 318 if err != nil { 319 return ErrInvalidPrivKey 320 } 321 mw.priKey = key 322 return nil 323 } 324 325 func (mw *Config) publicKey() error { 326 keyData, err := ioutil.ReadFile(mw.PubKeyFile) 327 if err != nil { 328 return ErrNoPubKeyFile 329 } 330 key, err := jwt.ParseRSAPublicKeyFromPEM(keyData) 331 if err != nil { 332 return ErrInvalidPubKey 333 } 334 mw.pubKey = key 335 return nil 336 } 337 338 func (mw *Config) usingPublicKeyAlgo() bool { 339 switch mw.SigningAlgorithm { 340 case "RS256", "RS512", "RS384": 341 return true 342 } 343 return false 344 } 345 346 // Init initialize jwt configs. 347 func (mw *Config) Init() error { 348 if mw.TokenLookup == "" { 349 mw.TokenLookup = "header:Authorization" 350 } 351 352 if mw.SigningAlgorithm == "" { 353 mw.SigningAlgorithm = "HS256" 354 } 355 356 if mw.Timeout == 0 { 357 mw.Timeout = time.Hour 358 } 359 360 if mw.TimeFunc == nil { 361 mw.TimeFunc = time.Now 362 } 363 364 mw.TokenHeadName = strings.TrimSpace(mw.TokenHeadName) 365 if len(mw.TokenHeadName) == 0 { 366 mw.TokenHeadName = "Bearer" 367 } 368 369 if mw.Authorization == nil { 370 mw.Authorization = func(c *fast.Ctx, data interface{}) bool { 371 return true 372 } 373 } 374 375 if mw.Unauthorized == nil { 376 mw.Unauthorized = func(c *fast.Ctx, code int, message string) { 377 _ = c.JSON(g.Map{ 378 "code": code, 379 "message": message, 380 }) 381 } 382 } 383 384 if mw.LoginResponse == nil { 385 mw.LoginResponse = func(c *fast.Ctx, code int, token string, expire time.Time) { 386 _ = c.JSON(g.Map{ 387 "code": http.StatusOK, 388 "token": token, 389 "expire": expire.Format(time.RFC3339), 390 }) 391 } 392 } 393 394 if mw.LogoutResponse == nil { 395 mw.LogoutResponse = func(c *fast.Ctx, code int) { 396 _ = c.JSON(g.Map{ 397 "code": http.StatusOK, 398 }) 399 } 400 } 401 402 if mw.RefreshResponse == nil { 403 mw.RefreshResponse = func(c *fast.Ctx, code int, token string, expire time.Time) { 404 _ = c.JSON(g.Map{ 405 "code": http.StatusOK, 406 "token": token, 407 "expire": expire.Format(time.RFC3339), 408 }) 409 } 410 } 411 412 if mw.IdentityKey == "" { 413 mw.IdentityKey = IdentityKey 414 } 415 416 if mw.IdentityHandler == nil { 417 mw.IdentityHandler = func(c *fast.Ctx) interface{} { 418 claims := ExtractClaims(c) 419 return claims[mw.IdentityKey] 420 } 421 } 422 423 if mw.HTTPStatusMessageFunc == nil { 424 mw.HTTPStatusMessageFunc = func(c *fast.Ctx, e error) string { 425 return e.Error() 426 } 427 } 428 429 if mw.Realm == "" { 430 mw.Realm = "jwt" 431 } 432 433 if mw.CookieName == "" { 434 mw.CookieName = "jwt" 435 } 436 437 if mw.usingPublicKeyAlgo() { 438 return mw.readKeys() 439 } 440 441 if mw.Key == nil { 442 return ErrMissingSecretKey 443 } 444 return nil 445 } 446 447 // MiddlewareFunc makes Config implement the Middleware interface. 448 func (mw *Config) MiddlewareFunc() func(c *fast.Ctx) { 449 return func(c *fast.Ctx) { 450 mw.middlewareImpl(c) 451 } 452 } 453 454 func (mw *Config) middlewareImpl(c *fast.Ctx) { 455 claims, err := mw.GetClaimsFromJWT(c) 456 if err != nil { 457 mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(c, err)) 458 return 459 } 460 461 if claims["exp"] == nil { 462 mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(c, ErrMissingExpField)) 463 return 464 } 465 466 if _, ok := claims["exp"].(float64); !ok { 467 mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(c, ErrWrongFormatOfExp)) 468 return 469 } 470 471 if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() { 472 mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(c, ErrExpiredToken)) 473 return 474 } 475 476 c.Set("JWT_PAYLOAD", claims) 477 identity := mw.IdentityHandler(c) 478 479 if identity != nil { 480 c.Set(mw.IdentityKey, identity) 481 } 482 483 if !mw.Authorization(c, identity) { 484 mw.unauthorized(c, http.StatusForbidden, mw.HTTPStatusMessageFunc(c, ErrForbidden)) 485 return 486 } 487 488 c.Next() 489 } 490 491 // GetClaimsFromJWT get claims from JWT token 492 func (mw *Config) GetClaimsFromJWT(c *fast.Ctx) (g.Map, error) { 493 token, err := mw.ParseToken(c) 494 495 if err != nil { 496 return nil, err 497 } 498 499 if mw.SendAuthorization { 500 if v := c.Get("JWT_TOKEN"); v != nil { 501 c.SetHeader("Authorization", mw.TokenHeadName+" "+v.(string)) 502 } 503 } 504 505 claims := g.Map{} 506 for key, value := range token.Claims.(jwt.MapClaims) { 507 claims[key] = value 508 } 509 510 return claims, nil 511 } 512 513 // LoginHandler can be used by clients to get a jwt token. 514 // Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}. 515 // Reply will be of the form {"token": "TOKEN"}. 516 func (mw *Config) LoginHandler(c *fast.Ctx) { 517 if mw.Authenticator == nil { 518 mw.unauthorized(c, http.StatusInternalServerError, mw.HTTPStatusMessageFunc(c, ErrMissingAuthenticatorFunc)) 519 return 520 } 521 522 data, err := mw.Authenticator(c) 523 524 if err != nil { 525 mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(c, err)) 526 return 527 } 528 529 // Create the token 530 token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm)) 531 claims := token.Claims.(jwt.MapClaims) 532 533 if mw.PayloadFunc != nil { 534 for key, value := range mw.PayloadFunc(data) { 535 claims[key] = value 536 } 537 } 538 539 expire := mw.TimeFunc().Add(mw.Timeout) 540 claims["exp"] = expire.Unix() 541 claims["orig_iat"] = mw.TimeFunc().Unix() 542 tokenString, err := mw.signedString(token) 543 544 if err != nil { 545 mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(c, ErrFailedTokenCreation)) 546 return 547 } 548 549 // set cookie 550 if mw.SendCookie { 551 //maxage := int(expire.Unix() - time.Now().Unix()) 552 c.SetCookie( 553 mw.CookieName, 554 tokenString, 555 expire, 556 "/", 557 mw.CookieDomain, 558 mw.SecureCookie, 559 mw.CookieHTTPOnly, 560 ) 561 } 562 563 mw.LoginResponse(c, http.StatusOK, tokenString, expire) 564 } 565 566 // LogoutHandler can be used by clients to remove the jwt cookie (if set) 567 func (mw *Config) LogoutHandler(c *fast.Ctx) { 568 // delete auth cookie 569 if mw.SendCookie { 570 c.SetCookie( 571 mw.CookieName, 572 "", 573 mw.TimeFunc().Add(mw.Timeout*-1), 574 "/", 575 mw.CookieDomain, 576 mw.SecureCookie, 577 mw.CookieHTTPOnly, 578 ) 579 } 580 581 mw.LogoutResponse(c, http.StatusOK) 582 } 583 584 func (mw *Config) signedString(token *jwt.Token) (string, error) { 585 var tokenString string 586 var err error 587 if mw.usingPublicKeyAlgo() { 588 tokenString, err = token.SignedString(mw.priKey) 589 } else { 590 tokenString, err = token.SignedString(mw.Key) 591 } 592 return tokenString, err 593 } 594 595 // RefreshHandler can be used to refresh a token. The token still needs to be valid on refresh. 596 // Shall be put under an endpoint that is using the Config. 597 // Reply will be of the form {"token": "TOKEN"}. 598 func (mw *Config) RefreshHandler(c *fast.Ctx) { 599 tokenString, expire, err := mw.RefreshToken(c) 600 if err != nil { 601 mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(c, err)) 602 return 603 } 604 605 mw.RefreshResponse(c, http.StatusOK, tokenString, expire) 606 } 607 608 // RefreshToken refresh token and check if token is expired 609 func (mw *Config) RefreshToken(c *fast.Ctx) (string, time.Time, error) { 610 claims, err := mw.CheckIfTokenExpire(c) 611 if err != nil { 612 return "", time.Now(), err 613 } 614 615 // Create the token 616 newToken := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm)) 617 newClaims := newToken.Claims.(jwt.MapClaims) 618 619 for key := range claims { 620 newClaims[key] = claims[key] 621 } 622 623 expire := mw.TimeFunc().Add(mw.Timeout) 624 newClaims["exp"] = expire.Unix() 625 newClaims["orig_iat"] = mw.TimeFunc().Unix() 626 tokenString, err := mw.signedString(newToken) 627 628 if err != nil { 629 return "", time.Now(), err 630 } 631 632 // set cookie 633 if mw.SendCookie { 634 //maxage := int(expire.Unix() - time.Now().Unix()) 635 c.SetCookie( 636 mw.CookieName, 637 tokenString, 638 expire, 639 "/", 640 mw.CookieDomain, 641 mw.SecureCookie, 642 mw.CookieHTTPOnly, 643 ) 644 } 645 646 return tokenString, expire, nil 647 } 648 649 // CheckIfTokenExpire check if token expire 650 func (mw *Config) CheckIfTokenExpire(c *fast.Ctx) (jwt.MapClaims, error) { 651 token, err := mw.ParseToken(c) 652 653 if token == nil { 654 return nil, ErrExpiredToken 655 } 656 657 if err != nil { 658 // If we receive an error, and the error is anything other than a single 659 // ValidationErrorExpired, we want to return the error. 660 // If the error is just ValidationErrorExpired, we want to continue, as we can still 661 // refresh the token if it's within the MaxRefresh time. 662 // (see https://github.com/appleboy/gin-jwt/issues/176) 663 validationErr, ok := err.(*jwt.ValidationError) 664 if !ok || validationErr.Errors != jwt.ValidationErrorExpired { 665 return nil, err 666 } 667 } 668 669 claims := token.Claims.(jwt.MapClaims) 670 671 origIat := int64(claims["orig_iat"].(float64)) 672 673 if origIat < mw.TimeFunc().Add(-mw.MaxRefresh).Unix() { 674 return nil, ErrExpiredToken 675 } 676 677 return claims, nil 678 } 679 680 // TokenGenerator method that clients can use to get a jwt token. 681 func (mw *Config) TokenGenerator(data interface{}) (string, time.Time, error) { 682 token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm)) 683 claims := token.Claims.(jwt.MapClaims) 684 685 if mw.PayloadFunc != nil { 686 for key, value := range mw.PayloadFunc(data) { 687 claims[key] = value 688 } 689 } 690 691 expire := mw.TimeFunc().UTC().Add(mw.Timeout) 692 claims["exp"] = expire.Unix() 693 claims["orig_iat"] = mw.TimeFunc().Unix() 694 tokenString, err := mw.signedString(token) 695 if err != nil { 696 return "", time.Time{}, err 697 } 698 699 return tokenString, expire, nil 700 } 701 702 func (mw *Config) jwtFromHeader(c *fast.Ctx, key string) (string, error) { 703 authHeader := c.GetHeader(key) 704 705 if authHeader == "" { 706 return "", ErrEmptyAuthHeader 707 } 708 709 parts := strings.SplitN(authHeader, " ", 2) 710 if !(len(parts) == 2 && parts[0] == mw.TokenHeadName) { 711 return "", ErrInvalidAuthHeader 712 } 713 714 return parts[1], nil 715 } 716 717 func (mw *Config) jwtFromQuery(c *fast.Ctx, key string) (string, error) { 718 token := c.Query(key) 719 720 if token == "" { 721 return "", ErrEmptyQueryToken 722 } 723 724 return token, nil 725 } 726 727 func (mw *Config) jwtFromCookie(c *fast.Ctx, key string) (string, error) { 728 cookie := c.Cookies(key) 729 730 if cookie == "" { 731 return "", ErrEmptyCookieToken 732 } 733 734 return cookie, nil 735 } 736 737 func (mw *Config) jwtFromParam(c *fast.Ctx, key string) (string, error) { 738 token := c.Params(key) 739 740 if token == "" { 741 return "", ErrEmptyParamToken 742 } 743 744 return token, nil 745 } 746 747 // ParseToken parse jwt token from gin context 748 func (mw *Config) ParseToken(c *fast.Ctx) (*jwt.Token, error) { 749 var token string 750 var err error 751 752 methods := strings.Split(mw.TokenLookup, ",") 753 for _, method := range methods { 754 if len(token) > 0 { 755 break 756 } 757 parts := strings.Split(strings.TrimSpace(method), ":") 758 k := strings.TrimSpace(parts[0]) 759 v := strings.TrimSpace(parts[1]) 760 switch k { 761 case "header": 762 token, err = mw.jwtFromHeader(c, v) 763 case "query": 764 token, err = mw.jwtFromQuery(c, v) 765 case "cookie": 766 token, err = mw.jwtFromCookie(c, v) 767 case "param": 768 token, err = mw.jwtFromParam(c, v) 769 } 770 } 771 772 if err != nil { 773 return nil, err 774 } 775 776 return jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { 777 if jwt.GetSigningMethod(mw.SigningAlgorithm) != t.Method { 778 return nil, ErrInvalidSigningAlgorithm 779 } 780 if mw.usingPublicKeyAlgo() { 781 return mw.pubKey, nil 782 } 783 784 // save token string if valid 785 c.Set("JWT_TOKEN", token) 786 787 return mw.Key, nil 788 }) 789 } 790 791 // ParseTokenString parse jwt token string 792 func (mw *Config) ParseTokenString(token string) (*jwt.Token, error) { 793 return jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { 794 if jwt.GetSigningMethod(mw.SigningAlgorithm) != t.Method { 795 return nil, ErrInvalidSigningAlgorithm 796 } 797 if mw.usingPublicKeyAlgo() { 798 return mw.pubKey, nil 799 } 800 801 return mw.Key, nil 802 }) 803 } 804 805 func (mw *Config) unauthorized(c *fast.Ctx, code int, message string) { 806 c.SetHeader("WWW-Authenticate", "JWT realm="+mw.Realm) 807 if !mw.DisabledAbort { 808 c.Abort() 809 } 810 811 mw.Unauthorized(c, code, message) 812 } 813 814 // ExtractClaims help to extract the JWT claims 815 func ExtractClaims(c *fast.Ctx) g.Map { 816 claims := c.Get("JWT_PAYLOAD") 817 if claims == nil { 818 return make(g.Map) 819 } 820 821 return claims.(g.Map) 822 } 823 824 // ExtractClaimsFromToken help to extract the JWT claims from token 825 func ExtractClaimsFromToken(token *jwt.Token) g.Map { 826 if token == nil { 827 return make(g.Map) 828 } 829 830 claims := g.Map{} 831 for key, value := range token.Claims.(jwt.MapClaims) { 832 claims[key] = value 833 } 834 835 return claims 836 } 837 838 // GetToken help to get the JWT token string 839 func GetToken(c *fast.Ctx) string { 840 token := c.Get("JWT_TOKEN") 841 if token != "" { 842 return "" 843 } 844 845 return token.(string) 846 }