kubernetes/kops · error

unmarshalling authorization token: %w

Error message

unmarshalling authorization token: %w

What it means

After base64 decoding, VerifyToken unmarshals the bytes into gcetpm.AuthToken {Data []byte, Signature []byte}. If json.Unmarshal fails, the token bytes are not the expected JSON structure — the payload is corrupt or from an incompatible format.

Source

Thrown at upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier/tpmverifier.go:90

var _ bootstrap.Verifier = (*tpmVerifier)(nil)

func (v *tpmVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, authToken string, body []byte) (*bootstrap.VerifyResult, error) {
	// Reminder: we shouldn't trust any data we get from the client until we've checked the signature (and even then...)
	// Thankfully the GCE SDK does seem to escape the parameters correctly, for example.

	if !strings.HasPrefix(authToken, gcetpm.GCETPMAuthenticationTokenPrefix) {
		return nil, bootstrap.ErrNotThisVerifier
	}
	authToken = strings.TrimPrefix(authToken, gcetpm.GCETPMAuthenticationTokenPrefix)

	tokenBytes, err := base64.StdEncoding.DecodeString(authToken)
	if err != nil {
		return nil, fmt.Errorf("decoding authorization token: %w", err)
	}

	token := &gcetpm.AuthToken{}
	if err = json.Unmarshal(tokenBytes, token); err != nil {
		return nil, fmt.Errorf("unmarshalling authorization token: %w", err)
	}

	tokenData := gcetpm.AuthTokenData{}
	if err := json.Unmarshal(token.Data, &tokenData); err != nil {
		return nil, fmt.Errorf("unmarshalling authorization token data: %w", err)
	}

	// Guard against replay attacks
	if tokenData.Audience != gcetpm.AudienceNodeAuthentication {
		return nil, fmt.Errorf("incorrect Audience")
	}
	timeSkew := math.Abs(time.Since(time.Unix(tokenData.Timestamp, 0)).Seconds())
	if timeSkew > float64(v.opt.MaxTimeSkew) {
		return nil, fmt.Errorf("incorrect Timestamp %v", tokenData.Timestamp)
	}

	// Verify the token has signed the body content.
	requestHash := sha256.Sum256(body)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Rebuild/redeploy node and control-plane components so both use the same gcetpm.AuthToken schema
  2. Inspect the decoded payload (echo <token> | base64 -d) to see its actual structure
  3. Ensure the client signs via gcetpm authenticator, not another auth mechanism
  4. Add no manual JSON construction — always produce tokens via CreateToken
Defensive patterns

Strategy: validation

Validate before calling

tokenBytes, err := base64.StdEncoding.DecodeString(b64)
if err != nil { return err }
var probe map[string]json.RawMessage
if err := json.Unmarshal(tokenBytes, &probe); err != nil {
    return fmt.Errorf("payload is not a JSON object")
}
if _, ok := probe["Data"]; !ok {
    return fmt.Errorf("missing Data field")
}

Type guard

func looksLikeAuthToken(tokenBytes []byte) bool {
    var probe struct {
        Data      json.RawMessage `json:"Data"`
        Signature json.RawMessage `json:"Signature"`
    }
    return json.Unmarshal(tokenBytes, &probe) == nil && len(probe.Data) > 0
}

Try / catch

token, err := verifier.VerifyToken(ctx, rawToken, request)
if err != nil && strings.Contains(err.Error(), "unmarshalling authorization token") {
    return fmt.Errorf("incompatible token schema; align node/verifier versions: %w", err)
}

Prevention

When it happens

Trigger: json.Unmarshal(tokenBytes, token) fails: token payload is valid base64 but not the expected JSON object with Data/Signature fields (wrong types, e.g. strings instead of base64 byte fields, or arbitrary non-JSON content).

Common situations: Client/verifier version skew changing the AuthToken shape; a test token built by hand with wrong field types; another token format (e.g. JWT) sent where the GCE TPM token is expected.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/a2f0c14e4cd1d58b. Report an issue: GitHub.