dexie-cloud-addon 4.3.0 → 4.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO-SOCIALAUTH.md +129 -400
- package/dist/modern/DexieCloudOptions.d.ts +8 -11
- package/dist/modern/authentication/handleOAuthCallback.d.ts +20 -14
- package/dist/modern/authentication/interactWithUser.d.ts +3 -0
- package/dist/modern/authentication/oauthLogin.d.ts +28 -27
- package/dist/modern/default-ui/LoginDialog.d.ts +29 -4
- package/dist/modern/default-ui/OptionButton.d.ts +21 -0
- package/dist/modern/default-ui/SelectDialog.d.ts +10 -0
- package/dist/modern/dexie-cloud-addon.js +454 -305
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/errors/OAuthError.d.ts +1 -1
- package/dist/modern/errors/OAuthRedirectError.d.ts +11 -0
- package/dist/modern/service-worker.js +454 -305
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/modern/types/DXCUserInteraction.d.ts +33 -25
- package/dist/umd/dexie-cloud-addon.js +454 -305
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/service-worker.js +454 -305
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/oauth_flow.md +84 -76
- package/package.json +3 -3
package/TODO-SOCIALAUTH.md
CHANGED
|
@@ -13,16 +13,15 @@ This feature adds support for OAuth 2.0 social login providers (Google, GitHub,
|
|
|
13
13
|
- `src/api/oauth/registerOAuthEndpoints.ts` - OAuth endpoints
|
|
14
14
|
- `src/api/oauth/oauth-helpers.ts` - Provider exchange logic
|
|
15
15
|
- `src/api/registerTokenEndpoint.ts` - Token endpoint (authorization_code grant)
|
|
16
|
-
- `web-templates/oauth-callback.handlebars` - Callback page template
|
|
17
16
|
|
|
18
17
|
### Flow Summary
|
|
19
18
|
|
|
20
19
|
1. **Client** fetches available auth providers from `GET /auth-providers`
|
|
21
|
-
2. **Client**
|
|
20
|
+
2. **Client** redirects to `GET /oauth/login/:provider` (full page redirect)
|
|
22
21
|
3. **Dexie Cloud Server** redirects to OAuth provider and handles callback at `/oauth/callback/:provider`
|
|
23
22
|
4. **Server** exchanges provider code for tokens, verifies email, generates single-use Dexie auth code
|
|
24
|
-
5. **Server**
|
|
25
|
-
6. **Client**
|
|
23
|
+
5. **Server** redirects back to client with `dxc-auth` query parameter (base64url-encoded JSON)
|
|
24
|
+
6. **Client** detects `dxc-auth` in `db.cloud.configure()`, exchanges code for tokens via `POST /token`
|
|
26
25
|
|
|
27
26
|
### Supported Providers
|
|
28
27
|
- **Google** - OpenID Connect with PKCE
|
|
@@ -33,395 +32,132 @@ This feature adds support for OAuth 2.0 social login providers (Google, GitHub,
|
|
|
33
32
|
|
|
34
33
|
### Client Delivery Methods
|
|
35
34
|
|
|
36
|
-
The library must support multiple integration patterns:
|
|
37
|
-
|
|
38
35
|
| Method | Use Case | Delivery Mechanism |
|
|
39
36
|
|--------|----------|-------------------|
|
|
40
|
-
| **
|
|
41
|
-
| **Custom URL Scheme** | Capacitor/Native apps | Deep link redirect (e.g., `myapp
|
|
42
|
-
| **Full Page Redirect** | Web without popup support | HTTP redirect with query params |
|
|
37
|
+
| **Full Page Redirect** | Web SPAs (recommended) | HTTP redirect with `dxc-auth` query param |
|
|
38
|
+
| **Custom URL Scheme** | Capacitor/Native apps | Deep link redirect (e.g., `myapp://`) |
|
|
43
39
|
|
|
44
40
|
---
|
|
45
41
|
|
|
46
|
-
##
|
|
42
|
+
## Implementation Status
|
|
47
43
|
|
|
48
|
-
### ✅
|
|
44
|
+
### ✅ Server-Side (dexie-cloud-server) - COMPLETE
|
|
49
45
|
|
|
50
46
|
- [x] **OAuth provider configuration type** (`OAuthProviderConfig`)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- `iconUrl` for custom provider icons
|
|
56
|
-
|
|
57
|
-
- [x] **`GET /auth-providers` endpoint**
|
|
58
|
-
- Returns list of enabled OAuth providers (without secrets) and OTP status
|
|
59
|
-
- Includes `type`, `name`, `displayName`, `iconUrl`, `scopes` per provider
|
|
60
|
-
- Default icons served from `/static/icons/` for built-in providers
|
|
61
|
-
|
|
62
|
-
- [x] **`GET /oauth/login/:provider` endpoint**
|
|
63
|
-
- Accepts `redirect_uri` parameter for postMessage target / deep link
|
|
64
|
-
- Validates `redirect_uri` against whitelisted origins (via `npx dexie-cloud whitelist`)
|
|
65
|
-
- Generates state and PKCE challenge
|
|
66
|
-
- Stores `state`, `codeVerifier`, `spaOrigin`, `clientRedirectUri` in challenges table
|
|
67
|
-
- Redirects to OAuth provider with server callback URL (`/oauth/callback/:provider`)
|
|
68
|
-
|
|
69
|
-
- [x] **`GET /oauth/callback/:provider` endpoint** (NEW)
|
|
70
|
-
- Receives callback from OAuth provider
|
|
71
|
-
- Verifies state against stored challenges
|
|
72
|
-
- Exchanges provider code for tokens (server-side)
|
|
73
|
-
- Fetches and verifies user info (email must be verified)
|
|
74
|
-
- Generates single-use Dexie Cloud authorization code (64 chars, 5 min TTL)
|
|
75
|
-
- Stores user claims with auth code in challenges table
|
|
76
|
-
- Returns HTML page that delivers auth code to client via:
|
|
77
|
-
- `postMessage` to `window.opener` (popup flow)
|
|
78
|
-
- Redirect to custom URL scheme (Capacitor/native)
|
|
79
|
-
- Redirect to HTTPS URL (full page redirect flow)
|
|
80
|
-
|
|
81
|
-
- [x] **`POST /token` with `grant_type: "authorization_code"`**
|
|
82
|
-
- Validates Dexie auth code (not provider code)
|
|
83
|
-
- Verifies code is `dexie_oauth` type and within 5 min TTL
|
|
84
|
-
- Extracts stored user claims (sub, email, name, picture, etc.)
|
|
85
|
-
- Generates Dexie Cloud access/refresh tokens
|
|
86
|
-
- `sub` format: `{provider}:{user_id}`
|
|
87
|
-
|
|
47
|
+
- [x] **`GET /auth-providers` endpoint** - Returns enabled providers and OTP status
|
|
48
|
+
- [x] **`GET /oauth/login/:provider` endpoint** - Initiates OAuth flow with PKCE
|
|
49
|
+
- [x] **`GET /oauth/callback/:provider` endpoint** - Handles provider callback, redirects with `dxc-auth`
|
|
50
|
+
- [x] **`POST /token` with `grant_type: "authorization_code"`** - Exchanges Dexie auth code for tokens
|
|
88
51
|
- [x] **OAuth helper functions** (`oauth-helpers.ts`)
|
|
89
|
-
- `getProviderEndpoints()` - Returns auth/token/userinfo URLs per provider
|
|
90
|
-
- `shouldUsePKCE()` - Determines if provider supports PKCE
|
|
91
|
-
- `exchangeCodeForTokens()` - Exchanges code for provider tokens
|
|
92
|
-
- `fetchUserInfo()` - Fetches and normalizes user info from provider
|
|
93
|
-
|
|
94
|
-
- [x] **Crypto utilities** (`crypto-utils.ts`)
|
|
95
|
-
- `generateRandomString()` - For state, PKCE code_verifier, and auth codes
|
|
96
|
-
|
|
97
|
-
- [x] **Handlebars template** (`oauth-callback.handlebars`)
|
|
98
|
-
- Renders callback page with postMessage/redirect logic
|
|
99
|
-
- Handles error display
|
|
100
|
-
|
|
101
52
|
- [x] **Configuration GUI in dexie-cloud-manager**
|
|
102
|
-
- `OAuthProviderDialog.tsx` - UI for configuring OAuth providers per database
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Client-Side Tasks (dexie-cloud-addon)
|
|
107
|
-
|
|
108
|
-
> **Note**: dexie-cloud-addon is a **library**, not an app. It must support various frameworks including Next.js, Vite, Svelte, SvelteKit, Capacitor, React Native, etc.
|
|
109
|
-
|
|
110
|
-
### Backward Compatibility Requirements
|
|
111
|
-
|
|
112
|
-
The implementation must be backward compatible in several scenarios:
|
|
113
|
-
|
|
114
|
-
#### 1. Apps with Custom Login GUI (not using default-ui)
|
|
115
|
-
|
|
116
|
-
Apps that implement their own login UI by subscribing to `db.cloud.userInteraction` must continue to work without changes. The new `DXCProviderSelection` type should only be emitted when:
|
|
117
|
-
- The server has OAuth providers configured AND enabled
|
|
118
|
-
- The client has NOT opted out via `db.cloud.configure({ socialAuth: false })`
|
|
119
|
-
|
|
120
|
-
**Solution**: Fetch `/auth-providers` first. If it returns providers, emit `DXCProviderSelection`. If empty or fails, emit `DXCEmailPrompt` as before. Apps with custom GUIs that don't handle `DXCProviderSelection` can:
|
|
121
|
-
- Add `socialAuth: false` to their config to disable it
|
|
122
|
-
- Or update their UI to handle the new type
|
|
123
|
-
|
|
124
|
-
#### 2. requireAuth with Unknown Server Capabilities
|
|
125
|
-
|
|
126
|
-
When `requireAuth: true`, the client must authenticate before accessing the database. The `/auth-providers` endpoint must be accessible **without authentication** (public read-only endpoint) so the client can discover available auth methods.
|
|
127
|
-
|
|
128
|
-
**Note**: The server already implements `/auth-providers` as a public endpoint (uses `readRateLimiter`, no auth required).
|
|
129
53
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
Customers running older dexie-cloud-server versions won't have the `/auth-providers` endpoint. The client must handle this gracefully:
|
|
133
|
-
|
|
134
|
-
**Solution**:
|
|
135
|
-
- Catch 404/network errors from `/auth-providers`
|
|
136
|
-
- Fall back to OTP-only flow (existing `DXCEmailPrompt` behavior)
|
|
137
|
-
- Never emit `DXCProviderSelection` if endpoint unavailable
|
|
138
|
-
- Log a debug message for troubleshooting
|
|
139
|
-
|
|
140
|
-
#### 4. Client Opt-Out
|
|
141
|
-
|
|
142
|
-
Even if the server has OAuth configured, apps may want to disable it client-side (e.g., during migration, or for specific deployments).
|
|
143
|
-
|
|
144
|
-
**Solution**: Add `socialAuth` option to `DexieCloudOptions`:
|
|
145
|
-
```typescript
|
|
146
|
-
interface DexieCloudOptions {
|
|
147
|
-
// ... existing options
|
|
148
|
-
|
|
149
|
-
/** Enable social/OAuth authentication.
|
|
150
|
-
* - true: Fetch providers from server, show if available (default)
|
|
151
|
-
* - false: Disable OAuth, always use OTP flow
|
|
152
|
-
*/
|
|
153
|
-
socialAuth?: boolean; // Default: true
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### ✅ Completed
|
|
54
|
+
### ✅ Client-Side (dexie-cloud-addon) - COMPLETE
|
|
158
55
|
|
|
159
56
|
#### Types in dexie-cloud-common
|
|
160
57
|
|
|
161
|
-
- [x]
|
|
162
|
-
- [x]
|
|
163
|
-
- [x]
|
|
164
|
-
- [x] **Extend `TokenRequest` types** - Added `AuthorizationCodeTokenRequest` (`libs/dexie-cloud-common/src/AuthorizationCodeTokenRequest.ts`)
|
|
165
|
-
|
|
166
|
-
### 🔲 TODO
|
|
167
|
-
|
|
168
|
-
#### Types in dexie-cloud-common
|
|
169
|
-
|
|
170
|
-
> Types that reflect server API data should be placed in `dexie-cloud-common` (shared between server and client).
|
|
171
|
-
|
|
172
|
-
- [x] **Create `OAuthProviderInfo` type** (`libs/dexie-cloud-common`)
|
|
173
|
-
```typescript
|
|
174
|
-
interface OAuthProviderInfo {
|
|
175
|
-
type: 'google' | 'github' | 'microsoft' | 'apple' | 'custom-oauth2';
|
|
176
|
-
name: string; // Provider identifier (e.g., 'google' or custom name)
|
|
177
|
-
displayName: string; // Human-readable name
|
|
178
|
-
iconUrl?: string; // URL to provider icon
|
|
179
|
-
scopes?: string[];
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
- [x] **Create `AuthProvidersResponse` type** (`libs/dexie-cloud-common`)
|
|
184
|
-
```typescript
|
|
185
|
-
interface AuthProvidersResponse {
|
|
186
|
-
providers: OAuthProviderInfo[];
|
|
187
|
-
otpEnabled: boolean;
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
- [x] **Create `OAuthResultMessage` type** (`libs/dexie-cloud-common`)
|
|
192
|
-
```typescript
|
|
193
|
-
interface OAuthResultMessage {
|
|
194
|
-
type: 'dexie:oauthResult';
|
|
195
|
-
code?: string; // Dexie auth code
|
|
196
|
-
error?: string; // Error message
|
|
197
|
-
provider: string;
|
|
198
|
-
state: string;
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
- [x] **Extend `TokenRequest` types** (`libs/dexie-cloud-common`)
|
|
203
|
-
- Add `AuthorizationCodeTokenRequest` type for `grant_type: "authorization_code"`
|
|
58
|
+
- [x] **`OAuthProviderInfo` type** - Provider metadata
|
|
59
|
+
- [x] **`AuthProvidersResponse` type** - Response from `/auth-providers`
|
|
60
|
+
- [x] **`AuthorizationCodeTokenRequest` type** - Token request for OAuth codes
|
|
204
61
|
|
|
205
62
|
#### Types and Interfaces in dexie-cloud-addon
|
|
206
63
|
|
|
207
|
-
- [x] **
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
- Keep existing `email`, `userId`, `grant_type`, `otpId`, `otp`
|
|
211
|
-
|
|
212
|
-
- [x] **Add `DXCProviderSelection` to `DXCUserInteraction`** (`src/types/DXCUserInteraction.ts`)
|
|
213
|
-
|
|
214
|
-
The current `DXCUserInteraction` only supports text input fields. For OAuth, we need clickable provider buttons. Add a new interaction type:
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
/** When the system needs user to select a login method (OAuth provider or OTP) */
|
|
218
|
-
export interface DXCProviderSelection {
|
|
219
|
-
type: 'provider-selection';
|
|
220
|
-
title: string;
|
|
221
|
-
alerts: DXCAlert[];
|
|
222
|
-
providers: OAuthProviderInfo[]; // Available OAuth providers
|
|
223
|
-
otpEnabled: boolean; // Whether email/OTP option is available
|
|
224
|
-
fields: {}; // Empty - no text fields
|
|
225
|
-
submitLabel?: undefined; // No submit button - provider buttons instead
|
|
226
|
-
cancelLabel: string;
|
|
227
|
-
onSelectProvider: (providerName: string) => void;
|
|
228
|
-
onSelectOtp: () => void; // User chose email/OTP instead
|
|
229
|
-
onCancel: () => void;
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Update the union type:
|
|
234
|
-
```typescript
|
|
235
|
-
export type DXCUserInteraction =
|
|
236
|
-
| DXCGenericUserInteraction
|
|
237
|
-
| DXCEmailPrompt
|
|
238
|
-
| DXCOTPPrompt
|
|
239
|
-
| DXCMessageAlert
|
|
240
|
-
| DXCLogoutConfirmation
|
|
241
|
-
| DXCProviderSelection; // NEW
|
|
242
|
-
```
|
|
64
|
+
- [x] **Extended `LoginHints` interface** - Added `provider`, `oauthCode`
|
|
65
|
+
- [x] **`DXCProviderSelection` interaction type** - For provider selection UI
|
|
66
|
+
- [x] **`DexieCloudOptions` extension** - Added `socialAuth`, `oauthRedirectUri`
|
|
243
67
|
|
|
244
68
|
#### Core Authentication Flow
|
|
245
69
|
|
|
246
|
-
- [x]
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
- Respects `socialAuth: false` option (returns empty providers without fetching)
|
|
255
|
-
|
|
256
|
-
- [x] **Update authentication flow to check providers first**
|
|
257
|
-
- Before prompting for email, call `fetchAuthProviders()`
|
|
258
|
-
- If `providers.length > 0`:
|
|
259
|
-
- Emit `DXCProviderSelection` interaction
|
|
260
|
-
- Wait for user to select provider or OTP
|
|
261
|
-
- If `providers.length === 0` or `otpEnabled` only:
|
|
262
|
-
- Emit `DXCEmailPrompt` as before (existing behavior)
|
|
263
|
-
|
|
264
|
-
- [x] **Create `oauthLogin()` function** (`src/authentication/oauthLogin.ts`)
|
|
265
|
-
- Opens popup window to `/oauth/login/:provider`
|
|
266
|
-
- Listens for `postMessage` with `type: 'dexie:oauthResult'`
|
|
267
|
-
- Validates message origin
|
|
268
|
-
- Returns auth code or throws on error/cancel
|
|
269
|
-
- Handles popup blocked scenario (fallback to redirect?)
|
|
270
|
-
|
|
271
|
-
- [x] **Create `exchangeOAuthCode()` function** (`src/authentication/exchangeOAuthCode.ts`)
|
|
272
|
-
- Sends `POST /token` with:
|
|
273
|
-
```json
|
|
274
|
-
{
|
|
275
|
-
"grant_type": "authorization_code",
|
|
276
|
-
"code": "<DEXIE_AUTH_CODE>",
|
|
277
|
-
"public_key": "<SPA_PUBLIC_KEY>",
|
|
278
|
-
"scopes": ["ACCESS_DB"]
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
- Returns `TokenFinalResponse`
|
|
282
|
-
|
|
283
|
-
- [x] **Update `login()` function** (`src/authentication/login.ts`)
|
|
284
|
-
- If `hints.provider` is set, use OAuth flow instead of OTP
|
|
285
|
-
- If `hints.oauthCode` is set, exchange code directly (for redirect/deep link flows)
|
|
286
|
-
- Integrate with existing `authenticate()` flow
|
|
287
|
-
|
|
288
|
-
- [x] **Create `handleOAuthCallback()` function** (`src/authentication/handleOAuthCallback.ts`)
|
|
289
|
-
- For redirect/deep link flows where app needs to handle the callback
|
|
290
|
-
- Parses `code`, `provider`, `state`, `error` from URL
|
|
291
|
-
- Completes login flow by calling `login({ oauthCode, provider })`
|
|
292
|
-
- Can be called by apps on their callback route
|
|
70
|
+
- [x] **`fetchAuthProviders()`** - Fetches available providers from server
|
|
71
|
+
- [x] **`startOAuthRedirect()`** - Initiates OAuth via full page redirect
|
|
72
|
+
- [x] **`parseOAuthCallback()`** - Parses `dxc-auth` query parameter
|
|
73
|
+
- [x] **`cleanupOAuthUrl()`** - Removes `dxc-auth` from URL via `history.replaceState()`
|
|
74
|
+
- [x] **`exchangeOAuthCode()`** - Exchanges Dexie auth code for tokens
|
|
75
|
+
- [x] **OAuth detection in `configure()`** - Auto-detects `dxc-auth` on page load
|
|
76
|
+
- [x] **OAuth processing in `onDbReady`** - Completes login when database is ready
|
|
77
|
+
- [x] **Updated `login()` function** - Supports `provider` and `oauthCode` hints
|
|
293
78
|
|
|
294
79
|
#### Default UI Components
|
|
295
80
|
|
|
296
|
-
- [x]
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
- [x] **Create `AuthProviderButton` component** (`src/default-ui/AuthProviderButton.tsx`)
|
|
303
|
-
- Renders button for a single OAuth provider
|
|
304
|
-
- Displays provider icon and name
|
|
305
|
-
- Follows provider branding guidelines (Google, Apple, Microsoft have strict rules)
|
|
306
|
-
- Supports light/dark mode
|
|
307
|
-
|
|
308
|
-
- [x] **Update `LoginDialog.tsx`**
|
|
309
|
-
- Handle OAuth popup flow when provider button clicked (via `onSelectProvider`)
|
|
310
|
-
- Handle loading/error states
|
|
311
|
-
|
|
312
|
-
- [x] **Update main dialog renderer** (`src/default-ui/index.tsx`)
|
|
313
|
-
- Render `ProviderSelectionDialog` when `type === 'provider-selection'`
|
|
314
|
-
|
|
315
|
-
- [x] **Update `Styles.ts`**
|
|
316
|
-
- Add styles for OAuth provider buttons
|
|
317
|
-
- Provider-specific button colors (Google blue, GitHub black, etc.)
|
|
318
|
-
- Dark mode variants
|
|
319
|
-
|
|
320
|
-
#### Capacitor / Mobile Support
|
|
321
|
-
|
|
322
|
-
- [x] **Document deep link handling** (not library code, but patterns)
|
|
323
|
-
- Show how apps should register custom URL scheme
|
|
324
|
-
- Provide example of handling `appUrlOpen` event
|
|
325
|
-
- Call `db.cloud.login({ oauthCode, provider })` from handler
|
|
326
|
-
|
|
327
|
-
- [x] **Support `redirect_uri` configuration**
|
|
328
|
-
- Allow apps to configure their redirect URI in `db.cloud.configure()`
|
|
329
|
-
- Use for Capacitor apps with custom URL schemes
|
|
330
|
-
- Use for full-page redirect flows
|
|
331
|
-
|
|
332
|
-
#### DexieCloudOptions Extension
|
|
333
|
-
|
|
334
|
-
- [x] **Add OAuth-related options** (`src/DexieCloudOptions.ts`)
|
|
335
|
-
```typescript
|
|
336
|
-
interface DexieCloudOptions {
|
|
337
|
-
// ... existing options
|
|
338
|
-
|
|
339
|
-
/** Enable social/OAuth authentication.
|
|
340
|
-
* - true (default): Fetch providers from server, show if available
|
|
341
|
-
* - false: Disable OAuth, always use OTP flow
|
|
342
|
-
*
|
|
343
|
-
* Use `false` for backward compatibility if your custom login UI
|
|
344
|
-
* doesn't handle the `DXCProviderSelection` interaction type yet.
|
|
345
|
-
*/
|
|
346
|
-
socialAuth?: boolean;
|
|
347
|
-
|
|
348
|
-
/** Redirect URI for OAuth callback (Capacitor/redirect flows).
|
|
349
|
-
* For web popups, this is auto-detected from window.location.origin.
|
|
350
|
-
* Required for:
|
|
351
|
-
* - Capacitor apps: 'myapp://oauth-callback'
|
|
352
|
-
* - Full-page redirect flows: 'https://myapp.com/oauth-callback'
|
|
353
|
-
*/
|
|
354
|
-
oauthRedirectUri?: string;
|
|
355
|
-
|
|
356
|
-
/** Use popup window for OAuth flow.
|
|
357
|
-
* - true (default for web): Opens OAuth in popup, uses postMessage
|
|
358
|
-
* - false: Opens OAuth in same window or system browser (Capacitor)
|
|
359
|
-
*/
|
|
360
|
-
oauthPopup?: boolean;
|
|
361
|
-
}
|
|
362
|
-
```
|
|
81
|
+
- [x] **`ProviderSelectionDialog`** - Renders provider selection screen
|
|
82
|
+
- [x] **`AuthProviderButton`** - Renders individual provider buttons with icons
|
|
83
|
+
- [x] **`OtpButton`** - "Continue with email" option
|
|
84
|
+
- [x] **Updated `LoginDialog.tsx`** - Handles OAuth redirect flow
|
|
85
|
+
- [x] **Updated `Styles.ts`** - Provider button styles
|
|
363
86
|
|
|
364
87
|
#### Error Handling
|
|
365
88
|
|
|
366
|
-
- [x]
|
|
367
|
-
- Extends existing error handling
|
|
368
|
-
- Error codes: `popup_blocked`, `popup_closed`, `access_denied`, `invalid_state`, `email_not_verified`, `expired_code`
|
|
369
|
-
- User-friendly messages
|
|
89
|
+
- [x] **`OAuthError` class** - Error codes: `access_denied`, `invalid_state`, `email_not_verified`, `expired_code`, `provider_error`, `network_error`
|
|
370
90
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
- Provider error → show error alert
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🔲 Remaining TODO
|
|
375
94
|
|
|
376
|
-
|
|
95
|
+
### Testing
|
|
377
96
|
|
|
378
97
|
- [ ] **Unit tests for OAuth flow**
|
|
379
|
-
-
|
|
380
|
-
- Test popup lifecycle handling
|
|
98
|
+
- Test `parseOAuthCallback()` with various `dxc-auth` payloads
|
|
381
99
|
- Test error scenarios
|
|
100
|
+
- Test URL cleanup
|
|
382
101
|
|
|
383
102
|
- [ ] **Integration tests**
|
|
384
|
-
- Test with mock
|
|
103
|
+
- Test full redirect flow with mock server
|
|
385
104
|
- Test token exchange
|
|
386
105
|
|
|
387
|
-
|
|
106
|
+
- [ ] **Manual testing**
|
|
107
|
+
- Test with `samples/dexie-cloud-todo-app`
|
|
108
|
+
- Test with Capacitor app (deep links)
|
|
388
109
|
|
|
389
|
-
|
|
110
|
+
### Documentation
|
|
390
111
|
|
|
391
112
|
- [ ] **Update README.md**
|
|
392
113
|
- Document OAuth login: `db.cloud.login({ provider: 'google' })`
|
|
393
114
|
- Show Capacitor integration pattern
|
|
394
|
-
- Explain redirect
|
|
115
|
+
- Explain redirect flow
|
|
116
|
+
|
|
117
|
+
- [ ] **Update dexie.org docs**
|
|
118
|
+
- Add OAuth configuration guide
|
|
119
|
+
- Document `socialAuth` and `oauthRedirectUri` options
|
|
395
120
|
|
|
396
121
|
---
|
|
397
122
|
|
|
398
123
|
## Client Integration Patterns
|
|
399
124
|
|
|
400
|
-
### Web SPA (
|
|
125
|
+
### Web SPA (Redirect Flow)
|
|
401
126
|
|
|
402
127
|
```typescript
|
|
403
|
-
//
|
|
128
|
+
// Configure database
|
|
129
|
+
db.cloud.configure({
|
|
130
|
+
databaseUrl: 'https://mydb.dexie.cloud'
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// OAuth callback is handled automatically!
|
|
134
|
+
// When page loads with ?dxc-auth=..., the addon:
|
|
135
|
+
// 1. Detects the parameter in configure()
|
|
136
|
+
// 2. Cleans up the URL immediately
|
|
137
|
+
// 3. Completes login in db.on('ready')
|
|
138
|
+
|
|
139
|
+
// To manually initiate OAuth (e.g., from custom UI):
|
|
404
140
|
await db.cloud.login({ provider: 'google' });
|
|
405
|
-
//
|
|
141
|
+
// Page redirects to OAuth provider, then back with auth code
|
|
406
142
|
```
|
|
407
143
|
|
|
408
144
|
### Capacitor / Native App
|
|
409
145
|
|
|
410
146
|
```typescript
|
|
411
|
-
//
|
|
147
|
+
// Configure with custom URL scheme
|
|
412
148
|
db.cloud.configure({
|
|
413
149
|
databaseUrl: 'https://mydb.dexie.cloud',
|
|
414
|
-
oauthRedirectUri: 'myapp://
|
|
415
|
-
oauthPopup: false
|
|
150
|
+
oauthRedirectUri: 'myapp://'
|
|
416
151
|
});
|
|
417
152
|
|
|
418
153
|
// Handle deep link in app
|
|
419
154
|
App.addListener('appUrlOpen', async ({ url }) => {
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
155
|
+
const callback = handleOAuthCallback(url);
|
|
156
|
+
if (callback) {
|
|
157
|
+
await db.cloud.login({
|
|
158
|
+
oauthCode: callback.code,
|
|
159
|
+
provider: callback.provider
|
|
160
|
+
});
|
|
425
161
|
}
|
|
426
162
|
});
|
|
427
163
|
|
|
@@ -429,25 +165,6 @@ App.addListener('appUrlOpen', async ({ url }) => {
|
|
|
429
165
|
await db.cloud.login({ provider: 'google' });
|
|
430
166
|
```
|
|
431
167
|
|
|
432
|
-
### Full Page Redirect (No Popup)
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
db.cloud.configure({
|
|
436
|
-
databaseUrl: 'https://mydb.dexie.cloud',
|
|
437
|
-
oauthRedirectUri: 'https://myapp.com/oauth-callback',
|
|
438
|
-
oauthPopup: false
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
// On /oauth-callback page:
|
|
442
|
-
const params = new URLSearchParams(window.location.search);
|
|
443
|
-
const code = params.get('code');
|
|
444
|
-
const provider = params.get('provider');
|
|
445
|
-
if (code && provider) {
|
|
446
|
-
await db.cloud.login({ oauthCode: code, provider });
|
|
447
|
-
window.history.replaceState({}, '', '/'); // Clean URL
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
168
|
---
|
|
452
169
|
|
|
453
170
|
## Architecture Diagram
|
|
@@ -456,30 +173,29 @@ if (code && provider) {
|
|
|
456
173
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
457
174
|
│ CLIENT (dexie-cloud-addon) │
|
|
458
175
|
│ │
|
|
459
|
-
│
|
|
460
|
-
│ │ LoginDialog
|
|
461
|
-
│ │ (default UI)│ │
|
|
462
|
-
│
|
|
463
|
-
│
|
|
464
|
-
│
|
|
465
|
-
│
|
|
466
|
-
│
|
|
467
|
-
│
|
|
468
|
-
│
|
|
469
|
-
│
|
|
470
|
-
│
|
|
471
|
-
│
|
|
472
|
-
│
|
|
473
|
-
│
|
|
474
|
-
│
|
|
475
|
-
│
|
|
476
|
-
│
|
|
477
|
-
│
|
|
478
|
-
│
|
|
479
|
-
│
|
|
480
|
-
│
|
|
481
|
-
│
|
|
482
|
-
│ └─────────────┘ │
|
|
176
|
+
│ ┌─────────────────┐ ┌───────────────────┐ │
|
|
177
|
+
│ │ LoginDialog │───▶│ startOAuthRedirect│──▶ window.location.href = │
|
|
178
|
+
│ │ (default UI) │ │ () │ /oauth/login/:provider │
|
|
179
|
+
│ └─────────────────┘ └───────────────────┘ │
|
|
180
|
+
│ │
|
|
181
|
+
│ ... page navigates away, user authenticates ... │
|
|
182
|
+
│ │
|
|
183
|
+
│ ┌─────────────────┐ ┌───────────────────┐ │
|
|
184
|
+
│ │ Page loads with │───▶│ db.cloud. │──▶ Detects dxc-auth param │
|
|
185
|
+
│ │ ?dxc-auth=... │ │ configure() │ Cleans URL immediately │
|
|
186
|
+
│ └─────────────────┘ └───────────────────┘ Stores pending code │
|
|
187
|
+
│ │ │
|
|
188
|
+
│ ▼ │
|
|
189
|
+
│ ┌─────────────────┐ ┌─────────────────────────┐ │
|
|
190
|
+
│ │ db.on('ready') │───▶│ POST /token │ │
|
|
191
|
+
│ │ │ │ grant_type: │ │
|
|
192
|
+
│ │ │◀───│ authorization_code │ │
|
|
193
|
+
│ └─────────────────┘ └─────────────────────────┘ │
|
|
194
|
+
│ │ │
|
|
195
|
+
│ ▼ │
|
|
196
|
+
│ ┌─────────────────┐ │
|
|
197
|
+
│ │ User logged in! │ │
|
|
198
|
+
│ └─────────────────┘ │
|
|
483
199
|
└──────────────────────────────────────────────────────────────────────────────┘
|
|
484
200
|
|
|
485
201
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
@@ -495,7 +211,7 @@ if (code && provider) {
|
|
|
495
211
|
│ ├── Exchange code for provider tokens (server-side!) │
|
|
496
212
|
│ ├── Fetch user info, verify email │
|
|
497
213
|
│ ├── Generate Dexie auth code (single-use, 5 min TTL) │
|
|
498
|
-
│ └──
|
|
214
|
+
│ └── HTTP 302 redirect with ?dxc-auth=<base64url-json> │
|
|
499
215
|
│ │
|
|
500
216
|
│ POST /token (grant_type: authorization_code) │
|
|
501
217
|
│ ├── Validate Dexie auth code │
|
|
@@ -506,40 +222,53 @@ if (code && provider) {
|
|
|
506
222
|
|
|
507
223
|
---
|
|
508
224
|
|
|
225
|
+
## The `dxc-auth` Query Parameter
|
|
226
|
+
|
|
227
|
+
The OAuth callback uses a single `dxc-auth` query parameter containing base64url-encoded JSON to avoid collisions with app query parameters:
|
|
228
|
+
|
|
229
|
+
**Success:**
|
|
230
|
+
```json
|
|
231
|
+
{ "code": "DEXIE_AUTH_CODE", "provider": "google", "state": "..." }
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Error:**
|
|
235
|
+
```json
|
|
236
|
+
{ "error": "Error message", "provider": "google", "state": "..." }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Example URL:
|
|
240
|
+
```
|
|
241
|
+
https://myapp.com/?dxc-auth=eyJjb2RlIjoiLi4uIiwicHJvdmlkZXIiOiJnb29nbGUiLCJzdGF0ZSI6Ii4uLiJ9
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
509
246
|
## Security Properties
|
|
510
247
|
|
|
511
248
|
- 🛡 **No provider tokens reach client** - All provider exchange happens server-side
|
|
512
249
|
- 🛡 **Single-use Dexie auth codes** - 5 minute TTL, deleted after use
|
|
513
250
|
- 🛡 **PKCE protection** - Prevents code interception (where supported)
|
|
514
251
|
- 🛡 **State parameter** - CSRF protection, stored server-side
|
|
515
|
-
- 🛡 **Origin validation** -
|
|
252
|
+
- 🛡 **Origin validation** - redirect_uri validated and whitelisted
|
|
516
253
|
- 🛡 **Email verification enforced** - Server rejects unverified emails
|
|
254
|
+
- 🛡 **No tokens in URL fragments** - Auth code in query param, not fragment
|
|
517
255
|
|
|
518
256
|
---
|
|
519
257
|
|
|
520
|
-
## Key Files
|
|
258
|
+
## Key Files
|
|
521
259
|
|
|
522
|
-
**
|
|
260
|
+
**dexie-cloud-common:**
|
|
523
261
|
- `src/OAuthProviderInfo.ts`
|
|
524
262
|
- `src/AuthProvidersResponse.ts`
|
|
525
|
-
- `src/OAuthResultMessage.ts`
|
|
526
263
|
- `src/AuthorizationCodeTokenRequest.ts`
|
|
527
264
|
|
|
528
|
-
**
|
|
529
|
-
- `src/
|
|
530
|
-
- `src/authentication/
|
|
531
|
-
- `src/authentication/
|
|
532
|
-
- `src/authentication/
|
|
533
|
-
- `src/
|
|
534
|
-
- `src/
|
|
535
|
-
- `src/default-ui/
|
|
536
|
-
- `src/
|
|
537
|
-
|
|
538
|
-
**Files to modify:**
|
|
539
|
-
- `src/DexieCloudAPI.ts` - Extend `LoginHints`
|
|
540
|
-
- `src/DexieCloudOptions.ts` - Add OAuth options
|
|
541
|
-
- `src/authentication/login.ts` - Integrate OAuth flow
|
|
542
|
-
- `src/authentication/interactWithUser.ts` - Add `promptForProvider()` helper
|
|
543
|
-
- `src/default-ui/index.tsx` - Render `ProviderSelectionDialog`
|
|
544
|
-
- `src/default-ui/LoginDialog.tsx` - Handle provider selection callbacks
|
|
545
|
-
- `src/default-ui/Styles.ts` - Provider button styles
|
|
265
|
+
**dexie-cloud-addon:**
|
|
266
|
+
- `src/authentication/oauthLogin.ts` - `startOAuthRedirect()`, `mapOAuthError()`
|
|
267
|
+
- `src/authentication/handleOAuthCallback.ts` - `parseOAuthCallback()`, `cleanupOAuthUrl()`
|
|
268
|
+
- `src/authentication/exchangeOAuthCode.ts` - Token exchange
|
|
269
|
+
- `src/authentication/fetchAuthProviders.ts` - Fetch available providers
|
|
270
|
+
- `src/errors/OAuthError.ts` - OAuth-specific errors
|
|
271
|
+
- `src/default-ui/ProviderSelectionDialog.tsx` - Provider selection UI
|
|
272
|
+
- `src/default-ui/AuthProviderButton.tsx` - Provider button component
|
|
273
|
+
- `src/dexie-cloud-client.ts` - OAuth detection in `configure()`, processing in `onDbReady`
|
|
274
|
+
- `src/DexieCloudOptions.ts` - `socialAuth`, `oauthRedirectUri` options
|