Google OAuth
Loquent supports Google OAuth 2.0 via the authorization code flow. A single callback handler manages login, signup, invitation acceptance, and auto-linking of existing email accounts. The frontend provides a reusable sign-in button that appears on all authentication pages.
Architecture
Section titled “Architecture”Browser → get_google_auth_url_api → Google consent screen → Google redirects to /auth/google/callback → Token exchange + ID token decode → Smart user routing (login / link / signup) → Session cookie → redirect to appGoogle OAuth credentials are stored in the core_conf table (not environment variables), making them configurable per deployment from the admin panel.
Database Schema
Section titled “Database Schema”google_account table
Section titled “google_account table”| Column | Type | Notes |
|---|---|---|
id | UUID | Primary key |
user_id | UUID | FK to user.id, unique, cascade delete |
google_id | String | Google’s sub claim, unique |
email | String | Google email |
name | Option<String> | Display name |
picture_url | Option<String> | Profile picture URL |
created_at | Timestamptz | |
updated_at | Timestamptz |
core_conf additions
Section titled “core_conf additions”Two new columns store the OAuth credentials:
google_oauth_client_id— your Google Cloud client IDgoogle_oauth_client_secret— the corresponding client secret
Configure these in Settings → System Config in the admin panel.
Authorization URL
Section titled “Authorization URL”POST /auth/google/url generates the Google consent URL.
// Request{ invitation_token: Option<String>, organization_name: Option<String>,}
// Response — (auth_url, csrf_token)("https://accounts.google.com/o/oauth2/v2/auth?...", "a1b2c3d4...")The server loads credentials from core_conf, generates a CSRF token (UUID v4), and encodes an OAuthFlowState as base64 in the state parameter. The frontend stores the CSRF token in a google_oauth_csrf cookie before redirecting.
Callback Handler
Section titled “Callback Handler”GET /auth/google/callback is a raw Axum handler (not a Dioxus server function) because it needs direct access to query parameters and cookies from Google’s redirect.
-
CSRF validation — Decodes the
stateparameter, extracts the CSRF token, and compares it against thegoogle_oauth_csrfcookie. Returns 403 on mismatch. -
Token exchange — POSTs the authorization code to
https://oauth2.googleapis.com/tokenwith the client credentials. Receives anaccess_tokenandid_token(JWT). -
ID token decode — Extracts the JWT payload (no signature verification needed — the token comes directly from Google over HTTPS with the client secret).
-
User routing — Three scenarios:
Scenario Condition Action Login google_accountexists bygoogle_idFind user, create session Auto-link email_password_accountexists by emailCreate google_accountlinked to that userSignup No matching account Create user + google_account, then create org or process invitation -
Session creation — Sets
session_tokencookie (HttpOnly, Secure) and clears the CSRF cookie. Redirects to/.
Error handling
Section titled “Error handling”All failures redirect to /login?error=Something+went+wrong.... If a new user or organization was partially created, the handler deletes the records to prevent orphaned data.
Data Types
Section titled “Data Types”All types live in src/bases/auth/types/google_oauth_types.rs:
// Encoded as base64 in the state query parameterstruct OAuthFlowState { csrf_token: String, organization_name: Option<String>, invitation_token: Option<String>,}
// Deserialized from Google's token endpoint responsestruct GoogleTokenResponse { access_token: String, id_token: String, // JWT containing user claims token_type: String, expires_in: u64,}
// Decoded from the id_token JWT payloadstruct GoogleIdTokenClaims { sub: String, // Google user ID email: String, name: Option<String>, picture: Option<String>,}Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/bases/auth/api/get_google_auth_url_api.rs | Generates authorization URL |
src/bases/auth/api/google_callback_handler.rs | Handles Google’s redirect callback |
src/bases/auth/types/google_oauth_types.rs | OAuth data types |
src/bases/auth/constants/google_oauth_constants.rs | URLs and cookie names |
src/bases/auth/conf/core/google_oauth_core_conf.rs | Loads credentials from core_conf |
migration/src/m20260317_120000_create_google_account_table.rs | google_account migration |
migration/src/m20260317_130000_core_conf_add_google_oauth_fields.rs | core_conf credential columns |
src/shared/components/google_sign_in_button_component.rs | Reusable Google sign-in button |
src/bases/auth/types/session_type.rs | Session type with has_password_account flag |
src/shared/types/client_session_type.rs | Client-side session type |
Frontend Sign-In Button
Section titled “Frontend Sign-In Button”The GoogleSignInButton component (src/shared/components/google_sign_in_button_component.rs) is a reusable Dioxus component that handles the entire client-side OAuth redirect flow.
| Prop | Type | Default | Purpose |
|---|---|---|---|
invitation_token | Option<String> | None | Passed through to the callback for invitation acceptance |
organization_name | Option<String> | None | Org name from the signup form |
label | String | "Continue with Google" | Button text |
Behavior
Section titled “Behavior”- User clicks the button → calls
get_google_auth_url_apiwith the optionalinvitation_tokenandorganization_name - Receives
(auth_url, csrf_token)from the server - Sets the
google_oauth_csrfcookie via JavaScript (max-age 600s, SameSite=Lax) - Redirects the browser to
auth_url
The button renders with the official Google “G” icon and shows a loading spinner during the redirect.
Where it appears
Section titled “Where it appears”- Login page (
login_card_component.rs) — standard sign-in - Signup page (
signup_card_component.rs) — passesorganization_name - Invitation acceptance (
accept_invitation_card_component.rs) — passesinvitation_token
Invitation Email Enforcement
Section titled “Invitation Email Enforcement”When a user accepts an invitation via Google OAuth, the callback handler verifies that the Google account’s email matches the invitation email. If they don’t match, the handler redirects back to the invitation page with an error:
“This invitation was sent to {email}. Please sign in with that Google account instead.”
This prevents users from accepting invitations with a different Google account than the one the invitation was sent to.
OAuth-Only User Detection
Section titled “OAuth-Only User Detection”The Session and ClientSession types include a has_password_account: bool field that indicates whether the user has an email/password account linked. This enables conditional UI behavior:
- OAuth-only users (
has_password_account: false) — the Account tab in Settings is hidden since there’s no password to manage. The default Settings tab falls back to Notifications. - Email+password users (
has_password_account: true) — the Account tab is visible with email and password management.
Admin Panel
Section titled “Admin Panel”The admin system config service (src/mods/admin/services/admin_system_config_service.rs) exposes Google OAuth credentials for get/update operations. Provider health checks validate that credentials are present and track verification status via audit logs with the provider.verify.google_oauth action.