Skip to content

Harden Google ID token validation#97

Open
fabianmarian8 wants to merge 1 commit into
BAWES-Universe:masterfrom
fabianmarian8:harden-google-id-token-validation
Open

Harden Google ID token validation#97
fabianmarian8 wants to merge 1 commit into
BAWES-Universe:masterfrom
fabianmarian8:harden-google-id-token-validation

Conversation

@fabianmarian8
Copy link
Copy Markdown

@fabianmarian8 fabianmarian8 commented May 17, 2026

Summary

  • add a shared Google ID token verifier for backend Google login flows
  • require runtime-configured GOOGLE_OAUTH_CLIENT_IDS and validate aud, iss, exp, email, and email_verified before account lookup/signup
  • replace duplicated tokeninfo-only checks in admin, company, staff, manager, and candidate API controllers
  • add a static regression guard for the Google-login hardening path

/claim #55

Validation

  • python3 tests/check-google-id-token-hardening.py
  • git diff --check

PHP/Codeception could not be run in this local environment because php is not installed. No live Google account, AWS/IAM/S3 access, candidate data, or real secret values were used.

Demo evidence

The static guard verifies that all five login-by-google handlers call the shared verifier, no handler keeps the direct tokeninfo?id_token= path, and the verifier contains audience, issuer, expiry, email, and verified-email checks.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Google authentication token validation with enhanced security verification across all login endpoints.
    • Standardized error responses and handling for token validation failures.
  • Tests

    • Added test coverage for Google authentication security hardening measures.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

📝 Walkthrough

Walkthrough

This PR centralizes Google ID token verification across five application modules by introducing a reusable GoogleIdTokenVerifier Yii component that encapsulates claim validation, then updates module AuthControllers to use this service instead of making direct HTTP calls to Google's tokeninfo endpoint. A hardening test verifies the changes enforce security requirements.

Changes

Google ID Token Verification Centralization

Layer / File(s) Summary
GoogleIdTokenVerifier component implementation
common/components/GoogleIdTokenVerifier.php
New Yii component with configurable client IDs and timeout; verify() fetches tokeninfo, validates presence and values of aud, iss, exp, email_verified claims against allowed client IDs and accepted issuers, and returns response only when validation passes. Helper methods parse client IDs and handle HTTP retrieval/JSON decoding.
Component configuration wiring
common/config/main.php
Registers googleIdTokenVerifier in Yii components, populating allowed OAuth client IDs from GOOGLE_OAUTH_CLIENT_IDS environment variable.
Module AuthController integration
admin/modules/v1/controllers/AuthController.php, candidate/modules/v1/controllers/AuthController.php, company/modules/v1/controllers/AuthController.php, manager/modules/v1/controllers/AuthController.php, staff/modules/v1/controllers/AuthController.php
All five module actionLoginByGoogle() methods replace direct cURL calls to Google tokeninfo with centralized googleIdTokenVerifier->verify($token) service, checking for falsy result and returning error when verification fails.
Security hardening test
tests/check-google-id-token-hardening.py
Python test script verifying verifier component contains claim validation fields and logic, configuration includes the verifier, controllers call googleIdTokenVerifier->verify(), and no controller retains prior insecure patterns (direct tokeninfo requests or curl_exec with json_decode).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A token verifier, shiny and new,
Guards five controllers from insecure cURL stew,
One component to validate them all,
With claims and emails standing tall,
No more direct HTTP calls!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Harden Google ID token validation' accurately and clearly summarizes the main objective of the changeset: replacing duplicated, less-secure tokeninfo checks across five AuthController modules with a centralized, hardened Google ID token verifier component that validates critical token claims.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot
Copy link
Copy Markdown

dosubot Bot commented May 17, 2026

Related Knowledge

3 documents with suggested updates are ready for review.

BAWES Universe

Backend Codebase Tour (studenthub)
View Suggested Changes
@@ -70,6 +70,7 @@
 - **S3ResourceManager** (`common/components/S3ResourceManager.php`), **CloudinaryManager** (`common/components/CloudinaryManager.php`): Abstract storage for uploads (photos, IDs, etc.), used across apps.
 - **Algolia** (`common/components/Algolia.php`): Handles search index synchronization for candidates, companies, etc.
 - **JWT** (`common/components/JWT.php`): Handles JWT authentication.
+- **GoogleIdTokenVerifier** (`common/components/GoogleIdTokenVerifier.php`): Provides centralized, hardened validation of Google ID tokens for authentication flows across all portals (admin, company, staff, manager, candidate). Validates the `aud`, `iss`, `exp`, `email`, and `email_verified` claims before allowing account lookup/signup, replacing the previous pattern where each AuthController directly called Google's tokeninfo endpoint. Requires the `GOOGLE_OAUTH_CLIENT_IDS` environment variable to be configured with comma-separated OAuth client IDs. Configured as `googleIdTokenVerifier` in `common/config/main.php`.
 - **SMSComponent** (`common/components/SMSComponent.php`): Sends SMS notifications.
 - **ReCaptcha** (`common/components/ReCaptcha.php`): Integrates Google reCAPTCHA.
 - **SlackLogger** (`common/components/SlackLogger.php`): Sends logs to a Slack webhook.

[Accept] [Edit] [Decline]

DB, API endpoint, S3, Slack, and payment keys
View Suggested Changes
@@ -53,6 +53,7 @@
 # IPSTACK_ACCESS_KEY=your_ipstack_access_key
 # RECAPTCHA_SECRET_KEY=your_recaptcha_secret_key
 # GOOGLE_MAP_TOKEN=your_google_map_token
+# GOOGLE_OAUTH_CLIENT_IDS=client_id_1.apps.googleusercontent.com,client_id_2.apps.googleusercontent.com
 # NETLIFY_TOKEN=your_netlify_token
 # GITHUB_TOKEN=your_github_token
 # GITHUB_BRANCH=master
@@ -70,4 +71,5 @@
 - Replace all `your_*` values with your actual credentials or secrets.
 - The S3, Slack, and payment keys are directly mapped from the structure in [`common/config/main.php`](https://github.com/BAWES-Universe/plugn/blob/bc485b0a1da61d516955c5dc4fc29e95afccea92/common/config/main.php#L3-L172).
 - Database and API endpoint keys are placeholders, as they are not defined in `main.php` and may be set elsewhere in your project.
+- `GOOGLE_OAUTH_CLIENT_IDS` is a comma-separated list of Google OAuth client IDs used to validate Google ID tokens during login flows across all portals (admin, company, staff, manager, candidate). The `GoogleIdTokenVerifier` component validates the `aud` (audience) claim in incoming tokens, ensuring tokens were issued for the correct OAuth client(s). This variable is required for Google login functionality to work properly.
 - All other configuration keys in `main.php` are optional and can be set if your local environment requires them.

[Accept] [Edit] [Decline]

Security, Auth & Permissions (RBAC + Tenant Scoping)
View Suggested Changes
@@ -48,9 +48,12 @@
 
 ReCaptcha enforcement is present but commented out in login and related actions, indicating it may be disabled or optional. See [candidate/modules/v1/controllers/AuthController.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/candidate/modules/v1/controllers/AuthController.php#L10-L1151) and [admin/modules/v1/controllers/AuthController.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/admin/modules/v1/controllers/AuthController.php#L19-L276).
 
+Google ID tokens are validated via the shared `googleIdTokenVerifier` component (`common/components/GoogleIdTokenVerifier`), which provides centralized, hardened validation. The verifier checks the `aud` (audience) claim against `GOOGLE_OAUTH_CLIENT_IDS`, validates the `iss` (issuer) is `accounts.google.com`, ensures the token has not expired (`exp`), and requires `email` and `email_verified` claims before allowing account lookup or signup. This replaces the previous approach of calling Google's tokeninfo endpoint directly in each portal's AuthController. See [common/config/main.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/common/config/main.php#L25-L130).
+
 Rate limiting, throttling, and webhook secrets are not documented in the available sources. Unknown / Verify.
 
 Auth/security-related environment variables include:
+- `GOOGLE_OAUTH_CLIENT_IDS` (comma-separated list of Google OAuth client IDs used to validate the `aud` claim in Google ID tokens during login flows across all portals – admin, company, staff, manager, and candidate; used in `common/components/GoogleIdTokenVerifier` in [common/config/main.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/common/config/main.php#L25-L130))
 - `GOOGLE_MAPS_API_KEY` (used in `common/components/GoogleMap` in [common/config/main.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/common/config/main.php#L25-L130))
 - ReCaptcha secret key (used in `common/components/ReCaptcha` in [common/config/main.php](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/common/config/main.php#L25-L130))
 

[Accept] [Edit] [Decline]

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
company/modules/v1/controllers/AuthController.php (1)

300-302: 💤 Low value

Inconsistent email_verified check may skip email verification update.

The verifier's isEmailVerified() accepts true (boolean), "true", or "1". However, the check here only matches when $response->email_verified == "true" (string comparison). If the tokeninfo response returns a boolean true, this condition will pass due to loose comparison, but the intent seems unclear.

Since the verifier already guarantees email_verified is truthy before returning a response, consider simplifying to just check for truthiness:

♻️ Suggested simplification
-            if($response->email_verified && $response->email_verified == "true") {
+            if($response->email_verified) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@company/modules/v1/controllers/AuthController.php` around lines 300 - 302,
The email verification check in AuthController (currently using
if($response->email_verified && $response->email_verified == "true")) is too
narrow; change it to a simple truthy check so any truthy tokeninfo value
accepted by the verifier marks the contact verified. Replace that condition with
a single truthiness test (e.g., if ($response->email_verified) or use the
verifier's isEmailVerified(...) helper) and then set
Contact::$contact_email_verification to Contact::EMAIL_VERIFIED and call
$contact->save(false) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@company/modules/v1/controllers/AuthController.php`:
- Around line 300-302: The email verification check in AuthController (currently
using if($response->email_verified && $response->email_verified == "true")) is
too narrow; change it to a simple truthy check so any truthy tokeninfo value
accepted by the verifier marks the contact verified. Replace that condition with
a single truthiness test (e.g., if ($response->email_verified) or use the
verifier's isEmailVerified(...) helper) and then set
Contact::$contact_email_verification to Contact::EMAIL_VERIFIED and call
$contact->save(false) as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21b006c0-0b77-4d51-a17e-4dde92f9e531

📥 Commits

Reviewing files that changed from the base of the PR and between 7b023ff and ede9dd3.

📒 Files selected for processing (8)
  • admin/modules/v1/controllers/AuthController.php
  • candidate/modules/v1/controllers/AuthController.php
  • common/components/GoogleIdTokenVerifier.php
  • common/config/main.php
  • company/modules/v1/controllers/AuthController.php
  • manager/modules/v1/controllers/AuthController.php
  • staff/modules/v1/controllers/AuthController.php
  • tests/check-google-id-token-hardening.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant