{"record":{"id":"0dbdcee21e191737","repo":"go-kit/kit","slug":"jwt-is-expired","errorCode":null,"errorMessage":"JWT is expired","messagePattern":"JWT is expired","errorType":"exception","errorClass":"ErrTokenExpired","httpStatus":401,"severity":"error","filePath":"auth/jwt/middleware.go","lineNumber":36,"sourceCode":"\t//\n\t// Deprecated: prefer JWTContextKey.\n\tJWTTokenContextKey = JWTContextKey\n\n\t// JWTClaimsContextKey holds the key used to store the JWT Claims in the\n\t// context.\n\tJWTClaimsContextKey contextKey = \"JWTClaims\"\n)\n\nvar (\n\t// ErrTokenContextMissing denotes a token was not passed into the parsing\n\t// middleware's context.\n\tErrTokenContextMissing = errors.New(\"token up for parsing was not passed through the context\")\n\n\t// ErrTokenInvalid denotes a token was not able to be validated.\n\tErrTokenInvalid = errors.New(\"JWT was invalid\")\n\n\t// ErrTokenExpired denotes a token's expire header (exp) has since passed.\n\tErrTokenExpired = errors.New(\"JWT is expired\")\n\n\t// ErrTokenMalformed denotes a token was not formatted as a JWT.\n\tErrTokenMalformed = errors.New(\"JWT is malformed\")\n\n\t// ErrTokenNotActive denotes a token's not before header (nbf) is in the\n\t// future.\n\tErrTokenNotActive = errors.New(\"token is not valid yet\")\n\n\t// ErrUnexpectedSigningMethod denotes a token was signed with an unexpected\n\t// signing method.\n\tErrUnexpectedSigningMethod = errors.New(\"unexpected signing method\")\n)\n\n// NewSigner creates a new JWT generating middleware, specifying key ID,\n// signing string, signing method and the claims you would like it to contain.\n// Tokens are signed with a Key ID header (kid) which is useful for determining\n// the key to use for parsing. Particularly useful for clients.\nfunc NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/go-kit/kit/blob/78fbbceece7bbcf073bee814a7772f4397ea756c/auth/jwt/middleware.go#L18-L54","documentation":"Returned by jwt.NewParser when the underlying jwt.ValidationError has the ValidationErrorExpired bit set: the token's exp claim is in the past. The token may be otherwise perfectly well-formed and correctly signed — it has simply aged out of its validity window.","triggerScenarios":"Calling a protected endpoint with a token whose exp has elapsed; long-lived client (CLI, mobile app, background job) reusing a token issued hours/days earlier; clock skew between the issuing host and the verifying server making a just-issued token appear expired; signer that computed exp in milliseconds instead of seconds.","commonSituations":"No token refresh flow implemented; tokens minted with a very short lifetime; NTP drift on VMs/containers; migration from one auth provider that used different token lifetimes; caching a token in config instead of fetching fresh ones.","solutions":["Implement refresh: on this error, obtain a new token (re-login or refresh grant) and retry the request once","Issue tokens with an appropriate exp (e.g. 15–60 min) and refresh them proactively before expiry","Sync clocks (NTP/chrony) on signer and verifier to eliminate skew","Ensure exp is expressed in seconds since epoch (NumericDate), not milliseconds"],"exampleFix":"// before: token signed once with a short life and reused forever\nclaims := &jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute))}\n\n// after: sane lifetime plus client-side refresh-and-retry\nif _, err := ep(ctx, req); err != nil && errors.Is(err, jwt.ErrTokenExpired) {\n\tnewTok := refreshToken() // re-auth / refresh grant\n\tctx = context.WithValue(ctx, jwt.JWTContextKey, newTok)\n\t_, err = ep(ctx, req)\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"resp, err := ep(ctx, req)\nif err != nil && errors.Is(err, jwt.ErrTokenExpired) {\n\tif tok, ok := refreshToken(); ok { // re-login or refresh grant\n\t\tctx = context.WithValue(ctx, jwt.JWTContextKey, tok)\n\t\tresp, err = ep(ctx, req) // single retry with the fresh token\n\t}\n}\nreturn resp, err","preventionTips":["Refresh tokens proactively at ~80% of lifetime instead of waiting for rejection","Run NTP on all hosts so expiry decisions are trustworthy","Store tokens with their expiry and have background jobs fetch new ones before starting work"],"tags":["go","go-kit","jwt","authentication","token-expiry"],"backgroundTag":null,"analyzedSha":"78fbbceece7bbcf073bee814a7772f4397ea756c","analyzedAt":"2026-08-15T22:31:35.570Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}