dexie-cloud-addon 4.2.5 → 4.3.2

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.
Files changed (170) hide show
  1. package/TODO-SOCIALAUTH.md +274 -0
  2. package/dist/modern/DexieCloudAPI.d.ts +4 -0
  3. package/dist/modern/DexieCloudOptions.d.ts +17 -0
  4. package/dist/modern/authentication/exchangeOAuthCode.d.ts +23 -0
  5. package/dist/modern/authentication/fetchAuthProviders.d.ts +14 -0
  6. package/dist/modern/authentication/handleOAuthCallback.d.ts +63 -0
  7. package/dist/modern/authentication/interactWithUser.d.ts +22 -0
  8. package/dist/modern/authentication/oauthLogin.d.ts +38 -0
  9. package/dist/modern/default-ui/AuthProviderButton.d.ts +21 -0
  10. package/dist/modern/default-ui/LoginDialog.d.ts +30 -2
  11. package/dist/modern/default-ui/OptionButton.d.ts +21 -0
  12. package/dist/modern/default-ui/ProviderSelectionDialog.d.ts +7 -0
  13. package/dist/modern/default-ui/SelectDialog.d.ts +10 -0
  14. package/dist/modern/dexie-cloud-addon.js +686 -13
  15. package/dist/modern/dexie-cloud-addon.js.map +1 -1
  16. package/dist/modern/dexie-cloud-addon.min.js +1 -1
  17. package/dist/modern/dexie-cloud-addon.min.js.gz +0 -0
  18. package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
  19. package/dist/modern/errors/OAuthError.d.ts +10 -0
  20. package/dist/modern/service-worker.js +686 -13
  21. package/dist/modern/service-worker.js.map +1 -1
  22. package/dist/modern/service-worker.min.js +1 -1
  23. package/dist/modern/service-worker.min.js.map +1 -1
  24. package/dist/modern/types/DXCUserInteraction.d.ts +32 -1
  25. package/dist/umd/DISABLE_SERVICEWORKER_STRATEGY.d.ts +1 -0
  26. package/dist/umd/DXCWebSocketStatus.d.ts +1 -0
  27. package/dist/umd/DexieCloudAPI.d.ts +75 -0
  28. package/dist/umd/DexieCloudOptions.d.ts +27 -0
  29. package/dist/umd/DexieCloudSyncOptions.d.ts +4 -0
  30. package/dist/umd/DexieCloudTable.d.ts +18 -0
  31. package/dist/umd/InvalidLicenseError.d.ts +5 -0
  32. package/dist/umd/Invite.d.ts +8 -0
  33. package/dist/umd/PermissionChecker.d.ts +15 -0
  34. package/dist/umd/TSON.d.ts +17 -0
  35. package/dist/umd/WSObservable.d.ts +72 -0
  36. package/dist/umd/associate.d.ts +1 -0
  37. package/dist/umd/authentication/AuthPersistedContext.d.ts +9 -0
  38. package/dist/umd/authentication/TokenErrorResponseError.d.ts +10 -0
  39. package/dist/umd/authentication/TokenExpiredError.d.ts +3 -0
  40. package/dist/umd/authentication/UNAUTHORIZED_USER.d.ts +2 -0
  41. package/dist/umd/authentication/authenticate.d.ts +13 -0
  42. package/dist/umd/authentication/currentUserObservable.d.ts +1 -0
  43. package/dist/umd/authentication/interactWithUser.d.ts +21 -0
  44. package/dist/umd/authentication/login.d.ts +3 -0
  45. package/dist/umd/authentication/logout.d.ts +5 -0
  46. package/dist/umd/authentication/otpFetchTokenCallback.d.ts +3 -0
  47. package/dist/umd/authentication/setCurrentUser.d.ts +14 -0
  48. package/dist/umd/authentication/waitUntil.d.ts +3 -0
  49. package/dist/umd/computeSyncState.d.ts +4 -0
  50. package/dist/umd/createSharedValueObservable.d.ts +3 -0
  51. package/dist/umd/currentUserEmitter.d.ts +3 -0
  52. package/dist/umd/db/DexieCloudDB.d.ts +61 -0
  53. package/dist/umd/db/entities/BaseRevisionMapEntry.d.ts +5 -0
  54. package/dist/umd/db/entities/EntityCommon.d.ts +5 -0
  55. package/dist/umd/db/entities/GuardedJob.d.ts +5 -0
  56. package/dist/umd/db/entities/Member.d.ts +19 -0
  57. package/dist/umd/db/entities/PersistedSyncState.d.ts +22 -0
  58. package/dist/umd/db/entities/Realm.d.ts +14 -0
  59. package/dist/umd/db/entities/Role.d.ts +11 -0
  60. package/dist/umd/db/entities/UserLogin.d.ts +23 -0
  61. package/dist/umd/default-ui/Dialog.d.ts +5 -0
  62. package/dist/umd/default-ui/LoginDialog.d.ts +3 -0
  63. package/dist/umd/default-ui/Styles.d.ts +3 -0
  64. package/dist/umd/default-ui/index.d.ts +24 -0
  65. package/dist/umd/define-ydoc-trigger.d.ts +3 -0
  66. package/dist/umd/dexie-cloud-addon.d.ts +3 -0
  67. package/dist/umd/dexie-cloud-addon.js +687 -14
  68. package/dist/umd/dexie-cloud-addon.js.gz +0 -0
  69. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  70. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  71. package/dist/umd/dexie-cloud-addon.min.js.gz +0 -0
  72. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  73. package/dist/umd/dexie-cloud-client.d.ts +23 -0
  74. package/dist/umd/errors/HttpError.d.ts +5 -0
  75. package/dist/umd/extend-dexie-interface.d.ts +23 -0
  76. package/dist/umd/getGlobalRolesObservable.d.ts +5 -0
  77. package/dist/umd/getInternalAccessControlObservable.d.ts +12 -0
  78. package/dist/umd/getInvitesObservable.d.ts +23 -0
  79. package/dist/umd/getPermissionsLookupObservable.d.ts +16 -0
  80. package/dist/umd/getTiedRealmId.d.ts +2 -0
  81. package/dist/umd/helpers/BroadcastedAndLocalEvent.d.ts +8 -0
  82. package/dist/umd/helpers/CancelToken.d.ts +4 -0
  83. package/dist/umd/helpers/IS_SERVICE_WORKER.d.ts +1 -0
  84. package/dist/umd/helpers/SWBroadcastChannel.d.ts +12 -0
  85. package/dist/umd/helpers/allSettled.d.ts +1 -0
  86. package/dist/umd/helpers/bulkUpdate.d.ts +4 -0
  87. package/dist/umd/helpers/computeRealmSetHash.d.ts +2 -0
  88. package/dist/umd/helpers/date-constants.d.ts +5 -0
  89. package/dist/umd/helpers/flatten.d.ts +1 -0
  90. package/dist/umd/helpers/getMutationTable.d.ts +1 -0
  91. package/dist/umd/helpers/getSyncableTables.d.ts +4 -0
  92. package/dist/umd/helpers/getTableFromMutationTable.d.ts +1 -0
  93. package/dist/umd/helpers/makeArray.d.ts +1 -0
  94. package/dist/umd/helpers/randomString.d.ts +1 -0
  95. package/dist/umd/helpers/resolveText.d.ts +16 -0
  96. package/dist/umd/helpers/throwVersionIncrementNeeded.d.ts +1 -0
  97. package/dist/umd/helpers/visibilityState.d.ts +1 -0
  98. package/dist/umd/isEagerSyncDisabled.d.ts +2 -0
  99. package/dist/umd/isFirefox.d.ts +1 -0
  100. package/dist/umd/isSafari.d.ts +2 -0
  101. package/dist/umd/mapValueObservable.d.ts +5 -0
  102. package/dist/umd/mergePermissions.d.ts +2 -0
  103. package/dist/umd/middleware-helpers/guardedTable.d.ts +11 -0
  104. package/dist/umd/middleware-helpers/idGenerationHelpers.d.ts +18 -0
  105. package/dist/umd/middlewares/createIdGenerationMiddleware.d.ts +3 -0
  106. package/dist/umd/middlewares/createImplicitPropSetterMiddleware.d.ts +3 -0
  107. package/dist/umd/middlewares/createMutationTrackingMiddleware.d.ts +17 -0
  108. package/dist/umd/middlewares/outstandingTransaction.d.ts +4 -0
  109. package/dist/umd/overrideParseStoresSpec.d.ts +4 -0
  110. package/dist/umd/performInitialSync.d.ts +4 -0
  111. package/dist/umd/permissions.d.ts +9 -0
  112. package/dist/umd/prodLog.d.ts +9 -0
  113. package/dist/umd/service-worker.d.ts +1 -0
  114. package/dist/umd/service-worker.js +687 -14
  115. package/dist/umd/service-worker.js.map +1 -1
  116. package/dist/umd/service-worker.min.js +1 -1
  117. package/dist/umd/service-worker.min.js.map +1 -1
  118. package/dist/umd/sync/DEXIE_CLOUD_SYNCER_ID.d.ts +1 -0
  119. package/dist/umd/sync/LocalSyncWorker.d.ts +7 -0
  120. package/dist/umd/sync/SyncRequiredError.d.ts +3 -0
  121. package/dist/umd/sync/applyServerChanges.d.ts +3 -0
  122. package/dist/umd/sync/connectWebSocket.d.ts +2 -0
  123. package/dist/umd/sync/encodeIdsForServer.d.ts +4 -0
  124. package/dist/umd/sync/extractRealm.d.ts +2 -0
  125. package/dist/umd/sync/getLatestRevisionsPerTable.d.ts +6 -0
  126. package/dist/umd/sync/getTablesToSyncify.d.ts +3 -0
  127. package/dist/umd/sync/isOnline.d.ts +1 -0
  128. package/dist/umd/sync/isSyncNeeded.d.ts +2 -0
  129. package/dist/umd/sync/listClientChanges.d.ts +9 -0
  130. package/dist/umd/sync/listSyncifiedChanges.d.ts +5 -0
  131. package/dist/umd/sync/messageConsumerIsReady.d.ts +2 -0
  132. package/dist/umd/sync/messagesFromServerQueue.d.ts +8 -0
  133. package/dist/umd/sync/modifyLocalObjectsWithNewUserId.d.ts +4 -0
  134. package/dist/umd/sync/myId.d.ts +1 -0
  135. package/dist/umd/sync/numUnsyncedMutations.d.ts +2 -0
  136. package/dist/umd/sync/old_startSyncingClientChanges.d.ts +39 -0
  137. package/dist/umd/sync/performGuardedJob.d.ts +2 -0
  138. package/dist/umd/sync/ratelimit.d.ts +3 -0
  139. package/dist/umd/sync/registerSyncEvent.d.ts +3 -0
  140. package/dist/umd/sync/sync.d.ts +15 -0
  141. package/dist/umd/sync/syncIfPossible.d.ts +5 -0
  142. package/dist/umd/sync/syncWithServer.d.ts +6 -0
  143. package/dist/umd/sync/triggerSync.d.ts +2 -0
  144. package/dist/umd/sync/updateBaseRevs.d.ts +5 -0
  145. package/dist/umd/types/DXCAlert.d.ts +25 -0
  146. package/dist/umd/types/DXCInputField.d.ts +11 -0
  147. package/dist/umd/types/DXCUserInteraction.d.ts +93 -0
  148. package/dist/umd/types/NewIdOptions.d.ts +3 -0
  149. package/dist/umd/types/SWMessageEvent.d.ts +3 -0
  150. package/dist/umd/types/SWSyncEvent.d.ts +4 -0
  151. package/dist/umd/types/SyncState.d.ts +9 -0
  152. package/dist/umd/types/TXExpandos.d.ts +11 -0
  153. package/dist/umd/updateSchemaFromOptions.d.ts +3 -0
  154. package/dist/umd/userIsActive.d.ts +7 -0
  155. package/dist/umd/verifyConfig.d.ts +2 -0
  156. package/dist/umd/verifySchema.d.ts +2 -0
  157. package/dist/umd/yjs/YDexieCloudSyncState.d.ts +3 -0
  158. package/dist/umd/yjs/YTable.d.ts +3 -0
  159. package/dist/umd/yjs/applyYMessages.d.ts +9 -0
  160. package/dist/umd/yjs/awareness.d.ts +3 -0
  161. package/dist/umd/yjs/createYClientUpdateObservable.d.ts +4 -0
  162. package/dist/umd/yjs/createYHandler.d.ts +2 -0
  163. package/dist/umd/yjs/downloadYDocsFromServer.d.ts +3 -0
  164. package/dist/umd/yjs/getUpdatesTable.d.ts +3 -0
  165. package/dist/umd/yjs/listUpdatesSince.d.ts +3 -0
  166. package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +26 -0
  167. package/dist/umd/yjs/reopenDocSignal.d.ts +10 -0
  168. package/dist/umd/yjs/updateYSyncStates.d.ts +6 -0
  169. package/oauth_flow.md +307 -0
  170. package/package.json +1 -1
@@ -0,0 +1,274 @@
1
+ # Social Authentication for Dexie Cloud
2
+
3
+ ## Overview
4
+
5
+ This feature adds support for OAuth 2.0 social login providers (Google, GitHub, Microsoft, Apple, and custom OAuth2) as an alternative to the existing OTP (One-Time Password) email authentication in Dexie Cloud.
6
+
7
+ **Key Design Principle**: The Dexie Cloud server acts as an OAuth broker, handling all provider interactions including the OAuth callback. The client library (dexie-cloud-addon) never receives provider tokens - only Dexie Cloud authorization codes which are exchanged for Dexie Cloud tokens.
8
+
9
+ ### Related Files
10
+
11
+ - **Detailed flow diagram**: [oauth_flow.md](oauth_flow.md) - Sequence diagrams and detailed protocol description
12
+ - **Server implementation**: `/Users/daw/repos/dexie-cloud/libs/dexie-cloud-server`
13
+ - `src/api/oauth/registerOAuthEndpoints.ts` - OAuth endpoints
14
+ - `src/api/oauth/oauth-helpers.ts` - Provider exchange logic
15
+ - `src/api/registerTokenEndpoint.ts` - Token endpoint (authorization_code grant)
16
+
17
+ ### Flow Summary
18
+
19
+ 1. **Client** fetches available auth providers from `GET /auth-providers`
20
+ 2. **Client** redirects to `GET /oauth/login/:provider` (full page redirect)
21
+ 3. **Dexie Cloud Server** redirects to OAuth provider and handles callback at `/oauth/callback/:provider`
22
+ 4. **Server** exchanges provider code for tokens, verifies email, generates single-use Dexie auth code
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`
25
+
26
+ ### Supported Providers
27
+ - **Google** - OpenID Connect with PKCE
28
+ - **GitHub** - OAuth 2.0 (client secret only)
29
+ - **Microsoft** - OpenID Connect with PKCE
30
+ - **Apple** - Sign in with Apple (form_post response mode)
31
+ - **Custom OAuth2** - Configurable endpoints for self-hosted identity providers
32
+
33
+ ### Client Delivery Methods
34
+
35
+ | Method | Use Case | Delivery Mechanism |
36
+ |--------|----------|-------------------|
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://`) |
39
+
40
+ ---
41
+
42
+ ## Implementation Status
43
+
44
+ ### ✅ Server-Side (dexie-cloud-server) - COMPLETE
45
+
46
+ - [x] **OAuth provider configuration type** (`OAuthProviderConfig`)
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
51
+ - [x] **OAuth helper functions** (`oauth-helpers.ts`)
52
+ - [x] **Configuration GUI in dexie-cloud-manager**
53
+
54
+ ### ✅ Client-Side (dexie-cloud-addon) - COMPLETE
55
+
56
+ #### Types in dexie-cloud-common
57
+
58
+ - [x] **`OAuthProviderInfo` type** - Provider metadata
59
+ - [x] **`AuthProvidersResponse` type** - Response from `/auth-providers`
60
+ - [x] **`AuthorizationCodeTokenRequest` type** - Token request for OAuth codes
61
+
62
+ #### Types and Interfaces in dexie-cloud-addon
63
+
64
+ - [x] **Extended `LoginHints` interface** - Added `provider`, `oauthCode`
65
+ - [x] **`DXCProviderSelection` interaction type** - For provider selection UI
66
+ - [x] **`DexieCloudOptions` extension** - Added `socialAuth`, `oauthRedirectUri`
67
+
68
+ #### Core Authentication Flow
69
+
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
78
+
79
+ #### Default UI Components
80
+
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
86
+
87
+ #### Error Handling
88
+
89
+ - [x] **`OAuthError` class** - Error codes: `access_denied`, `invalid_state`, `email_not_verified`, `expired_code`, `provider_error`, `network_error`
90
+
91
+ ---
92
+
93
+ ## 🔲 Remaining TODO
94
+
95
+ ### Testing
96
+
97
+ - [ ] **Unit tests for OAuth flow**
98
+ - Test `parseOAuthCallback()` with various `dxc-auth` payloads
99
+ - Test error scenarios
100
+ - Test URL cleanup
101
+
102
+ - [ ] **Integration tests**
103
+ - Test full redirect flow with mock server
104
+ - Test token exchange
105
+
106
+ - [ ] **Manual testing**
107
+ - Test with `samples/dexie-cloud-todo-app`
108
+ - Test with Capacitor app (deep links)
109
+
110
+ ### Documentation
111
+
112
+ - [ ] **Update README.md**
113
+ - Document OAuth login: `db.cloud.login({ provider: 'google' })`
114
+ - Show Capacitor integration pattern
115
+ - Explain redirect flow
116
+
117
+ - [ ] **Update dexie.org docs**
118
+ - Add OAuth configuration guide
119
+ - Document `socialAuth` and `oauthRedirectUri` options
120
+
121
+ ---
122
+
123
+ ## Client Integration Patterns
124
+
125
+ ### Web SPA (Redirect Flow)
126
+
127
+ ```typescript
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):
140
+ await db.cloud.login({ provider: 'google' });
141
+ // Page redirects to OAuth provider, then back with auth code
142
+ ```
143
+
144
+ ### Capacitor / Native App
145
+
146
+ ```typescript
147
+ // Configure with custom URL scheme
148
+ db.cloud.configure({
149
+ databaseUrl: 'https://mydb.dexie.cloud',
150
+ oauthRedirectUri: 'myapp://'
151
+ });
152
+
153
+ // Handle deep link in app
154
+ App.addListener('appUrlOpen', async ({ url }) => {
155
+ const callback = handleOAuthCallback(url);
156
+ if (callback) {
157
+ await db.cloud.login({
158
+ oauthCode: callback.code,
159
+ provider: callback.provider
160
+ });
161
+ }
162
+ });
163
+
164
+ // Initiate login (opens system browser)
165
+ await db.cloud.login({ provider: 'google' });
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Architecture Diagram
171
+
172
+ ```
173
+ ┌──────────────────────────────────────────────────────────────────────────────┐
174
+ │ CLIENT (dexie-cloud-addon) │
175
+ │ │
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
+ │ └─────────────────┘ │
199
+ └──────────────────────────────────────────────────────────────────────────────┘
200
+
201
+ ┌──────────────────────────────────────────────────────────────────────────────┐
202
+ │ DEXIE CLOUD SERVER │
203
+ │ │
204
+ │ /oauth/login/:provider │
205
+ │ ├── Generate state, PKCE │
206
+ │ ├── Store in challenges table │
207
+ │ └── Redirect to OAuth provider │
208
+ │ │
209
+ │ /oauth/callback/:provider ◀── OAuth provider redirects here │
210
+ │ ├── Verify state │
211
+ │ ├── Exchange code for provider tokens (server-side!) │
212
+ │ ├── Fetch user info, verify email │
213
+ │ ├── Generate Dexie auth code (single-use, 5 min TTL) │
214
+ │ └── HTTP 302 redirect with ?dxc-auth=<base64url-json> │
215
+ │ │
216
+ │ POST /token (grant_type: authorization_code) │
217
+ │ ├── Validate Dexie auth code │
218
+ │ ├── Extract stored user claims │
219
+ │ └── Return Dexie Cloud access + refresh tokens │
220
+ └──────────────────────────────────────────────────────────────────────────────┘
221
+ ```
222
+
223
+ ---
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
+
246
+ ## Security Properties
247
+
248
+ - 🛡 **No provider tokens reach client** - All provider exchange happens server-side
249
+ - 🛡 **Single-use Dexie auth codes** - 5 minute TTL, deleted after use
250
+ - 🛡 **PKCE protection** - Prevents code interception (where supported)
251
+ - 🛡 **State parameter** - CSRF protection, stored server-side
252
+ - 🛡 **Origin validation** - redirect_uri validated and whitelisted
253
+ - 🛡 **Email verification enforced** - Server rejects unverified emails
254
+ - 🛡 **No tokens in URL fragments** - Auth code in query param, not fragment
255
+
256
+ ---
257
+
258
+ ## Key Files
259
+
260
+ **dexie-cloud-common:**
261
+ - `src/OAuthProviderInfo.ts`
262
+ - `src/AuthProvidersResponse.ts`
263
+ - `src/AuthorizationCodeTokenRequest.ts`
264
+
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
@@ -17,6 +17,10 @@ export interface LoginHints {
17
17
  grant_type?: 'demo' | 'otp';
18
18
  otpId?: string;
19
19
  otp?: string;
20
+ /** OAuth provider name to initiate OAuth flow (e.g., 'google', 'github') */
21
+ provider?: string;
22
+ /** Dexie Cloud authorization code received from OAuth callback */
23
+ oauthCode?: string;
20
24
  }
21
25
  export interface DexieCloudAPI {
22
26
  version: string;
@@ -24,4 +24,21 @@ export interface DexieCloudOptions {
24
24
  };
25
25
  }) => Promise<TokenFinalResponse>;
26
26
  awarenessProtocol?: typeof import('y-protocols/awareness');
27
+ /** Enable social/OAuth authentication.
28
+ * - true (default): Fetch providers from server, show if available
29
+ * - false: Disable OAuth, always use OTP flow
30
+ *
31
+ * Use `false` for backward compatibility if your custom login UI
32
+ * doesn't handle the `DXCSelect` interaction type yet.
33
+ */
34
+ socialAuth?: boolean;
35
+ /** Redirect URI for OAuth callback.
36
+ * Defaults to window.location.href for web SPAs.
37
+ *
38
+ * For Capacitor/native apps, set this to a custom URL scheme:
39
+ * ```
40
+ * oauthRedirectUri: 'myapp://'
41
+ * ```
42
+ */
43
+ oauthRedirectUri?: string;
27
44
  }
@@ -0,0 +1,23 @@
1
+ import type { TokenFinalResponse } from 'dexie-cloud-common';
2
+ /** Options for exchanging an OAuth code */
3
+ export interface ExchangeOAuthCodeOptions {
4
+ /** The Dexie Cloud database URL */
5
+ databaseUrl: string;
6
+ /** The Dexie Cloud authorization code from OAuth callback */
7
+ code: string;
8
+ /** The client's public key in PEM format */
9
+ publicKey: string;
10
+ /** Requested scopes (defaults to ['ACCESS_DB']) */
11
+ scopes?: string[];
12
+ }
13
+ /**
14
+ * Exchanges a Dexie Cloud authorization code for access and refresh tokens.
15
+ *
16
+ * This is called after the OAuth callback delivers the authorization code
17
+ * via postMessage (popup flow) or redirect.
18
+ *
19
+ * @param options - Exchange options
20
+ * @returns Promise resolving to TokenFinalResponse
21
+ * @throws OAuthError or TokenErrorResponseError on failure
22
+ */
23
+ export declare function exchangeOAuthCode(options: ExchangeOAuthCodeOptions): Promise<TokenFinalResponse>;
@@ -0,0 +1,14 @@
1
+ import type { AuthProvidersResponse } from 'dexie-cloud-common';
2
+ /**
3
+ * Fetches available authentication providers from the Dexie Cloud server.
4
+ *
5
+ * @param databaseUrl - The Dexie Cloud database URL
6
+ * @param socialAuthEnabled - Whether social auth is enabled in client config (default: true)
7
+ * @returns Promise resolving to AuthProvidersResponse
8
+ *
9
+ * Handles failures gracefully:
10
+ * - 404 → Returns OTP-only (old server version)
11
+ * - Network error → Returns OTP-only
12
+ * - socialAuthEnabled: false → Returns OTP-only without fetching
13
+ */
14
+ export declare function fetchAuthProviders(databaseUrl: string, socialAuthEnabled?: boolean): Promise<AuthProvidersResponse>;
@@ -0,0 +1,63 @@
1
+ /** Parsed OAuth callback parameters from dxc-auth query parameter */
2
+ export interface OAuthCallbackParams {
3
+ /** The Dexie Cloud authorization code */
4
+ code: string;
5
+ /** The OAuth provider that was used */
6
+ provider: string;
7
+ /** The state parameter */
8
+ state: string;
9
+ }
10
+ /**
11
+ * Parses OAuth callback parameters from the dxc-auth query parameter.
12
+ *
13
+ * The dxc-auth parameter contains base64url-encoded JSON with the following structure:
14
+ * - On success: { "code": "...", "provider": "...", "state": "..." }
15
+ * - On error: { "error": "...", "provider": "...", "state": "..." }
16
+ *
17
+ * @param url - The URL to parse (defaults to window.location.href)
18
+ * @returns OAuthCallbackParams if valid callback, null otherwise
19
+ * @throws OAuthError if there's an error in the callback
20
+ */
21
+ export declare function parseOAuthCallback(url?: string): OAuthCallbackParams | null;
22
+ /**
23
+ * Validates the OAuth state parameter against the stored state.
24
+ *
25
+ * @param receivedState - The state from the callback URL
26
+ * @returns true if valid, false otherwise
27
+ */
28
+ export declare function validateOAuthState(receivedState: string): boolean;
29
+ /**
30
+ * Gets the OAuth provider from sessionStorage (for redirect flows).
31
+ */
32
+ export declare function getStoredOAuthProvider(): string | null;
33
+ /**
34
+ * Cleans up the dxc-auth query parameter from the URL.
35
+ * Call this after successfully handling the callback to clean up the browser URL.
36
+ */
37
+ export declare function cleanupOAuthUrl(): void;
38
+ /**
39
+ * Complete handler for OAuth callback.
40
+ *
41
+ * Parses the dxc-auth query parameter, validates state, and returns the parameters
42
+ * needed to complete the login flow.
43
+ *
44
+ * Note: For web SPAs using full page redirect, the dexie-cloud-addon automatically
45
+ * detects and processes the dxc-auth parameter when db.cloud.configure() is called.
46
+ * This function is primarily useful for Capacitor/native apps handling deep links.
47
+ *
48
+ * @param url - The callback URL (defaults to window.location.href)
49
+ * @returns OAuthCallbackParams if valid callback, null otherwise
50
+ * @throws OAuthError on validation failure or if callback contains an error
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Capacitor deep link handler:
55
+ * App.addListener('appUrlOpen', async ({ url }) => {
56
+ * const callback = handleOAuthCallback(url);
57
+ * if (callback) {
58
+ * await db.cloud.login({ oauthCode: callback.code, provider: callback.provider });
59
+ * }
60
+ * });
61
+ * ```
62
+ */
63
+ export declare function handleOAuthCallback(url?: string): OAuthCallbackParams | null;
@@ -1,3 +1,4 @@
1
+ import type { OAuthProviderInfo } from 'dexie-cloud-common';
1
2
  import { BehaviorSubject } from 'rxjs';
2
3
  import { DXCAlert } from '../types/DXCAlert';
3
4
  import { DXCInputField } from '../types/DXCInputField';
@@ -19,3 +20,24 @@ export declare function alertUser(userInteraction: BehaviorSubject<DXCUserIntera
19
20
  export declare function promptForEmail(userInteraction: BehaviorSubject<DXCUserInteraction | undefined>, title: string, emailHint?: string): Promise<string>;
20
21
  export declare function promptForOTP(userInteraction: BehaviorSubject<DXCUserInteraction | undefined>, email: string, alert?: DXCAlert): Promise<string>;
21
22
  export declare function confirmLogout(userInteraction: BehaviorSubject<DXCUserInteraction | undefined>, currentUserId: string, numUnsyncedChanges: number): Promise<boolean>;
23
+ /** Result from provider selection prompt */
24
+ export type ProviderSelectionResult = {
25
+ type: 'provider';
26
+ provider: string;
27
+ } | {
28
+ type: 'otp';
29
+ };
30
+ /**
31
+ * Prompts the user to select an authentication method (OAuth provider or OTP).
32
+ *
33
+ * This function converts OAuth providers and OTP option into generic DXCOption[]
34
+ * for the DXCSelect interaction, handling icon fetching and style hints.
35
+ *
36
+ * @param userInteraction - The user interaction BehaviorSubject
37
+ * @param providers - Available OAuth providers
38
+ * @param otpEnabled - Whether OTP is available
39
+ * @param title - Dialog title
40
+ * @param alerts - Optional alerts to display
41
+ * @returns Promise resolving to the user's selection
42
+ */
43
+ export declare function promptForProvider(userInteraction: BehaviorSubject<DXCUserInteraction | undefined>, providers: OAuthProviderInfo[], otpEnabled: boolean, title?: string, alerts?: DXCAlert[]): Promise<ProviderSelectionResult>;
@@ -0,0 +1,38 @@
1
+ import { OAuthError } from '../errors/OAuthError';
2
+ /** Options for initiating OAuth redirect */
3
+ export interface OAuthRedirectOptions {
4
+ /** The Dexie Cloud database URL */
5
+ databaseUrl: string;
6
+ /** The OAuth provider name */
7
+ provider: string;
8
+ /** Optional redirect URI override.
9
+ * Defaults to window.location.href for web apps.
10
+ * For Capacitor/native apps, use a custom URL scheme like 'myapp://'
11
+ */
12
+ redirectUri?: string;
13
+ }
14
+ /**
15
+ * Initiates OAuth login via full page redirect.
16
+ *
17
+ * The page will navigate to the OAuth provider. After authentication,
18
+ * the user is redirected back to the app with a `dxc-auth` query parameter
19
+ * containing base64url-encoded JSON with the authorization code.
20
+ *
21
+ * The dexie-cloud-addon automatically detects and processes this parameter
22
+ * when db.cloud.configure() is called on page load.
23
+ *
24
+ * @param options - OAuth redirect options
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Initiate OAuth login
29
+ * startOAuthRedirect({
30
+ * databaseUrl: 'https://mydb.dexie.cloud',
31
+ * provider: 'google'
32
+ * });
33
+ * // Page navigates away, user authenticates, then returns with auth code
34
+ * ```
35
+ */
36
+ export declare function startOAuthRedirect(options: OAuthRedirectOptions): void;
37
+ /** Map OAuth error strings to error codes */
38
+ export declare function mapOAuthError(error: string): OAuthError['code'];
@@ -0,0 +1,21 @@
1
+ import { h } from 'preact';
2
+ import type { OAuthProviderInfo } from 'dexie-cloud-common';
3
+ export interface AuthProviderButtonProps {
4
+ provider: OAuthProviderInfo;
5
+ onClick: () => void;
6
+ }
7
+ /**
8
+ * Button component for OAuth provider login.
9
+ * Displays the provider's icon and name following provider branding guidelines.
10
+ */
11
+ export declare function AuthProviderButton({ provider, onClick }: AuthProviderButtonProps): h.JSX.Element;
12
+ /**
13
+ * Button for email/OTP authentication option.
14
+ */
15
+ export declare function OtpButton({ onClick }: {
16
+ onClick: () => void;
17
+ }): h.JSX.Element;
18
+ /**
19
+ * Visual divider with "or" text.
20
+ */
21
+ export declare function Divider(): h.JSX.Element;
@@ -1,3 +1,31 @@
1
1
  import { h } from 'preact';
2
- import { DXCUserInteraction } from '../types/DXCUserInteraction';
3
- export declare function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }: DXCUserInteraction): h.JSX.Element;
2
+ import { DXCOption } from '../types/DXCUserInteraction';
3
+ import { DXCInputField } from '../types/DXCInputField';
4
+ import { DXCAlert } from '../types/DXCAlert';
5
+ /** Props for LoginDialog - accepts any user interaction */
6
+ interface LoginDialogProps {
7
+ title: string;
8
+ alerts: DXCAlert[];
9
+ fields: {
10
+ [name: string]: DXCInputField;
11
+ };
12
+ options?: DXCOption[];
13
+ submitLabel?: string;
14
+ cancelLabel?: string | null;
15
+ onSubmit: (params: {
16
+ [paramName: string]: string;
17
+ }) => void;
18
+ onCancel: () => void;
19
+ }
20
+ /**
21
+ * Generic dialog that can render:
22
+ * - Form fields (text inputs)
23
+ * - Selectable options (buttons)
24
+ * - Or both together
25
+ *
26
+ * When an option is clicked, calls onSubmit({ [option.name]: option.value }).
27
+ * This unified approach means the same callback handles both form submission
28
+ * and option selection.
29
+ */
30
+ export declare function LoginDialog({ title, alerts, fields, options, submitLabel, cancelLabel, onCancel, onSubmit, }: LoginDialogProps): h.JSX.Element;
31
+ export {};
@@ -0,0 +1,21 @@
1
+ import { h } from 'preact';
2
+ import { DXCOption } from '../types/DXCUserInteraction';
3
+ export interface OptionButtonProps {
4
+ option: DXCOption;
5
+ onClick: () => void;
6
+ }
7
+ /**
8
+ * Generic button component for selectable options.
9
+ * Displays the option's icon and display name.
10
+ *
11
+ * The icon can be:
12
+ * - Inline SVG (iconSvg) - rendered directly with dangerouslySetInnerHTML
13
+ * - Image URL (iconUrl) - rendered as an img tag
14
+ *
15
+ * Style is determined by the styleHint property for branding purposes.
16
+ */
17
+ export declare function OptionButton({ option, onClick }: OptionButtonProps): h.JSX.Element;
18
+ /**
19
+ * Visual divider with "or" text.
20
+ */
21
+ export declare function Divider(): h.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { h } from 'preact';
2
+ import { DXCProviderSelection } from '../types/DXCUserInteraction';
3
+ /**
4
+ * Dialog component for OAuth provider selection.
5
+ * Displays available OAuth providers as buttons and optionally an email/OTP option.
6
+ */
7
+ export declare function ProviderSelectionDialog({ title, alerts, providers, otpEnabled, cancelLabel, onSelectProvider, onSelectOtp, onCancel, }: DXCProviderSelection): h.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { h } from 'preact';
2
+ import { DXCSelect } from '../types/DXCUserInteraction';
3
+ /**
4
+ * Dialog component for generic option selection.
5
+ * Displays available options as buttons.
6
+ *
7
+ * This component is UI-agnostic and works with any DXCSelect interaction,
8
+ * whether for authentication, settings, or other selection purposes.
9
+ */
10
+ export declare function SelectDialog({ title, alerts, options, cancelLabel, onSelect, onCancel, }: DXCSelect): h.JSX.Element;