{"record":{"id":"791cc0f0b746cd2b","repo":"Tencent/WeKnora","slug":"decode-oidc-state-signature-w","errorCode":null,"errorMessage":"decode oidc state signature: %w","messagePattern":"decode oidc state signature: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/oidc_state.go","lineNumber":84,"sourceCode":"\tmac.Write(raw)\n\tsig := mac.Sum(nil)\n\treturn base64.RawURLEncoding.EncodeToString(raw) + \".\" + base64.RawURLEncoding.EncodeToString(sig), nil\n}\n\n// VerifyOIDCState validates the HMAC and freshness of a state token.\nfunc VerifyOIDCState(raw string) (*OIDCStatePayload, error) {\n\traw = strings.TrimSpace(raw)\n\tparts := strings.Split(raw, \".\")\n\tif len(parts) != 2 {\n\t\treturn nil, errors.New(\"invalid oidc state format\")\n\t}\n\tpayloadBytes, err := base64.RawURLEncoding.DecodeString(parts[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decode oidc state payload: %w\", err)\n\t}\n\tsigBytes, err := base64.RawURLEncoding.DecodeString(parts[1])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decode oidc state signature: %w\", err)\n\t}\n\tmac := hmac.New(sha256.New, []byte(oidcStateSigningKey()))\n\tmac.Write(payloadBytes)\n\tif !hmac.Equal(mac.Sum(nil), sigBytes) {\n\t\treturn nil, errors.New(\"oidc state signature mismatch\")\n\t}\n\tvar payload OIDCStatePayload\n\tif err := json.Unmarshal(payloadBytes, &payload); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal oidc state: %w\", err)\n\t}\n\tif strings.TrimSpace(payload.RedirectURI) == \"\" {\n\t\treturn nil, errors.New(\"state.redirect_uri is required\")\n\t}\n\tif payload.IssuedAt == 0 {\n\t\treturn nil, errors.New(\"state.iat is required\")\n\t}\n\tissuedAt := time.Unix(payload.IssuedAt, 0)\n\tif time.Since(issuedAt) > oidcStateMaxAge || time.Until(issuedAt) > time.Minute {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/oidc_state.go#L66-L102","documentation":"VerifyOIDCState could not base64url-decode the signature segment (the part after the dot) of the OIDC state token. The payload decoded fine, but the second segment is not valid unpadded base64url. This indicates corruption or tampering of the signature portion.","triggerScenarios":"Calling VerifyOIDCState with a state whose signature half was truncated (cut-and-paste loss), re-encoded with '=' padding, or replaced by non-base64 characters by a malicious or buggy intermediary.","commonSituations":"State passed through HTML-escaping that alters characters; manual splitting on '.' that drops part of the token; attackers altering the signature segment to try to bypass HMAC verification; storage layers stripping trailing characters.","solutions":["Regenerate the state via SignOIDCState and transmit it opaquely (hidden form field or cookie) without re-encoding","Log the received state length and compare with the issued length to detect truncation","Verify no middleware (WAF, template engine) mutates '-'/'_' characters in the token","Treat repeated occurrences as tampering attempts: reject the auth flow and return invalid_request to the client"],"exampleFix":"// before: template auto-escaping mangles the token\n<input value=\"{{ .State }}\">\n// after: emit pre-escaped safe attribute\n<input value=\"{{ .StateAttr }}\"> // rendered with html.SafeAttr of the raw base64url token","handlingStrategy":"try-catch","validationCode":"func sigSegmentIsValid(state string) bool {\n    parts := strings.Split(state, \".\")\n    if len(parts) != 2 { return false }\n    _, err := base64.RawURLEncoding.DecodeString(parts[1])\n    return err == nil\n}","typeGuard":"func hasDecodableSignature(state string) bool {\n    idx := strings.LastIndex(state, \".\")\n    if idx < 0 { return false }\n    _, err := base64.RawURLEncoding.DecodeString(state[idx+1:])\n    return err == nil\n}","tryCatchPattern":"payload, err := utils.VerifyOIDCState(rawState)\nif err != nil {\n    if strings.Contains(err.Error(), \"decode oidc state signature\") {\n        log.Warn(\"oidc state signature segment undecodable; possible tampering\", \"len\", len(rawState))\n        http.Error(w, \"invalid state\", http.StatusBadRequest)\n        return\n    }\n    http.Error(w, \"invalid state\", http.StatusBadRequest)\n}","preventionTips":["Pass the state through hidden form fields or cookies verbatim; log received vs issued lengths","Reject states whose length deviates from the issued length — truncation shows up here","Alert on repeated signature-decode failures from one client (tampering indicator)"],"tags":["oidc","hmac","base64","state-token"],"backgroundTag":"oidc-state-verification-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}