addons/dexie-cloud/oauth_flow.md
The SPA:
Dexie Cloud acts as OAuth broker and manages tenant + identity linkage.
User clicks "Login", SPA displays list of providers:
Google | GitHub | Apple | Microsoft
No nonce or PKCE is created yet.
Example: User selects Google
The client initiates the OAuth flow. There are two ways to do this:
window.location.href = `https://<db>.dexie.cloud/oauth/login/google?redirect_uri=${encodeURIComponent(location.href)}`;
The redirect_uri parameter specifies where Dexie Cloud should redirect after authentication.
This can be any page in your app - no dedicated callback route is needed.
// Open in system browser or in-app browser
Browser.open({
url: `https://<db>.dexie.cloud/oauth/login/google?redirect_uri=${encodeURIComponent('myapp://')}`
});
The custom scheme myapp:// tells Dexie Cloud to redirect back via deep link.
Dexie Cloud receives /oauth/login/google and generates:
state (anti-CSRF)code_verifier (PKCE)code_challenge (PKCE)Stores these in the challenges table, then redirects the browser to provider:
https://accounts.google.com/o/oauth2/v2/auth?
client_id=...
redirect_uri=https://<db>.dexie.cloud/oauth/callback/google
state=STATE
code_challenge=CHALLENGE
code_challenge_method=S256
response_type=code
scope=openid email profile
Note: redirect_uri points to the Dexie Cloud server callback endpoint.
Provider authenticates the user and requests consent if needed.
Provider redirects back to Dexie Cloud:
https://<db>.dexie.cloud/oauth/callback/google?code=CODE&state=STATE
Dexie Cloud:
stateDexie Cloud issues an HTTP 302 redirect back to the client with the authorization code.
The auth data is encapsulated in a single dxc-auth query parameter containing base64url-encoded JSON.
This avoids collisions with the app's own query parameters.
If the client passed an http/https redirect_uri, Dexie Cloud redirects:
HTTP/1.1 302 Found
Location: https://myapp.com/?dxc-auth=eyJjb2RlIjoiLi4uIiwicHJvdmlkZXIiOiJnb29nbGUiLCJzdGF0ZSI6Ii4uLiJ9
The dxc-auth parameter contains base64url-encoded JSON:
{ "code": "DEXIE_AUTH_CODE", "provider": "google", "state": "STATE" }
Or in case of error:
{ "error": "Error message", "provider": "google", "state": "STATE" }
The app doesn't need a dedicated OAuth callback route - the dexie-cloud client library
detects and processes the dxc-auth parameter on any page load.
If the client passed a redirect_uri with a custom scheme (e.g., myapp://),
Dexie Cloud redirects to that URL with the same dxc-auth parameter:
HTTP/1.1 302 Found
Location: myapp://?dxc-auth=eyJjb2RlIjoiLi4uIiwicHJvdmlkZXIiOiJnb29nbGUiLCJzdGF0ZSI6Ii4uLiJ9
The native app intercepts this deep link and decodes the parameter.
If no valid redirect_uri was provided, an error page is displayed
explaining that the auth flow cannot complete.
For Full Page Redirect (6a):
The dexie-cloud-addon library handles OAuth callback detection automatically:
db.cloud.configure() is called, the addon checks for the dxc-auth query parameterhistory.replaceState() to remove dxc-authsetTimeout(cb, 0) is scheduled to initiate the token exchangedatabaseUrldb.on('ready') callback when Dexie is ready// Pseudocode for dexie-cloud-addon implementation
function configure(options) {
// Only check in DOM environment, not workers
if (typeof window !== 'undefined' && window.location) {
const encoded = new URLSearchParams(location.search).get('dxc-auth');
if (encoded) {
// Decode base64url (unpadded) to JSON
const padded = encoded + '='.repeat((4 - (encoded.length % 4)) % 4);
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
const payload = JSON.parse(atob(base64));
const { code, provider, state, error } = payload;
// Clean up URL immediately (remove dxc-auth param)
const url = new URL(location.href);
url.searchParams.delete('dxc-auth');
history.replaceState(null, '', url.toString());
if (!error) {
// Schedule token exchange (processed in db.on('ready'))
setTimeout(() => {
// Perform token exchange with options.databaseUrl
}, 0);
}
}
}
}
For Capacitor/Native Apps (6b):
App registers a deep link handler and decodes the same parameter:
// Capacitor example
App.addListener('appUrlOpen', ({ url }) => {
const parsedUrl = new URL(url);
const encoded = parsedUrl.searchParams.get('dxc-auth');
if (encoded) {
const padded = encoded + '='.repeat((4 - (encoded.length % 4)) % 4);
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
const payload = JSON.parse(atob(base64));
const { code, provider, state, error } = payload;
// Proceed to token exchange
}
});
Upon success, client proceeds to token exchange.
Client sends:
POST /token
Content-Type: application/json
Payload:
{
"grant_type": "authorization_code",
"code": "<DEXIE_AUTH_CODE>",
"public_key": "<SPA_PUBLIC_KEY>",
"scopes": ["ACCESS_DB"]
}
Dexie Cloud validates:
Dexie Cloud responds with:
{
"access_token": "...",
"refresh_token": "...",
"expires_in": 3600,
"token_type": "Bearer"
}
This completes authentication.
state parameter/token exchangeThis aligns with modern OIDC/OAuth best practices (2023+) and matches architectures used by: Auth0, Firebase, Supabase, Okta, MSAL, Google Identity Services, Clerk, etc.