Tencent/WeKnora · error
decode oidc state signature: %w
Error message
decode oidc state signature: %w
What it means
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.
Source
Thrown at internal/utils/oidc_state.go:84
mac.Write(raw)
sig := mac.Sum(nil)
return base64.RawURLEncoding.EncodeToString(raw) + "." + base64.RawURLEncoding.EncodeToString(sig), nil
}
// VerifyOIDCState validates the HMAC and freshness of a state token.
func VerifyOIDCState(raw string) (*OIDCStatePayload, error) {
raw = strings.TrimSpace(raw)
parts := strings.Split(raw, ".")
if len(parts) != 2 {
return nil, errors.New("invalid oidc state format")
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return nil, fmt.Errorf("decode oidc state payload: %w", err)
}
sigBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("decode oidc state signature: %w", err)
}
mac := hmac.New(sha256.New, []byte(oidcStateSigningKey()))
mac.Write(payloadBytes)
if !hmac.Equal(mac.Sum(nil), sigBytes) {
return nil, errors.New("oidc state signature mismatch")
}
var payload OIDCStatePayload
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
return nil, fmt.Errorf("unmarshal oidc state: %w", err)
}
if strings.TrimSpace(payload.RedirectURI) == "" {
return nil, errors.New("state.redirect_uri is required")
}
if payload.IssuedAt == 0 {
return nil, errors.New("state.iat is required")
}
issuedAt := time.Unix(payload.IssuedAt, 0)
if time.Since(issuedAt) > oidcStateMaxAge || time.Until(issuedAt) > time.Minute {View on GitHub (pinned to 988cbb0330)
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
Example fix
// before: template auto-escaping mangles the token
<input value="{{ .State }}">
// after: emit pre-escaped safe attribute
<input value="{{ .StateAttr }}"> // rendered with html.SafeAttr of the raw base64url token Defensive patterns
Strategy: try-catch
Validate before calling
func sigSegmentIsValid(state string) bool {
parts := strings.Split(state, ".")
if len(parts) != 2 { return false }
_, err := base64.RawURLEncoding.DecodeString(parts[1])
return err == nil
} Type guard
func hasDecodableSignature(state string) bool {
idx := strings.LastIndex(state, ".")
if idx < 0 { return false }
_, err := base64.RawURLEncoding.DecodeString(state[idx+1:])
return err == nil
} Try / catch
payload, err := utils.VerifyOIDCState(rawState)
if err != nil {
if strings.Contains(err.Error(), "decode oidc state signature") {
log.Warn("oidc state signature segment undecodable; possible tampering", "len", len(rawState))
http.Error(w, "invalid state", http.StatusBadRequest)
return
}
http.Error(w, "invalid state", http.StatusBadRequest)
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- decode oidc state payload: %w
- unmarshal oidc state: %w
- OIDC provider returned no user claims
- empty JWK modulus or exponent
- invalid JWK exponent value
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/791cc0f0b746cd2b.
Report an issue: GitHub.