{"record":{"id":"d75d860bf3a82bdd","repo":"kubernetes/kops","slug":"decoding-authorization-token-w-d75d86","errorCode":null,"errorMessage":"decoding authorization token: %w","messagePattern":"decoding authorization token: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier/tpmverifier.go","lineNumber":85,"sourceCode":"\t\tcomputeClient: computeClient,\n\t\tcapiManager:   capiManager,\n\t}, nil\n}\n\nvar _ bootstrap.Verifier = (*tpmVerifier)(nil)\n\nfunc (v *tpmVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, authToken string, body []byte) (*bootstrap.VerifyResult, error) {\n\t// Reminder: we shouldn't trust any data we get from the client until we've checked the signature (and even then...)\n\t// Thankfully the GCE SDK does seem to escape the parameters correctly, for example.\n\n\tif !strings.HasPrefix(authToken, gcetpm.GCETPMAuthenticationTokenPrefix) {\n\t\treturn nil, bootstrap.ErrNotThisVerifier\n\t}\n\tauthToken = strings.TrimPrefix(authToken, gcetpm.GCETPMAuthenticationTokenPrefix)\n\n\ttokenBytes, err := base64.StdEncoding.DecodeString(authToken)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding authorization token: %w\", err)\n\t}\n\n\ttoken := &gcetpm.AuthToken{}\n\tif err = json.Unmarshal(tokenBytes, token); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshalling authorization token: %w\", err)\n\t}\n\n\ttokenData := gcetpm.AuthTokenData{}\n\tif err := json.Unmarshal(token.Data, &tokenData); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshalling authorization token data: %w\", err)\n\t}\n\n\t// Guard against replay attacks\n\tif tokenData.Audience != gcetpm.AudienceNodeAuthentication {\n\t\treturn nil, fmt.Errorf(\"incorrect Audience\")\n\t}\n\ttimeSkew := math.Abs(time.Since(time.Unix(tokenData.Timestamp, 0)).Seconds())\n\tif timeSkew > float64(v.opt.MaxTimeSkew) {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier/tpmverifier.go#L67-L103","documentation":"VerifyToken expects the incoming authorization token to be a base64-encoded gcetpm.AuthToken JSON prefixed with GCETPMAuthenticationTokenPrefix. If base64.StdEncoding.DecodeString fails on the prefix-stripped value, the token is malformed and cannot even be parsed. This is a client-supplied data validation failure.","triggerScenarios":"A node sends an Authorization token whose value after the prefix is not valid standard base64: truncated token, token produced by a different/newer client format, token double-encoded or with URL-safe base64 instead of StdEncoding, or garbage/corrupted bytes in transit.","commonSituations":"Version skew between kops node binary (signer) and control-plane (verifier) changing token format; a proxy or middleware mangling the header; client sending an empty or differently formatted token; manual testing with curl pasting an incorrect token.","solutions":["Regenerate the token with the matching kops/gcetpm version on the node","Verify both signer and verifier use the same GCETPMAuthenticationTokenPrefix and encoding","Ensure the full token string reaches the verifier (no truncation by proxies/headers)","Check that the client uses base64.StdEncoding (not RawURLEncoding) when constructing the token","Confirm the client is actually the TPM authenticator and not sending a different credential type"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"authHeader := r.Header.Get(\"Authorization\")\nif !strings.HasPrefix(authHeader, gcetpm.GCETPMAuthenticationTokenPrefix) {\n    return bootstrap.ErrNotThisVerifier\n}\nb64 := strings.TrimPrefix(authHeader, gcetpm.GCETPMAuthenticationTokenPrefix)\nif _, err := base64.StdEncoding.DecodeString(b64); err != nil {\n    return fmt.Errorf(\"client sent non-base64 token\")\n}","typeGuard":"func isValidTPMToken(header string) bool {\n    if !strings.HasPrefix(header, gcetpm.GCETPMAuthenticationTokenPrefix) {\n        return false\n    }\n    _, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(header, gcetpm.GCETPMAuthenticationTokenPrefix))\n    return err == nil\n}","tryCatchPattern":"token, err := verifier.VerifyToken(ctx, rawToken, request)\nif err != nil {\n    if strings.Contains(err.Error(), \"decoding authorization token\") {\n        return fmt.Errorf(\"malformed token from node; check client version/format: %w\", err)\n    }\n    return err\n}","preventionTips":["Keep signer and verifier binaries on the same kops release","Never truncate or rewrite the Authorization header in proxies","Validate token format client-side before sending","Use the standard CreateToken output unmodified"],"tags":["gce","tpm","base64","token-validation"],"backgroundTag":"malformed-auth-token","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}