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  }