{"record":{"id":"74b88aeee6b1888d","repo":"kataras/iris","slug":"refresh-max-age-should-be-bigger-than-access-token","errorCode":null,"errorMessage":"refresh max age should be bigger than access token's one[%d - %d]","messagePattern":"refresh max age should be bigger than access token's one\\[(.+?) - (.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/jwt/signer.go","lineNumber":84,"sourceCode":"}\n\n// Sign generates a new token based on the given \"claims\" which is valid up to \"s.MaxAge\".\nfunc (s *Signer) Sign(claims any, opts ...SignOption) ([]byte, error) {\n\tif len(opts) > 0 {\n\t\topts = append(opts, s.Options...)\n\t} else {\n\t\topts = s.Options\n\t}\n\n\treturn SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, opts...)\n}\n\n// NewTokenPair accepts the access and refresh claims plus the life time duration for the refresh token\n// and generates a new token pair which can be sent to the client.\n// The same token pair can be json-decoded.\nfunc (s *Signer) NewTokenPair(accessClaims any, refreshClaims any, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {\n\tif refreshMaxAge <= s.MaxAge {\n\t\treturn TokenPair{}, fmt.Errorf(\"refresh max age should be bigger than access token's one[%d - %d]\", refreshMaxAge, s.MaxAge)\n\t}\n\n\taccessToken, err := s.Sign(accessClaims, accessOpts...)\n\tif err != nil {\n\t\treturn TokenPair{}, err\n\t}\n\n\trefreshToken, err := Sign(s.Alg, s.Key, refreshClaims, MaxAge(refreshMaxAge))\n\tif err != nil {\n\t\treturn TokenPair{}, err\n\t}\n\n\ttokenPair := jwt.NewTokenPair(accessToken, refreshToken)\n\treturn tokenPair, nil\n}\n","sourceCodeStart":66,"sourceCodeEnd":100,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/middleware/jwt/signer.go#L66-L100","documentation":"iris-jwt's Signer.NewTokenPair requires the refresh token's MaxAge (refreshMaxAge) to be strictly greater than the access token's configured MaxAge (s.MaxAge). The guard prevents creating a pair where the refresh token expires before or exactly when the access token does, which would make refresh flows useless. The error message prints both durations.","triggerScenarios":"Calling signer.NewTokenPair(accessClaims, refreshClaims, refreshMaxAge) where refreshMaxAge <= s.MaxAge, e.g. both set to 15 minutes, or refreshMaxAge equal to the access lifetime.","commonSituations":"Misreading refreshMaxAge as an absolute timestamp instead of a duration, copying the same constant for both lifetimes, or lowering Signer MaxAge in config after hardcoding a refresh duration.","solutions":["Pass a refreshMaxAge clearly larger than the signer's MaxAge (e.g. access 15m, refresh 7*24h).","Review the Signer's MaxAge configuration (WithMaxAge) and pick refreshMaxAge relative to it, not equal to it.","If you intended refreshMaxAge as a timestamp, note it is a time.Duration from now; convert accordingly."],"exampleFix":"// before\nsigner.MaxAge = 15 * time.Minute\npair, err := signer.NewTokenPair(access, refresh, 15*time.Minute) // error\n\n// after\npair, err := signer.NewTokenPair(access, refresh, 7*24*time.Hour) // refresh outlives access","handlingStrategy":"validation","validationCode":"if refreshMaxAge <= signer.MaxAge { return errors.New(\"refreshMaxAge must exceed access MaxAge\") }","typeGuard":"func validTokenPairDurations(refresh, access time.Duration) bool { return refresh > access }","tryCatchPattern":"pair, err := signer.NewTokenPair(ac, rc, refreshMaxAge); if err != nil && strings.Contains(err.Error(), \"refresh max age\") { return fmt.Errorf(\"config: %w\", err) }","preventionTips":["Define access and refresh lifetimes as named constants with refresh >> access (e.g. 15m vs 7d).","Derive refreshMaxAge from the signer's MaxAge programmatically: s.MaxAge + margin.","Remember refreshMaxAge is a Duration from now, not an expiry timestamp."],"tags":["go","iris","jwt","token-lifetime","configuration"],"backgroundTag":"jwt-token-lifetime-misconfigured","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}