POST /api/v1/auth/oauth/google
Sign in via Google for mobile clients (iOS / Android).
The mobile app receives an id_token from the Google SDK and sends it to the backend. No OAuth redirects — stateless verification via Google tokeninfo.
Request
POST /api/v1/auth/oauth/google
Content-Type: application/json
{
"id_token": "<Google id_token from mobile SDK>"
}
| Field | Type | Required | Description |
|---|---|---|---|
id_token | string | Yes | JWT issued by Google Sign-In SDK on the device |
Response 200 — Existing user
{
"success": true,
"status": "200",
"message": "Signed in with Google",
"errors": null,
"data": {
"access_token": "<jwt>",
"refresh_token": "<jwt>",
"expires_at": "2026-04-06T12:30:00Z"
}
}
Response 201 — New user created
Same body, HTTP status 201 Created.
Response 401 — Invalid token
{
"success": false,
"status": "401",
"message": "Invalid Google token",
"errors": null,
"data": null
}
Account Linking Logic
| Scenario | Result |
|---|---|
OAuthIdentity exists for provider+uid | Sign in as the linked user |
No identity, but User with that email exists | Create OAuthIdentity, link to existing User |
No identity, no User with that email | Create new User (role client) + OAuthIdentity |
New users from OAuth:
- Receive a random password (32 hex) — change via password reset
- Receive a placeholder phone (
placeholder_<hex>) — update via profile
Token Verification
The backend calls Google tokeninfo:
GET https://oauth2.googleapis.com/tokeninfo?id_token=<id_token>
Fields extracted from the response:
| Field | Usage |
|---|---|
sub | Unique Google uid |
email | Find / create User |
given_name | Profile.first_name |
family_name | Profile.last_name |
oauth_identities Table
| Column | Type | Description |
|---|---|---|
id | UUID (PK) | |
user_id | UUID (FK) | → users.id |
provider | string | "google" (extensible) |
uid | string | Google sub |
created_at | timestamp | |
updated_at | timestamp |
Unique index: provider + uid. No token columns stored.