kubernetes/kops · error
unmarshalling authorization token data: %w
Error message
unmarshalling authorization token data: %w
What it means
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.
Source
Thrown at pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go:84
func (v *verifier) parseTokenData(tokenPrefix string, authToken string, body []byte) (*pkibootstrap.AuthToken, *pkibootstrap.AuthTokenData, error) {
if !strings.HasPrefix(authToken, tokenPrefix) {
return nil, nil, bootstrap.ErrNotThisVerifier
}
authToken = strings.TrimPrefix(authToken, tokenPrefix)
tokenBytes, err := base64.StdEncoding.DecodeString(authToken)
if err != nil {
return nil, nil, fmt.Errorf("decoding authorization token: %w", err)
}
token := &pkibootstrap.AuthToken{}
if err = json.Unmarshal(tokenBytes, token); err != nil {
return nil, nil, fmt.Errorf("unmarshalling authorization token: %w", err)
}
tokenData := &pkibootstrap.AuthTokenData{}
if err := json.Unmarshal(token.Data, tokenData); err != nil {
return nil, nil, fmt.Errorf("unmarshalling authorization token data: %w", err)
}
// Guard against replay attacks
if tokenData.Audience != pkibootstrap.AudienceNodeAuthentication {
return nil, nil, fmt.Errorf("incorrect Audience")
}
timeSkew := math.Abs(time.Since(time.Unix(tokenData.Timestamp, 0)).Seconds())
if timeSkew > float64(v.opt.MaxTimeSkew) {
return nil, nil, fmt.Errorf("incorrect Timestamp %v", tokenData.Timestamp)
}
// Verify the token has signed the body content.
requestHash := sha256.Sum256(body)
if !bytes.Equal(requestHash[:], tokenData.RequestHash) {
return nil, nil, fmt.Errorf("incorrect RequestHash")
}
return token, tokenData, nilView on GitHub (pinned to 4c8573c808)
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.
Example fix
// before: hand-built token with string fields
token := map[string]string{"data": base64.StdEncoding.EncodeToString(payload), "signature": sigB64}
// after: use the library's types so field types match AuthTokenData
token := &pkibootstrap.AuthToken{Data: payload, Signature: signature}
b, _ := json.Marshal(token)
header := pkibootstrap.AuthenticationTokenPrefix + base64.StdEncoding.EncodeToString(b) Defensive patterns
Strategy: validation
Validate before calling
// Decode and shape-check the token before sending it to kops-controller
func tokenDataLooksValid(authHeader string) bool {
const prefix = "kops.k8s.io/1.30/pki" // AuthenticationTokenPrefix of your kOps version
if !strings.HasPrefix(authHeader, prefix) {
return false
}
raw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, prefix))
if err != nil {
return false
}
var t pkibootstrap.AuthToken
if err := json.Unmarshal(raw, &t); err != nil || len(t.Data) == 0 {
return false
}
var d pkibootstrap.AuthTokenData
return json.Unmarshal(t.Data, &d) == nil && d.Instance != ""
} Type guard
func isAuthTokenData(b []byte) (*pkibootstrap.AuthTokenData, bool) {
var d pkibootstrap.AuthTokenData
if err := json.Unmarshal(b, &d); err != nil {
return nil, false
}
return &d, true
} Try / catch
result, err := verifier.VerifyToken(ctx, req, authToken, body)
if err != nil {
if strings.Contains(err.Error(), "unmarshalling authorization token data") {
// token payload is malformed; log the decoded claims for diagnosis and fail the request
klog.Errorf("bootstrap token has invalid Data payload: %v", err)
return nil, fmt.Errorf("malformed bootstrap token: %w", err)
}
return nil, err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- incorrect Audience
- incorrect Timestamp %v
- incorrect RequestHash
- failed to verify claim signature for node
- node identity is required
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/9f2af46c53c516d6.
Report an issue: GitHub.