OAuth Login Flow (Google) – Overview¶
This document explains how a user logs into Qubital with Google OAuth, from a high-level point of view.
We focus on five main components:
- Client – Qubital frontend (web or desktop) that the user interacts with.
- Backend – Qubital server, owner of Qubital sessions.
- Auth Provider – our external auth service (e.g. WorkOS) that integrates with Google.
- Hidden OAuth Redirect Page – a hidden OAuth redirect page (registered with WorkOS) that receives the authorization code from Google and immediately redirects to the client callback URL with the code and state parameters.
- Final Auth Page – the page shown after successful authentication is complete, with the message "You have authenticated successfully on Qubital" and the "Open Qubital" / "Cancel" options. This page is located at the
redirectURI(https://api.qubital.space) that is registered in WorkOS's redirect whitelist.
Google itself is the Identity Provider (IdP) used by the auth provider.
Actors & Responsibilities¶
At a high level:
- The client drives the user experience and holds the PKCE secret (
codeVerifier) during the flow. - The backend coordinates the login, talks only to the auth provider, and creates the Qubital session cookie.
- The auth provider (WorkOS) runs the OAuth protocol with Google, validates the authorization code and PKCE, and returns a verified identity.
- Google authenticates the user with their Google account.
- The Hidden OAuth Redirect Page receives the OAuth redirect from Google (with the authorization code) and immediately redirects to the client callback URL, passing along the code and state parameters.
- The Final Auth Page is where the user lands once authentication is complete and Qubital has established its own session. This page is located at the
redirectURI(https://api.qubital.space), which WorkOS keeps in its redirect whitelist.
Flowchart – High-Level OAuth Flow¶
%%{init: {'theme':'default', 'themeVariables': { 'fontSize':'24px'}}}%%
flowchart TB
%% Column headers (above lanes)
ClientHeader["Client - Qubital Frontend"]
BackendHeader["Backend - Qubital Server"]
AuthHeader["Auth Provider - e.g. WorkOS"]
HiddenHeader["Hidden OAuth Redirect Page"]
GoogleHeader["Google - Identity Provider"]
%% Client lane (no visible lane title)
subgraph ClientLane[" "]
direction TB
C1["User clicks<br/>'Sign in with Google'"]
C2["Generate<br/>codeVerifier & challenge"]
C3["Call Backend:<br/>start login<br/>(state, challenge)"]
C4["Receive<br/>auth URL"]
C5["Open Google<br/>auth URL in browser"]
C6["Receive redirect<br/>with ?code=...<br/>(on client callback URL)"]
C7["Call Backend:<br/>finalize login<br/>(send code + codeVerifier)"]
C8["Load Final Auth Page<br/>('Authenticated on Qubital'<br/>'Open Qubital')"]
end
%% Backend lane
subgraph BackendLane[" "]
direction TB
B1["Forward start-login<br/>to Auth Provider<br/>(challenge,<br/>state, redirectURI)"]
B2["Receive auth URL<br/>from Auth Provider"]
B3["Forward finalize-login<br/>to Auth Provider<br/>(code + codeVerifier)"]
B4["Receive verified<br/>identity / session result"]
B5["Create Qubital user / session<br/>Set Qubital session cookie"]
B6["Redirect to<br/>Final Auth Page"]
end
%% Auth provider lane
subgraph AuthLane[" "]
direction TB
A1["Store challenge<br/>& state"]
A2["Build Google OAuth URL<br/>(client_id, scopes,<br/>PKCE, state)"]
A3["Receive code +<br/>codeVerifier from Backend"]
A4["Verify PKCE:<br/>recompute challenge<br/>and compare with stored"]
A5["Exchange code with<br/>Google token endpoint<br/>(ID token + tokens)"]
A6["Return verified<br/>identity to Backend"]
end
%% Hidden redirect page lane
subgraph HiddenLane[" "]
direction TB
H1["Receive authorization code<br/>from Google<br/>(code & state params)"]
H2["Redirect to Client<br/>callback URL<br/>(pass code & state)"]
end
%% Google lane
subgraph GoogleLane[" "]
direction TB
G1["Show Google<br/>sign-in UI"]
G2["Authenticate user<br/>& obtain consent"]
G3["Redirect to Hidden<br/>Redirect Page<br/>with authorization code"]
G4["Issue tokens in response<br/>to Auth Provider"]
end
%% Attach headers above lanes
ClientHeader --> C1
BackendHeader --> B1
AuthHeader --> A1
HiddenHeader --> H1
GoogleHeader --> G1
%% Flow between actors
C1 --> C2 --> C3
C3 --> B1 --> A1 --> A2 --> B2 --> C4 --> C5
C5 --> G1 --> G2 --> G3 --> H1 --> H2 --> C6 --> C7
C7 --> B3 --> A3 --> A4 --> A5 --> G4 --> A6 --> B4 --> B5 --> B6 --> C8
Step 1 – Client generates PKCE and starts the login¶
The flow begins when the user clicks "Sign in with Google" in the client.
Before calling our backend, the client generates a PKCE pair:
- A random, secret
codeVerifier(kept only on the client). - A derived code challenge (sent to the backend).
The client then sends a "start login" request to the backend, including:
- The code challenge.
- A state parameter for security and context tracking.
The key idea is that the codeVerifier never leaves the client at this stage. Only the one-way challenge does.
Step 2 – Backend asks the auth provider to prepare the OAuth redirect¶
When the backend receives the "start login" request with the code challenge, it becomes the coordinator.
The backend calls the auth provider (WorkOS), asking it to prepare an OAuth flow with Google. It forwards:
- The PKCE code challenge.
- The state parameter.
- The
redirectURI(https://api.qubital.space) – the Final Auth Page URL where the user will ultimately land after authentication completes.
The auth provider:
- Stores the code challenge, state, and
redirectURIon its side. - Constructs the Google OAuth authorization URL, including:
- Google client ID managed by the auth provider.
- A redirect URI pointing to WorkOS's Hidden OAuth Redirect Page.
- Scopes (e.g.
openid,email,profile). - The PKCE code challenge.
- Its own
stateparameter to protect the flow.
The auth provider then returns this authorization URL to the backend, which passes it straight back to the client.
The client now has a safe URL that leads to the Google login page. It still holds the codeVerifier, which is required later to complete the flow.
Step 3 – Client opens the Google sign-in page¶
The client opens the authorization URL in the user's browser. The user sees the Google sign-in page and authenticates with their Google account:
- They may choose an account or enter credentials.
- Google may apply MFA or other security checks.
- If already signed in, they might only confirm access.
During this step:
- The client is simply showing Google’s UI.
- The auth provider and backend are waiting.
- No Qubital session exists yet. Only Google is authenticating the user.
If the user cancels, the flow ends. If they complete the login and consent, Google issues an authorization code and redirects back.
Step 4 – Google redirects to the Hidden OAuth Redirect Page¶
After a successful login, Google generates a short-lived authorization code and redirects the browser to the Hidden OAuth Redirect Page (a redirect handler controlled by WorkOS).
This hidden page:
- Is a simple redirect handler managed by WorkOS, not visible to the user.
- Receives the authorization code and state parameters from Google.
- Immediately redirects the browser to the client callback URL with these parameters.
The redirect URL to the client contains:
code=...– the authorization code from Google.state=...– the auth provider's state value.
This intermediate step is necessary because Google's OAuth flow requires a redirect URI controlled by the auth provider (WorkOS), which then forwards the authorization code to the client application.
Step 5 – Client receives the authorization code¶
The client now receives the redirect with the authorization code at its callback URL. At this moment:
- The client still has the original
codeVerifier. - It now has the authorization code.
- It still does not have a Qubital session.
The next step is to send both the authorization code and the codeVerifier to the backend so that Qubital can finalize the login.
Step 6 – Client sends the code and verifier to the backend¶
From the callback page, the client calls the backend again, this time to complete authentication.
The request includes:
- The authorization code received from Google.
- The
codeVerifierthat matches the original code challenge.
This is the critical proof that:
- The same client that started the flow (with the original code challenge) is now trying to finish it, and
- The authorization code belongs to this transaction.
The backend now forwards this information to the auth provider to perform the final validations and token exchange with Google.
Step 7 – Auth provider validates PKCE and exchanges the code¶
The auth provider receives from the backend:
- The authorization code.
- The
codeVerifier.
It performs two key checks:
- PKCE verification
It recomputes a code challenge from the providedcodeVerifierand compares it to the originally stored challenge for this transaction. - If they do not match, the request is rejected.
-
If they match, it proves the authorization code is being redeemed by the correct client.
-
Code exchange with Google
The auth provider sends the authorization code (plus its own credentials and redirect URI) to Google’s token endpoint.
Google verifies the code, and if everything is valid, returns tokens and identity information (e.g. ID token, user profile).
If both steps succeed, the auth provider builds an authentication result and returns it to our backend. Conceptually, this result says:
“Google has authenticated this user; here is the verified identity and tokens.”
The auth provider may also maintain its own session internally, but the Qubital user session is created only by our backend.
Step 8 – Backend creates the Qubital session and shows the final auth page¶
With the verified identity in hand, the backend now:
- Looks up or creates the corresponding Qubital user in our database.
- Creates a Qubital session bound to that user.
- Sets a Qubital session cookie in the HTTP response:
- Scoped to our domain.
HttpOnlyandSecure.
The backend then redirects the browser to the Final Auth Page (located at the redirectURI: https://api.qubital.space).
This page:
- Confirms to the user that they have authenticated successfully on Qubital.
- Displays actions such as “Open Qubital” and “Cancel”.
By the time this page loads:
- The Qubital session cookie is already set.
- From our backend’s perspective, the user is fully authenticated.
When the user clicks “Open Qubital”, the client can either:
- Navigate to the main web app area using the existing session, or
- Trigger a deep link to the desktop app, if that is the desired flow.
No additional OAuth work happens after this page. It is purely the handoff from authentication to application usage.