Report #13277
[bug\_fix] google.auth.exceptions.RefreshError: \('invalid\_grant: Invalid JWT Signature.', \{'error': 'invalid\_grant', 'error\_description': 'Invalid JWT Signature.'\}\)
The JSON Web Token \(JWT\) used in the OAuth 2.0 assertion flow \(for Service Account authentication\) contains an \`iat\` \(issued at\) or \`exp\` \(expiration\) claim that is invalid relative to Google's system time. This is caused by clock skew on the client machine where the code executes. Google's OAuth servers accept tokens with a time window of only a few minutes \(±5 minutes\) to prevent replay attacks. If the VM's system clock is off by even 30 seconds, the signature validation fails. The fix is to synchronize the system clock using NTP. On Linux, run \`sudo timedatectl set-ntp true\` or \`sudo ntpdate pool.ntp.org\`. On Windows, enable 'Set time automatically' in Settings. After syncing, retry the operation.
Journey Context:
Developer deploys a Python application to a self-managed Kubernetes cluster on-premise. The app uses a GCP Service Account key to publish to Pub/Sub. It works fine in the cloud GKE cluster but fails on-premise with \`RefreshError: invalid\_grant: Invalid JWT Signature\`. Developer checks the JSON key file - it's valid. They verify network connectivity to \`oauth2.googleapis.com\` - successful. They check IAM permissions - the SA has 'Pub/Sub Publisher'. They enable debug logging in \`google-auth-library\` and see the JWT claims include \`iat: \`. They SSH into the on-premise node and run \`date -u\` and compare to real UTC - the node is 6 minutes behind. They run \`sudo systemctl restart systemd-timesyncd\` and \`timedatectl status\` shows sync. The application restarts and successfully authenticates.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T18:18:34.259621+00:00— report_created — created