github.com/aldelo/common@v1.5.1/wrapper/gin/ginjwt.go (about)

     1  package gin
     2  
     3  /*
     4   * Copyright 2020-2023 Aldelo, LP
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  import (
    20  	"fmt"
    21  	util "github.com/aldelo/common"
    22  	"github.com/aldelo/common/wrapper/gin/ginbindtype"
    23  	"github.com/aldelo/common/wrapper/gin/ginjwtsignalgorithm"
    24  	"log"
    25  	"net/http"
    26  	"time"
    27  
    28  	jwt "github.com/appleboy/gin-jwt/v2"
    29  	"github.com/gin-gonic/gin"
    30  )
    31  
    32  // NewGinJwtMiddleware helper method returns a GinJwt struct object ready for setup and use
    33  //
    34  // Realm = (required) Realm name to display to the user
    35  // IdentityKey = (required) IdentityKey defines the key used for storing identity info in jwt claim
    36  // SigningSecretKey = (required) Secret key used for signing
    37  // AuthenticateBindingType = (required) AuthenticateBindingType defines the binding type to use for login form field data
    38  func NewGinJwtMiddleware(realm string, identityKey string, signingSecretKey string, authenticateBindingType ginbindtype.GinBindType) *GinJwt {
    39  	return &GinJwt{
    40  		Realm:                   realm,
    41  		IdentityKey:             identityKey,
    42  		SigningSecretKey:        signingSecretKey,
    43  		SigningAlgorithm:        ginjwtsignalgorithm.HS256,
    44  		AuthenticateBindingType: authenticateBindingType,
    45  	}
    46  }
    47  
    48  // GinJwt struct encapsulates gin authentication and authorization related services, along with jwt handling
    49  //
    50  // *** Basic Setup ***
    51  // Realm = (required) Realm name to display to the user
    52  // IdentityKey = (required) IdentityKey defines the key used for storing identity info in jwt claim
    53  // SigningSecretKey = (required) Secret key used for signing
    54  // SigningAlgorithm = (required) HS256, HS384, HS512, RS256, RS384 or RS512 Optional, default is HS256
    55  // PrivateKeyFile = (optional) Private key file for asymmetric algorithms
    56  // PublicKeyFile = (optional) Public key file for asymmetric algorithms
    57  // TokenValidDuration = (required) Duration that a jwt token is valid. Optional, defaults to one hour. (aka Timeout)
    58  // TokenMaxRefreshDuration = (required) This field allows clients to refresh their token until MaxRefresh has passed,
    59  //
    60  //	Note that clients can refresh their token in the last moment of MaxRefresh,
    61  //	This means that the maximum validity timespan for a token is TokenTime + MaxRefresh,
    62  //	Optional, defaults to 0 meaning not refreshable
    63  //
    64  // SendAuthorization = (optional) SendAuthorization allow return authorization header for every request, default = false
    65  // DisableAbort = (optional) Disable abort() of context, default = false
    66  // TokenLookup = (optional) TokenLookup is a string in the form of "<source>:<name>" that is used to extract token from the request,
    67  //
    68  //	Optional. Default value = "header: Authorization",
    69  //	Possible values:
    70  //		- "header:<name>"
    71  //		- "query:<name>"
    72  //		- "cookie:<name>"
    73  //		- "param:<name>"
    74  //	Examples:
    75  //		TokenLookup: "header: Authorization, query: token, cookie: jwt",
    76  //		TokenLookup: "query:token",
    77  //		TokenLookup: "cookie:token",
    78  //
    79  // TokenHeadName = (optional) TokenHeadName is a string in the header. Default value = "Bearer"
    80  //
    81  // *** Cookie Setup ***
    82  // SendCookie = (optional) Optionally return the token as a cookie, default = false
    83  // CookieHttpOnly = (optional) Allow cookies to be accessed client side for development, default = true
    84  // SecureCookie = (optional) Allow insecure cookies for development over http, default = true
    85  // CookieMaxAge = (optional) Duration that a cookie is valid. Optional, by default = Timeout value
    86  // CookieDomain = (optional) Allow cookie domain change for development, default = ""
    87  // CookieName = (optional) CookieName allow cookie name change for development, default = ""
    88  // CookieSameSite = (optional) CookieSameSite allow use http.SameSite cookie param, default = SameSiteDefaultMode
    89  //
    90  //	values = default, lax, strict, none
    91  //
    92  // *** Authentication Setup ***
    93  // AuthenticateBindingType = (required) AuthenticateBindingType defines the binding type to use for login form field data
    94  // LoginRequestDataPtr = (optional) LoginRequestDataPtr contains pointer object that represents login request (used for binding), if not set, default = &UserLogin helper struct
    95  // LoginRoutePath = (required) LoginRoutePath defines the relative path to the gin jwt middleware's built-in LoginHandler action, sets up as POST
    96  // LogoutRoutePath = (optional) LogoutRoutePath defines the relative path to the gin jwt middleware's built-in LogoutHandler action, sets up as POST
    97  // RefreshTokenRoutePath = (optional) RefreshTokenRoutePath defines the relative path to the middleware's built-in RefreshHandler action, sets up as GET
    98  // AuthenticateHandler = (required) AuthenticateHandler func is called by Authenticator,
    99  //
   100  //	receives loginRequestDataPtr for authentication use,
   101  //	if authentication succeeds, returns the loggedInCredentialPtr object
   102  //	(which typically is a user object containing user information logged in)
   103  //
   104  // AddClaimsHandler = (optional) LoggedInMapClaimsHandler func is called during Authenticator action upon success,
   105  //
   106  //	so that this handler when coded can insert jwt additional payload data,
   107  //	   - loggedInCredentialPtr = the returning loggedInCredentialPtr from LoginHandler,
   108  //	   - identityKeyValue = string value for the named identityKey defined within the struct
   109  //
   110  // GetIdentityHandler = (optional) GetIdentityHandler func is called when IdentityHandler is triggered,
   111  //
   112  //	field values from claims will be parsed and returned via object by the implementation code
   113  //
   114  // LoginResponseHandler = (optional) Callback function to handle custom login response
   115  // LogoutResponseHandler = (optional) Callback function to handle custom logout response
   116  // RefreshTokenResponseHandler = (optional) Callback function to handle custom token refresh response
   117  //
   118  // *** Authorization Setup ***
   119  // AuthorizerHandler = (optional) AuthorizerHandler func is called during authorization after authentication,
   120  //
   121  //								  to validate if the current credential has access rights to certain parts of the target site,
   122  //	 							  the loggedInCredentialPtr is the object that LoginHandler returns upon successful authentication,
   123  //								     - return value of true indicates authorization success,
   124  //								     - return value of false indicates authorization failure
   125  //
   126  // UnauthorizedHandler = (optional) UnauthorizedHandler func is called when the authorization is not authorized,
   127  //
   128  //	this handler will return the unauthorized message content to caller,
   129  //	such as via c.JSON, c.HTML, etc as dictated by the handler implementation process,
   130  //	   - c *gin.Context = context used to return the unauthorized access content
   131  //	   - code / message = unauthorized code and message as given by the web server to respond back to the caller
   132  //
   133  // *** Other Handlers ***
   134  // TimeHandler = (optional) TimeHandler provides the current time,
   135  //
   136  //	override it to use another time value,
   137  //	useful for testing or if server uses a different time zone than the tokens,
   138  //	default = time.Now()
   139  //
   140  // NoRouteHandler = (optional) Defines the route handler to execute when no route is encountered
   141  // MiddlewareErrorEvaluator = (optional) HTTP Status messages for when something in the JWT middleware fails,
   142  //
   143  //	Check error (e) to determine the appropriate error message
   144  type GinJwt struct {
   145  	// -----------------------------------------------------------------------------------------------------------------
   146  	// gin jwt setup fields
   147  	// -----------------------------------------------------------------------------------------------------------------
   148  
   149  	// Realm name to display to the user. Required.
   150  	Realm string
   151  
   152  	// IdentityKey defines the key used for storing identity info in jwt claim
   153  	IdentityKey string
   154  
   155  	// Secret key used for signing. Required.
   156  	SigningSecretKey string
   157  
   158  	// HS256, HS384, HS512, RS256, RS384 or RS512 Optional, default is HS256.
   159  	SigningAlgorithm ginjwtsignalgorithm.GinJwtSignAlgorithm
   160  
   161  	// Private key file for asymmetric algorithms
   162  	PrivateKeyFile string
   163  
   164  	// Public key file for asymmetric algorithms
   165  	PublicKeyFile string
   166  
   167  	// Duration that a jwt token is valid. Optional, defaults to one hour. (aka Timeout)
   168  	TokenValidDuration time.Duration
   169  
   170  	// This field allows clients to refresh their token until MaxRefresh has passed.
   171  	// Note that clients can refresh their token in the last moment of MaxRefresh.
   172  	// This means that the maximum validity timespan for a token is TokenTime + MaxRefresh.
   173  	// Optional, defaults to 0 meaning not refreshable.
   174  	TokenMaxRefreshDuration time.Duration
   175  
   176  	// SendAuthorization allow return authorization header for every request
   177  	SendAuthorization bool
   178  
   179  	// Disable abort() of context.
   180  	DisableAbort bool
   181  
   182  	// TokenLookup is a string in the form of "<source>:<name>" that is used to extract token from the request.
   183  	// Optional. Default value "header:Authorization".
   184  	// Possible values:
   185  	// - "header:<name>"
   186  	// - "query:<name>"
   187  	// - "cookie:<name>"
   188  	// - "param:<name>"
   189  	// TokenLookup: "header: Authorization, query: token, cookie: jwt",
   190  	// TokenLookup: "query:token",
   191  	// TokenLookup: "cookie:token",
   192  	TokenLookup string
   193  
   194  	// TokenHeadName is a string in the header. Default value is "Bearer"
   195  	// "Bearer"
   196  	TokenHeadName string
   197  
   198  	// -----------------------------------------------------------------------------------------------------------------
   199  	// cookie setup
   200  	// -----------------------------------------------------------------------------------------------------------------
   201  	SendCookie     bool
   202  	CookieMaxAge   time.Duration
   203  	SecureCookie   *bool
   204  	CookieHTTPOnly *bool
   205  	CookieDomain   string
   206  	CookieName     string
   207  	CookieSameSite *http.SameSite
   208  
   209  	// -----------------------------------------------------------------------------------------------------------------
   210  	// authentication setup
   211  	// -----------------------------------------------------------------------------------------------------------------
   212  
   213  	// AuthenticateBindingType defines the binding type to use for login form field data
   214  	AuthenticateBindingType ginbindtype.GinBindType
   215  
   216  	// LoginRequestDataPtr contains pointer object that represents login request (used for binding), if not set, default = &UserLogin helper struct
   217  	LoginRequestDataPtr interface{}
   218  
   219  	// LoginRoutePath defines the relative path to the middleware's built-in LoginHandler,
   220  	// this route path is setup as POST with the gin engine
   221  	LoginRoutePath string
   222  
   223  	// LogoutRoutePath defines the relative path to the middleware's built-in LogoutHandler,
   224  	// this route path is setup as POST with the gin engine
   225  	LogoutRoutePath string
   226  
   227  	// RefreshTokenRoutePath defines the relative path to the middleware's built-in RefreshHandler,
   228  	// this route path is setup as GET with the gin engine
   229  	RefreshTokenRoutePath string
   230  
   231  	// AuthenticateHandler func is called by Authenticator,
   232  	// receives loginRequestDataPtr for authentication use,
   233  	// if authentication succeeds, returns the loggedInCredentialPtr object
   234  	// (which typically is a user object containing user information logged in)
   235  	AuthenticateHandler func(loginRequestDataPtr interface{}) (loggedInCredentialPtr interface{})
   236  
   237  	// AddClaimsHandler func is called during Authenticator action upon success,
   238  	// so that this handler when coded can insert jwt additional payload data
   239  	//
   240  	// loggedInCredentialPtr = the returning loggedInCredentialPtr from LoginHandler
   241  	// identityKeyValue = string value for the named identityKey defined within the struct
   242  	AddClaimsHandler func(loggedInCredentialPtr interface{}) (identityKeyValue string, claims map[string]interface{})
   243  
   244  	// GetIdentityHandler func is called when IdentityHandler is triggered,
   245  	// field values from claims will be parsed and returned via object by the implementation code
   246  	GetIdentityHandler func(claims map[string]interface{}) interface{}
   247  
   248  	// Callback function to handle custom login response
   249  	LoginResponseHandler func(c *gin.Context, statusCode int, token string, expires time.Time)
   250  
   251  	// Callback function to handle custom logout response
   252  	LogoutResponseHandler func(c *gin.Context, statusCode int)
   253  
   254  	// Callback function to handle custom token refresh response
   255  	RefreshTokenResponseHandler func(c *gin.Context, statusCode int, token string, expires time.Time)
   256  
   257  	// -----------------------------------------------------------------------------------------------------------------
   258  	// authorization setup
   259  	// -----------------------------------------------------------------------------------------------------------------
   260  
   261  	// AuthorizerHandler func is called during authorization after authentication,
   262  	// to validate if the current credential has access rights to certain parts of the target site,
   263  	// the loggedInCredentialPtr is the object that LoginHandler returns upon successful authentication,
   264  	// return value of true indicates authorization success,
   265  	// return value of false indicates authorization failure
   266  	AuthorizerHandler func(loggedInCredentialPtr interface{}, c *gin.Context) bool
   267  
   268  	// UnauthorizedHandler func is called when the authorization is not authorized,
   269  	// this handler will return the unauthorized message content to caller,
   270  	// such as via c.JSON, c.HTML, etc as dictated by the handler implementation process
   271  	//
   272  	// c *gin.Context = context used to return the unauthorized access content
   273  	// code / message = unauthorized code and message as given by the web server to respond back to the caller
   274  	UnauthorizedHandler func(c *gin.Context, code int, message string)
   275  
   276  	// -----------------------------------------------------------------------------------------------------------------
   277  	// other handlers
   278  	// -----------------------------------------------------------------------------------------------------------------
   279  
   280  	// TimeFunc provides the current time.
   281  	// override it to use another time value.
   282  	// useful for testing or if server uses a different time zone than the tokens.
   283  	TimeHandler func() time.Time
   284  
   285  	// NoRouteHandler is called when no route situation is encountered
   286  	NoRouteHandler func(claims map[string]interface{}, c *gin.Context)
   287  
   288  	// HTTP Status messages for when something in the JWT middleware fails.
   289  	// Check error (e) to determine the appropriate error message.
   290  	MiddlewareErrorEvaluator func(e error, c *gin.Context) string
   291  
   292  	// -----------------------------------------------------------------------------------------------------------------
   293  	// local var
   294  	// -----------------------------------------------------------------------------------------------------------------
   295  	_ginJwtMiddleware *jwt.GinJWTMiddleware
   296  }
   297  
   298  // BuildGinJwtMiddleware sets up auth jwt middleware for gin web server,
   299  // including adding login, logout, refreshtoken, and other routes where applicable
   300  func (j *GinJwt) BuildGinJwtMiddleware(g *Gin) error {
   301  	j._ginJwtMiddleware = nil
   302  
   303  	if g == nil {
   304  		return fmt.Errorf("Gin Wrapper Object is Required")
   305  	}
   306  
   307  	if g._ginEngine == nil {
   308  		return fmt.Errorf("Gin Engine is Required")
   309  	}
   310  
   311  	if !j.AuthenticateBindingType.Valid() {
   312  		return fmt.Errorf("Authenticate Binding Type is Required")
   313  	}
   314  
   315  	if j.AuthenticateHandler == nil {
   316  		return fmt.Errorf("Authenticate Handler is Required")
   317  	}
   318  
   319  	if j.TokenValidDuration == 0 {
   320  		j.TokenValidDuration = 15 * time.Minute
   321  	}
   322  
   323  	if j.TokenMaxRefreshDuration == 0 {
   324  		j.TokenMaxRefreshDuration = 24 * time.Hour
   325  	}
   326  
   327  	if j.LoginRequestDataPtr == nil {
   328  		j.LoginRequestDataPtr = &UserLogin{}
   329  	}
   330  
   331  	// the jwt middleware
   332  	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
   333  		Realm:             j.Realm,
   334  		IdentityKey:       j.IdentityKey,
   335  		Key:               []byte(j.SigningSecretKey),
   336  		SigningAlgorithm:  j.SigningAlgorithm.Key(),
   337  		PrivKeyFile:       j.PrivateKeyFile,
   338  		PubKeyFile:        j.PublicKeyFile,
   339  		Timeout:           j.TokenValidDuration,
   340  		MaxRefresh:        j.TokenMaxRefreshDuration,
   341  		SendAuthorization: j.SendAuthorization,
   342  		DisabledAbort:     j.DisableAbort,
   343  		TokenLookup:       j.TokenLookup,
   344  		TokenHeadName:     j.TokenHeadName,
   345  
   346  		// TimeFunc provides the current time.
   347  		// override it to use another time value.
   348  		// useful for testing or if server uses a different time zone than the tokens.
   349  		TimeFunc: time.Now,
   350  
   351  		// Callback function that should perform the authentication of the user based on login info.
   352  		// Must return user data as user identifier,
   353  		// it will be stored in Claim Array. Required.
   354  		// Check error (e) to determine the appropriate error message.
   355  		Authenticator: func(c *gin.Context) (interface{}, error) {
   356  			if j.AuthenticateHandler == nil {
   357  				return nil, jwt.ErrMissingAuthenticatorFunc
   358  			}
   359  
   360  			// loginFields struct represents the login form fields serialized from context input
   361  			var loginRequestData interface{}
   362  			loginRequestData = j.LoginRequestDataPtr
   363  
   364  			if err := g.bindInput(c, j.AuthenticateBindingType, loginRequestData); err != nil {
   365  				return nil, fmt.Errorf(jwt.ErrMissingLoginValues.Error() + ": " + err.Error())
   366  			}
   367  
   368  			if loggedInCredential := j.AuthenticateHandler(loginRequestData); loggedInCredential != nil {
   369  				return loggedInCredential, nil
   370  			} else {
   371  				return nil, jwt.ErrFailedAuthentication
   372  			}
   373  		},
   374  
   375  		// Callback function that should perform the authorization of the authenticated user.
   376  		// Called only after an authentication success.
   377  		// Must return true on success, false on failure.
   378  		// Optional, default to success.
   379  		Authorizator: func(data interface{}, c *gin.Context) bool {
   380  			if j.AuthorizerHandler != nil {
   381  				return j.AuthorizerHandler(data, c)
   382  			} else {
   383  				return true
   384  			}
   385  		},
   386  
   387  		// UnauthorizedHandler func is called when the authorization is not authorized,
   388  		// this handler will return the unauthorized message content to caller,
   389  		// such as via c.JSON, c.HTML, etc as dictated by the handler implementation process
   390  		//
   391  		// c *gin.Context = context used to return the unauthorized access content
   392  		// code / message = unauthorized code and message as given by the web server to respond back to the caller
   393  		Unauthorized: func(c *gin.Context, code int, message string) {
   394  			if j.UnauthorizedHandler != nil {
   395  				j.UnauthorizedHandler(c, code, message)
   396  			} else {
   397  				c.JSON(code, gin.H{
   398  					"code":    code,
   399  					"message": message,
   400  				})
   401  			}
   402  		},
   403  	})
   404  
   405  	if err != nil {
   406  		return fmt.Errorf("Setup Gin Jwt Middleware Failed: %s", err.Error())
   407  	}
   408  
   409  	if j.AddClaimsHandler != nil {
   410  		// Callback function that will be called during login.
   411  		// Using this function it is possible to add additional payload data to the web token.
   412  		// The data is then made available during requests via c.Get("JWT_PAYLOAD").
   413  		// Note that the payload is not encrypted.
   414  		// The attributes mentioned on jwt.io can't be used as keys for the map.
   415  		// Optional, by default no additional data will be set
   416  		//
   417  		// reserved claims: do not use
   418  		//		iss = issuer of the jwt
   419  		//		sub = subject of the jwt (the user)
   420  		//		aud = audience / recipient for which the jwt is intended
   421  		//		exp = expiration time after which the jwt expires
   422  		//		nbf = not before time which the jwt must not be accepted for processing
   423  		//		iat = issued at time which the jwt was issued, can be used to determine the age of the jwt
   424  		//		jti = jwt id, the unique identifier, used to prevent jwt from being replayed (allows a token to be used only once)
   425  		//		more jwt reserved tokens, see = https://www.iana.org/assignments/jwt/jwt.xhtml#claims
   426  		//
   427  		// notes:
   428  		//		1) data interface{} = this object represents the Authenticator return object (loggedInCredential interface{})
   429  		//		2) internal code can assert the loggedInCredential to the actual struct to retrieve its field values
   430  		//				such as: v, ok := data.(*User)
   431  		authMiddleware.PayloadFunc = func(data interface{}) jwt.MapClaims {
   432  			if j.AddClaimsHandler != nil && data != nil {
   433  				if identVal, customMap := j.AddClaimsHandler(data); util.LenTrim(identVal) > 0 || customMap != nil {
   434  					if customMap == nil {
   435  						return jwt.MapClaims{
   436  							j.IdentityKey: identVal,
   437  						}
   438  					} else {
   439  						if util.LenTrim(identVal) > 0 {
   440  							customMap[j.IdentityKey] = identVal
   441  						}
   442  
   443  						return customMap
   444  					}
   445  				} else {
   446  					return nil
   447  				}
   448  			} else {
   449  				return nil
   450  			}
   451  		}
   452  	}
   453  
   454  	// Callback function to retrieve the identity info via gin context's jwt claims by identityKey
   455  	if j.GetIdentityHandler != nil {
   456  		authMiddleware.IdentityHandler = func(context *gin.Context) interface{} {
   457  			if j.GetIdentityHandler == nil {
   458  				return nil
   459  			}
   460  
   461  			claims := jwt.ExtractClaims(context)
   462  
   463  			if claims != nil {
   464  				return j.GetIdentityHandler(claims)
   465  			} else {
   466  				return nil
   467  			}
   468  		}
   469  	}
   470  
   471  	// Callback function to handle custom login response,
   472  	// i = status code
   473  	// s = token
   474  	// t = expires
   475  	if j.LoginResponseHandler != nil {
   476  		authMiddleware.LoginResponse = func(context *gin.Context, i int, s string, t time.Time) {
   477  			j.LoginResponseHandler(context, i, s, t)
   478  		}
   479  	}
   480  
   481  	// Callback function to handle custom logout response,
   482  	// i = status code
   483  	if j.LogoutResponseHandler != nil {
   484  		authMiddleware.LogoutResponse = func(context *gin.Context, i int) {
   485  			j.LogoutResponseHandler(context, i)
   486  		}
   487  	}
   488  
   489  	// Callback function to handle custom token refresh response,
   490  	// i = status code,
   491  	// s = token
   492  	// t = expires
   493  	if j.RefreshTokenResponseHandler != nil {
   494  		authMiddleware.RefreshResponse = func(context *gin.Context, i int, s string, t time.Time) {
   495  			j.RefreshTokenResponseHandler(context, i, s, t)
   496  		}
   497  	}
   498  
   499  	// TimeFunc provides the current time.
   500  	// override it to use another time value.
   501  	// useful for testing or if server uses a different time zone than the tokens.
   502  	if j.TimeHandler != nil {
   503  		authMiddleware.TimeFunc = func() time.Time {
   504  			return j.TimeHandler()
   505  		}
   506  	}
   507  
   508  	// HTTP Status messages for when something in the JWT middleware fails,
   509  	// Check error (e) to determine the appropriate error message
   510  	if j.MiddlewareErrorEvaluator != nil {
   511  		authMiddleware.HTTPStatusMessageFunc = func(e error, c *gin.Context) string {
   512  			return j.MiddlewareErrorEvaluator(e, c)
   513  		}
   514  	}
   515  
   516  	// setup cookie options if applicable
   517  	authMiddleware.SendCookie = j.SendCookie
   518  	authMiddleware.CookieMaxAge = j.TokenValidDuration
   519  
   520  	if j.SecureCookie == nil {
   521  		authMiddleware.SecureCookie = true
   522  	} else {
   523  		authMiddleware.SecureCookie = *j.SecureCookie
   524  	}
   525  
   526  	if j.CookieHTTPOnly == nil {
   527  		authMiddleware.CookieHTTPOnly = true
   528  	} else {
   529  		authMiddleware.CookieHTTPOnly = *j.CookieHTTPOnly
   530  	}
   531  
   532  	authMiddleware.CookieDomain = j.CookieDomain
   533  	authMiddleware.CookieName = j.CookieName
   534  
   535  	if j.CookieSameSite == nil {
   536  		authMiddleware.CookieSameSite = http.SameSiteDefaultMode
   537  	} else {
   538  		authMiddleware.CookieSameSite = *j.CookieSameSite
   539  	}
   540  
   541  	// now init middleware to set default values if required var not set during setup
   542  	if errInit := authMiddleware.MiddlewareInit(); errInit != nil {
   543  		return fmt.Errorf("Init Gin Jwt Middleware Failed: %s", errInit.Error())
   544  	}
   545  
   546  	// setup login route for LoginHandler
   547  	if util.LenTrim(j.LoginRoutePath) > 0 {
   548  		log.Println("Jwt Auth Login Set: (Custom) " + j.LoginRoutePath)
   549  		g._ginEngine.POST(j.LoginRoutePath, authMiddleware.LoginHandler)
   550  	} else {
   551  		log.Println("Jwt Auth Login Set: (Default) " + "/login")
   552  		g._ginEngine.POST("/login", authMiddleware.LoginHandler)
   553  	}
   554  
   555  	// setup logout route for LogoutHandler
   556  	if util.LenTrim(j.LogoutRoutePath) > 0 {
   557  		log.Println("Jwt Auth Logout Set: (Custom) " + j.LogoutRoutePath)
   558  		g._ginEngine.POST(j.LogoutRoutePath, authMiddleware.LogoutHandler)
   559  	} else {
   560  		log.Println("Jwt Auth Logout Set: (Default) " + "/logout")
   561  		g._ginEngine.POST("/logout", authMiddleware.LogoutHandler)
   562  	}
   563  
   564  	// setup refresh token route for RefreshHandler
   565  	if util.LenTrim(j.RefreshTokenRoutePath) > 0 {
   566  		log.Println("Jwt Token Refresh Set: (Custom) " + j.RefreshTokenRoutePath)
   567  		g._ginEngine.GET(j.RefreshTokenRoutePath, authMiddleware.RefreshHandler)
   568  	} else {
   569  		log.Println("Jwt Token Refresh Set: (Default) " + "/refreshtoken")
   570  		g._ginEngine.GET("/refreshtoken", authMiddleware.RefreshHandler)
   571  	}
   572  
   573  	// setup no route handler
   574  	if j.NoRouteHandler != nil {
   575  		g._ginEngine.NoRoute(authMiddleware.MiddlewareFunc(), func(context *gin.Context) {
   576  			claims := jwt.ExtractClaims(context)
   577  			j.NoRouteHandler(claims, context)
   578  		})
   579  	}
   580  
   581  	// setup middleware successful
   582  	j._ginJwtMiddleware = authMiddleware
   583  	return nil
   584  }
   585  
   586  // AuthMiddleware returns the GinJwt Middleware HandlerFunc,
   587  // so that it can perform jwt related auth services,
   588  // for all route path defined within the same router group
   589  //
   590  // For example:
   591  //
   592  //	ginJwt := <Gin Jwt Struct Object Obtained>
   593  //	authGroup := g.Group("/auth")
   594  //	authGroup.Use(ginJwt.AuthMiddleware())
   595  //	authGroup.GET("/hello", ...) // route path within this auth group now secured by AuthMiddleware
   596  func (j *GinJwt) AuthMiddleware() gin.HandlerFunc {
   597  	if j._ginJwtMiddleware != nil {
   598  		return j._ginJwtMiddleware.MiddlewareFunc()
   599  	} else {
   600  		return nil
   601  	}
   602  }
   603  
   604  // ExtractClaims extracts jwt claims from context and return via map
   605  func (j *GinJwt) ExtractClaims(c *gin.Context) map[string]interface{} {
   606  	return jwt.ExtractClaims(c)
   607  }
   608  
   609  // =====================================================================================================================
   610  // Identity Helper Structs
   611  // =====================================================================================================================
   612  
   613  // UserLogin is a helper struct for use in authentication,
   614  // this struct represents a common use case for user based login request data,
   615  // however, any other custom struct can be used instead as desired
   616  type UserLogin struct {
   617  	Username string `form:"username" json:"username" binding:"required"`
   618  	Password string `form:"password" json:"password" binding:"required"`
   619  }
   620  
   621  // SystemLogin is a helper struct for use in authentication,
   622  // this struct represents a common use case for system based login request data,
   623  // however, any other custom struct can be used instead as desired
   624  type SystemLogin struct {
   625  	AccessID  string `form:"accessid" json:"accessid" binding:"required"`
   626  	SecretKey string `form:"secretkey" json:"secretkey" binding:"required"`
   627  }
   628  
   629  // UserInfo is a helper struct for use in authentication, authorization and identity,
   630  // this struct represents a common use case for user based identity info,
   631  // however, any other custom struct can be used instead as desired
   632  type UserInfo struct {
   633  	UserName  string
   634  	FirstName string
   635  	LastName  string
   636  	Scopes    []string
   637  }
   638  
   639  // SystemInfo is a helper struct for use in authentication, authorization, and identity,
   640  // this struct represents a common use case for system based identity info,
   641  // however, any other custom struct can be used instead as desired
   642  type SystemInfo struct {
   643  	SystemName string
   644  	Scopes     []string
   645  }
   646  
   647  /*
   648  	Example:
   649  
   650  	func helloHandler(c *gin.Context) {
   651  		claims := jwt.ExtractClaims(c)
   652  		user, _ := c.Get(identityKey)
   653  
   654  		c.JSON(200, gin.H{
   655  			"userID": claims[identityKey],
   656  			"userName": user.(*User).UserName,
   657  			"text": "Hello World",
   658  		})
   659  	}
   660  */