{"record":{"id":"fe367735752e9112","repo":"dgraph-io/dgraph","slug":"token-contains-an-invalid-json-web-token-token-is","errorCode":null,"errorMessage":"token contains an invalid JSON Web Token: Token is expired","messagePattern":"token contains an invalid JSON Web Token: Token is expired","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/jwt_helper.go","lineNumber":43,"sourceCode":"\t\treturn []byte(kb)\n\t}\n\treturn k\n}\n\nfunc ParseJWT(jwtStr string) (jwt.MapClaims, error) {\n\ttoken, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) {\n\t\tif WorkerConfig.AclJwtAlg == nil {\n\t\t\treturn nil, errors.Errorf(\"ACL is disabled\")\n\t\t}\n\t\tif token.Method.Alg() != WorkerConfig.AclJwtAlg.Alg() {\n\t\t\treturn nil, errors.Errorf(\"unexpected signing method in token: %v\", token.Header[\"alg\"])\n\t\t}\n\t\treturn MaybeKeyToBytes(WorkerConfig.AclPublicKey), nil\n\t})\n\tif err != nil {\n\t\t// This is for backward compatibility in clients\n\t\tif errors.Is(err, jwt.ErrTokenExpired) {\n\t\t\terr = errors.Wrap(errTokenExpired, jwt.ErrTokenInvalidClaims.Error())\n\t\t}\n\t\treturn nil, errors.Wrapf(err, \"unable to parse jwt token\")\n\t}\n\n\tclaims, ok := token.Claims.(jwt.MapClaims)\n\tif !ok || !token.Valid {\n\t\treturn nil, errors.Errorf(\"claims in jwt token is not map claims\")\n\t}\n\treturn claims, nil\n}\n\nfunc ExtractUserName(jwtToken string) (string, error) {\n\tclaims, err := ParseJWT(jwtToken)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tuserId, ok := claims[\"userid\"].(string)\n\tif !ok {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/x/jwt_helper.go#L25-L61","documentation":"When jwt.Parse fails because the token's exp claim is in the past, ParseJWT rewraps it with errTokenExpired and jwt.ErrTokenInvalidClaims ('token contains an invalid JWT: Token is expired') for backward compatibility. It means the JWT is structurally fine but its lifetime has ended, so it must be refreshed.","triggerScenarios":"Calling ParseJWT/ExtractUserName/ExtractNamespaceFromJwt with an access JWT whose exp claim is earlier than the current time (errors.Is(err, jwt.ErrTokenExpired) is true).","commonSituations":"Long-running client processes caching an access JWT past its TTL (default access-ttl); system clock skew between client and server; tokens minted long ago and replayed after server restarts.","solutions":["Obtain a fresh access JWT (e.g. via refresh token / Login API) and retry the request.","Reduce client TTL assumptions: re-login or refresh before/at expiry instead of caching tokens indefinitely.","Check clock synchronization (NTP) if the token looks freshly minted yet is reported expired.","If the token should still be valid, inspect its exp claim (jwt.io / decoder) to confirm actual expiry."],"exampleFix":"// before: reuse cached token forever\ntoken := cachedToken\n// after: refresh when expired\nif claims, err := x.ParseJWT(token); err != nil && errors.Is(err, jwt.ErrTokenExpired) {\n    token = refreshToken(cfg.RefreshJwt)\n}","handlingStrategy":"retry","validationCode":"claims := jwt.MapClaims{}\nif _, _, err := jwt.NewParser().ParseUnverified(token, claims); err == nil {\n    if exp, err2 := claims.GetExpirationTime(); err2 == nil && exp != nil && exp.Before(time.Now()) {\n        token = refreshAccessToken() // fetch new token before calling the API\n    }\n}","typeGuard":"func isExpiredJwtErr(err error) bool { return err != nil && errors.Is(err, jwt.ErrTokenExpired) }","tryCatchPattern":"claims, err := x.ParseJWT(token)\nif errors.Is(err, jwt.ErrTokenExpired) { // matches wrapped errTokenExpired\n    token = refreshToken()\n    claims, err = x.ParseJWT(token)\n}","preventionTips":["Track exp claim client-side and refresh proactively (e.g. at 80% of TTL).","Use refresh tokens to obtain new access tokens automatically.","Synchronize clocks with NTP on all nodes.","Do not cache access JWTs beyond their access-ttl."],"tags":["jwt","expired","authentication","token"],"backgroundTag":"jwt-token-expired","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}