{"record":{"id":"9f2af46c53c516d6","repo":"kubernetes/kops","slug":"unmarshalling-authorization-token-data-w","errorCode":null,"errorMessage":"unmarshalling authorization token data: %w","messagePattern":"unmarshalling authorization token data: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go","lineNumber":84,"sourceCode":"func (v *verifier) parseTokenData(tokenPrefix string, authToken string, body []byte) (*pkibootstrap.AuthToken, *pkibootstrap.AuthTokenData, error) {\n\tif !strings.HasPrefix(authToken, tokenPrefix) {\n\t\treturn nil, nil, bootstrap.ErrNotThisVerifier\n\t}\n\tauthToken = strings.TrimPrefix(authToken, tokenPrefix)\n\n\ttokenBytes, err := base64.StdEncoding.DecodeString(authToken)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"decoding authorization token: %w\", err)\n\t}\n\n\ttoken := &pkibootstrap.AuthToken{}\n\tif err = json.Unmarshal(tokenBytes, token); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"unmarshalling authorization token: %w\", err)\n\t}\n\n\ttokenData := &pkibootstrap.AuthTokenData{}\n\tif err := json.Unmarshal(token.Data, tokenData); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"unmarshalling authorization token data: %w\", err)\n\t}\n\n\t// Guard against replay attacks\n\tif tokenData.Audience != pkibootstrap.AudienceNodeAuthentication {\n\t\treturn nil, 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) {\n\t\treturn nil, nil, fmt.Errorf(\"incorrect Timestamp %v\", tokenData.Timestamp)\n\t}\n\n\t// Verify the token has signed the body content.\n\trequestHash := sha256.Sum256(body)\n\tif !bytes.Equal(requestHash[:], tokenData.RequestHash) {\n\t\treturn nil, nil, fmt.Errorf(\"incorrect RequestHash\")\n\t}\n\n\treturn token, tokenData, nil","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go#L66-L102","documentation":"This error is returned by parseTokenData in the kops-controller PKI verifier when the inner claims payload (AuthToken.Data, the signed JSON claims blob inside the outer AuthToken envelope) fails to json.Unmarshal into pkibootstrap.AuthTokenData. The outer token parsed fine and base64 decoding succeeded, but the Data field is not valid JSON matching the AuthTokenData schema (instance, keyID, requestHash, timestamp, audience). It indicates a malformed or truncated token, almost always produced by a client-side serialization problem rather than an attacker.","triggerScenarios":"Occurs inside VerifyToken (called by kops-controller when nodeup posts a bootstrap request with a Authorization header carrying the AuthenticationTokenPrefix token) whenever json.Unmarshal(token.Data, &AuthTokenData{}) fails: Data is empty/null, Data is not valid JSON (e.g. base64 of raw bytes instead of JSON), the JSON is truncated, or field types mismatch (e.g. requestHash encoded as a base64 string instead of []byte, timestamp as a string instead of int64) due to a client/server version skew in the token format.","commonSituations":"A custom or hand-rolled node bootstrap client that builds the Authorization header manually and double-encodes or omits the Data field; a kOps version skew where nodeup from a different release emits an older token layout; a proxy or middleware that rewrites/strips the Authorization header body; debugging reproducers that paste a partially-copied token.","solutions":["Ensure the node is running the nodeup/kops version matching the kops-controller, so CreateToken (pkg/bootstrap/pkibootstrap/pkisigner.go:104) builds the token instead of hand-rolling it.","Decode the token offline to inspect it: strip the prefix, base64-std-decode, json.Unmarshal into AuthToken, then check that Data is valid JSON with the expected AuthTokenData fields.","Verify no proxy/ingress in front of kops-controller rewrites the Authorization header or truncates the body.","Check that Data was set to the exact []byte payload that was signed (json.Marshal of AuthTokenData), not a re-encoded or pretty-printed copy."],"exampleFix":"// before: hand-built token with string fields\ntoken := map[string]string{\"data\": base64.StdEncoding.EncodeToString(payload), \"signature\": sigB64}\n// after: use the library's types so field types match AuthTokenData\ntoken := &pkibootstrap.AuthToken{Data: payload, Signature: signature}\nb, _ := json.Marshal(token)\nheader := pkibootstrap.AuthenticationTokenPrefix + base64.StdEncoding.EncodeToString(b)","handlingStrategy":"validation","validationCode":"// Decode and shape-check the token before sending it to kops-controller\nfunc tokenDataLooksValid(authHeader string) bool {\n\tconst prefix = \"kops.k8s.io/1.30/pki\" // AuthenticationTokenPrefix of your kOps version\n\tif !strings.HasPrefix(authHeader, prefix) {\n\t\treturn false\n\t}\n\traw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, prefix))\n\tif err != nil {\n\t\treturn false\n\t}\n\tvar t pkibootstrap.AuthToken\n\tif err := json.Unmarshal(raw, &t); err != nil || len(t.Data) == 0 {\n\t\treturn false\n\t}\n\tvar d pkibootstrap.AuthTokenData\n\treturn json.Unmarshal(t.Data, &d) == nil && d.Instance != \"\"\n}","typeGuard":"func isAuthTokenData(b []byte) (*pkibootstrap.AuthTokenData, bool) {\n\tvar d pkibootstrap.AuthTokenData\n\tif err := json.Unmarshal(b, &d); err != nil {\n\t\treturn nil, false\n\t}\n\treturn &d, true\n}","tryCatchPattern":"result, err := verifier.VerifyToken(ctx, req, authToken, body)\nif err != nil {\n\tif strings.Contains(err.Error(), \"unmarshalling authorization token data\") {\n\t\t// token payload is malformed; log the decoded claims for diagnosis and fail the request\n\t\tklog.Errorf(\"bootstrap token has invalid Data payload: %v\", err)\n\t\treturn nil, fmt.Errorf(\"malformed bootstrap token: %w\", err)\n\t}\n\treturn nil, err\n}","preventionTips":["Always mint tokens via pkibootstrap.NewAuthenticator/CreateToken instead of constructing the Authorization header by hand.","Pin nodeup and kops-controller to the same kOps version in your cluster rollout.","Add an offline smoke test that round-trips CreateToken output through the verifier's parse path in CI.","Audit any proxy in front of kops-controller for header/body rewriting."],"tags":["go","json","authentication","pki","bootstrap"],"backgroundTag":"malformed-token-payload","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}