JWT Token Validation
OneTap Login validates every Google authentication using JWT (JSON Web Token) verification. This ensures only legitimate Google authentications are accepted.
What is JWT?
Definition
JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. Google uses JWTs for authentication tokens.
Structure
A JWT has three parts:
header.payload.signature
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXVkIjoiMTIzNDUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTc4OTUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjE2MjM5MDIyLCJleHAiOjE2MTYyNDI2MjJ9.
[signature]
Decoded Payload
{
"iss": "accounts.google.com",
"azp": "12345.apps.googleusercontent.com",
"aud": "12345.apps.googleusercontent.com",
"sub": "117895234567890123456",
"email": "user@example.com",
"email_verified": true,
"name": "John Doe",
"given_name": "John",
"family_name": "Doe",
"picture": "https://lh3.googleusercontent.com/...",
"locale": "en",
"iat": 1616239022,
"exp": 1616242622
}
Validation Flow
Complete Flow
User completes Google sign-in
↓
Google returns ID token (JWT)
↓
OneTap sends to Google tokeninfo
↓
Google validates signature
↓
Valid → Returns payload
↓
Invalid → Returns error
↓
OneTap validates payload claims
↓
All valid → Proceed
↓
Any invalid → Reject
Validation Steps
| Step | Check | Failure Action |
|---|---|---|
| 1 | Token present | Reject |
| 2 | Signature valid | Reject |
| 3 | Not expired (exp) | Reject |
| 4 | Correct issuer (iss) | Reject |
| 5 | Correct audience (aud) | Reject |
| 6 | Email verified | Reject |
| 7 | Not too old (iat) | Warn |
Token Claims
Required Claims
| Claim | Description | Expected Value |
|---|---|---|
iss | Issuer | accounts.google.com or https://accounts.google.com |
aud | Audience | Your Client ID |
sub | Subject (Google ID) | Unique user ID |
email | User's email | Valid email |
email_verified | Email verification | true |
exp | Expiration | Future timestamp |
iat | Issued at | Recent timestamp |
Optional Claims
| Claim | Description | Usage |
|---|---|---|
name | Full name | Display name |
given_name | First name | First name field |
family_name | Last name | Last name field |
picture | Avatar URL | Profile picture |
locale | Language | User locale |
azp | Authorized party | Additional verification |
Validation Implementation
Google Token Verification
OneTap uses Google's tokeninfo endpoint:
// Token verification
$response = wp_remote_get(
'https://oauth2.googleapis.com/tokeninfo?id_token=' . $token
);
if (is_wp_error($response)) {
throw new Exception('Validation request failed');
}
$payload = json_decode(wp_remote_retrieve_body($response), true);
if (isset($payload['error'])) {
throw new Exception('Invalid token: ' . $payload['error']);
}
Audience Verification
// Check audience matches your Client ID
if ($payload['aud'] !== get_option('onetap_client_id')) {
throw new Exception('Token audience mismatch');
}
Issuer Verification
// Check issuer is Google
$valid_issuers = [
'accounts.google.com',
'https://accounts.google.com'
];
if (!in_array($payload['iss'], $valid_issuers)) {
throw new Exception('Invalid token issuer');
}
Email Verification
// Check email is verified by Google
if ($payload['email_verified'] !== true) {
throw new Exception('Email not verified by Google');
}
Expiration Check
// Check token not expired
if ($payload['exp'] < time()) {
throw new Exception('Token has expired');
}
Issued At Check
// Check token not too old (e.g., 10 minutes)
$max_age = 600; // 10 minutes
if ($payload['iat'] < (time() - $max_age)) {
// Warning: Token is old but may still be valid
// Depending on use case, may reject or proceed with caution
}
Security Considerations
Why Validate Server-Side?
Client-side validation is insufficient:
- JavaScript can be modified
- Tokens can be forged
- Man-in-the-middle attacks
Server-side validation ensures:
- Token authenticity
- Signature verification
- Claim accuracy
Token Handling
// Never log full tokens
error_log('Token received for: ' . $payload['email']);
// Never store tokens long-term
// Tokens are single-use
// Never send tokens to third parties
// Unless absolutely necessary with user consent
HTTPS Requirement
Token validation requires HTTPS:
- Tokens in transit must be encrypted
- HTTP would expose tokens to interception
- Google rejects non-HTTPS for production
Common Validation Errors
Invalid Token
Error: Invalid token
Causes:
- Token corrupted
- Token modified
- Wrong token type
Solution: Ensure complete token is sent unchanged.
Token Expired
Error: Token has expired
Causes:
- User took too long to complete sign-in
- Clock sync issues
- Token replay attempt
Solution: Request new token; check server time.
Audience Mismatch
Error: Token audience mismatch
Causes:
- Wrong Client ID configured
- Token from different application
- Configuration error
Solution: Verify Client ID in settings matches Google Cloud Console.
Invalid Issuer
Error: Invalid token issuer
Causes:
- Token not from Google
- Forged token
- Wrong authentication provider
Solution: Ensure Google OAuth is properly configured.
Email Not Verified
Error: Email not verified by Google
Causes:
- Google account email not verified
- Very new Google account
- Enterprise account without verification
Solution: User must verify email with Google first.
Debugging Token Issues
View Token Claims
For debugging (remove in production):
// Debug: View token payload
$payload = json_decode(
base64_decode(
str_replace(['-', '_'], ['+', '/'],
explode('.', $token)[1]
)
),
true
);
error_log('Token claims: ' . print_r($payload, true));
Check Token Manually
Use jwt.io (for debugging only):
- Go to https://jwt.io
- Paste token in debugger
- View decoded payload
- Never use production tokens!
Google OAuth Playground
Test tokens with Google's OAuth Playground:
- Go to https://developers.google.com/oauthplayground
- Select Google OAuth2 API
- Authorize
- Exchange code for tokens
- Examine token structure
Token Lifecycle
Creation
User clicks Google sign-in
↓
Google authenticates user
↓
Google creates JWT with:
├── User info claims
├── Audience (your app)
├── Expiration (usually 1 hour)
└── Signature (Google's key)
↓
JWT sent to your site
Validation
JWT received by OneTap
↓
Sent to Google tokeninfo
↓
Google verifies signature
↓
Returns payload or error
Expiration
Token valid for ~1 hour
↓
After expiration:
├── Cannot be used
├── New sign-in required
└── Google issues new token
Best Practices
Do's
- Always validate server-side
- Check all required claims
- Use HTTPS everywhere
- Log validation failures
- Monitor for anomalies
Don'ts
- Trust client-side validation alone
- Store tokens long-term
- Log full token contents
- Share tokens with third parties
- Ignore validation errors
Hooks for Developers
After Token Validation
add_action('onetap_token_validated', function($payload) {
// Additional checks
error_log('Validated token for: ' . $payload['email']);
// Custom validation
if (custom_check_fails($payload)) {
throw new Exception('Custom validation failed');
}
}, 10, 1);
Filter Payload
add_filter('onetap_token_payload', function($payload) {
// Modify payload before use
// Use with caution!
return $payload;
});
Related Security
- Security Overview - All security measures
- Rate Limiting - Request limits
- Role Security - Permission controls
Next Steps
- Rate Limiting - Abuse prevention
- Role Security - Role restrictions
- Troubleshooting - Common issues