{"record":{"id":"82f5bbc0dd23b5b7","repo":"Tencent/WeKnora","slug":"token-not-yet-valid","errorCode":null,"errorMessage":"token not yet valid","messagePattern":"token not yet valid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/middleware/auth.go","lineNumber":646,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", token.Header[\"alg\"])\n\t\t}\n\t\treturn []byte(secret), nil\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif token == nil || !token.Valid {\n\t\treturn \"\", errors.New(\"invalid external user token\")\n\t}\n\texp, err := claims.GetExpirationTime()\n\tif err != nil || exp == nil {\n\t\treturn \"\", errors.New(\"missing expiration\")\n\t}\n\tif time.Until(exp.Time) > maxExternalUserTokenTTL {\n\t\treturn \"\", fmt.Errorf(\"token lifetime exceeds %s\", maxExternalUserTokenTTL)\n\t}\n\tif nbf, nbfErr := claims.GetNotBefore(); nbfErr == nil && nbf != nil && time.Now().Before(nbf.Time) {\n\t\treturn \"\", errors.New(\"token not yet valid\")\n\t}\n\tif got := principalTenantIDFromClaims(claims); got != tenantID {\n\t\treturn \"\", fmt.Errorf(\"workspace mismatch: got %d want %d\", got, tenantID)\n\t}\n\tsub, _ := claims[\"sub\"].(string)\n\tsub = strings.TrimSpace(sub)\n\tif sub == \"\" {\n\t\treturn \"\", errors.New(\"missing subject\")\n\t}\n\treturn sub, nil\n}\n\nfunc validateExternalUserID(id string) error {\n\tid = strings.TrimSpace(id)\n\tif id == \"\" {\n\t\treturn errors.New(\"empty external user id\")\n\t}\n\tif len(id) > maxExternalUserIDLen {","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/middleware/auth.go#L628-L664","documentation":"verifyExternalUserJWT validates an externally-issued user JWT used for API principal resolution. After checking expiry and max lifetime, it rejects any token whose 'nbf' (not-before) claim is in the future. This is thrown because the token is cryptographically valid but is being used before its official start time.","triggerScenarios":"Calling resolveAPIPrincipal with an external user JWT whose nbf claim is later than the server's current clock. Typically caused by clock skew between token issuer and this server, or by minting a token with a not-before time set too far ahead.","commonSituations":"Distributed deployments where the token-issuing service's clock is ahead of the API server's clock (NTP drift); tokens generated for scheduled activation (future-dated tokens) used immediately; local dev environments without synchronized clocks.","solutions":["Synchronize clocks: run NTP/chrony on both the token issuer and the API server so skew is well under the token's leeway.","Check how the external token is minted: set the nbf claim to now (or omit it) instead of a future timestamp.","Retry after waiting until nbf passes, if the token is intentionally future-dated.","Compare the raw token's nbf value against server time (decode the JWT payload) to confirm the skew amount before issuing new tokens."],"exampleFix":"// before: issuer minted token with future nbf\ntoken := issueJWT(sub, tenantID, jwt.MapClaims{\"nbf\": time.Now().Add(5 * time.Minute).Unix()})\n// after: token valid immediately\ntoken := issueJWT(sub, tenantID, jwt.MapClaims{\"nbf\": time.Now().Add(-30 * time.Second).Unix()})","handlingStrategy":"validation","validationCode":"// decode payload without verification, check nbf before calling the API\nparts := strings.Split(token, \".\")\npayload, _ := base64.RawURLEncoding.DecodeString(parts[1])\nvar c map[string]any\njson.Unmarshal(payload, &c)\nif nbf, ok := c[\"nbf\"].(float64); ok && time.Now().Before(time.Unix(int64(nbf), 0)) {\n    return fmt.Errorf(\"token not valid until %v, check clock sync\", time.Unix(int64(nbf), 0))\n}","typeGuard":"func tokenIsActive(claims jwt.MapClaims) bool {\n    if nbf, err := claims.GetNotBefore(); err != nil || nbf == nil {\n        return err == nil\n    } else {\n        return !time.Now().Before(nbf.Time)\n    }\n}","tryCatchPattern":null,"preventionTips":["Run NTP/chrony on all services that issue or verify tokens.","Mint tokens with nbf in the past (or omitted) unless future activation is intended.","Monitor for this error as a clock-skew alarm in production dashboards."],"tags":["auth","jwt","clock-skew"],"backgroundTag":"jwt-token-not-yet-valid","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}