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.
- package/TODO-SOCIALAUTH.md +274 -0
- package/dist/modern/DexieCloudAPI.d.ts +4 -0
- package/dist/modern/DexieCloudOptions.d.ts +17 -0
- package/dist/modern/authentication/exchangeOAuthCode.d.ts +23 -0
- package/dist/modern/authentication/fetchAuthProviders.d.ts +14 -0
- package/dist/modern/authentication/handleOAuthCallback.d.ts +63 -0
- package/dist/modern/authentication/interactWithUser.d.ts +22 -0
- package/dist/modern/authentication/oauthLogin.d.ts +38 -0
- package/dist/modern/default-ui/AuthProviderButton.d.ts +21 -0
- package/dist/modern/default-ui/LoginDialog.d.ts +30 -2
- package/dist/modern/default-ui/OptionButton.d.ts +21 -0
- package/dist/modern/default-ui/ProviderSelectionDialog.d.ts +7 -0
- package/dist/modern/default-ui/SelectDialog.d.ts +10 -0
- package/dist/modern/dexie-cloud-addon.js +686 -13
- 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.gz +0 -0
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/errors/OAuthError.d.ts +10 -0
- package/dist/modern/service-worker.js +686 -13
- 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 +32 -1
- package/dist/umd/DISABLE_SERVICEWORKER_STRATEGY.d.ts +1 -0
- package/dist/umd/DXCWebSocketStatus.d.ts +1 -0
- package/dist/umd/DexieCloudAPI.d.ts +75 -0
- package/dist/umd/DexieCloudOptions.d.ts +27 -0
- package/dist/umd/DexieCloudSyncOptions.d.ts +4 -0
- package/dist/umd/DexieCloudTable.d.ts +18 -0
- package/dist/umd/InvalidLicenseError.d.ts +5 -0
- package/dist/umd/Invite.d.ts +8 -0
- package/dist/umd/PermissionChecker.d.ts +15 -0
- package/dist/umd/TSON.d.ts +17 -0
- package/dist/umd/WSObservable.d.ts +72 -0
- package/dist/umd/associate.d.ts +1 -0
- package/dist/umd/authentication/AuthPersistedContext.d.ts +9 -0
- package/dist/umd/authentication/TokenErrorResponseError.d.ts +10 -0
- package/dist/umd/authentication/TokenExpiredError.d.ts +3 -0
- package/dist/umd/authentication/UNAUTHORIZED_USER.d.ts +2 -0
- package/dist/umd/authentication/authenticate.d.ts +13 -0
- package/dist/umd/authentication/currentUserObservable.d.ts +1 -0
- package/dist/umd/authentication/interactWithUser.d.ts +21 -0
- package/dist/umd/authentication/login.d.ts +3 -0
- package/dist/umd/authentication/logout.d.ts +5 -0
- package/dist/umd/authentication/otpFetchTokenCallback.d.ts +3 -0
- package/dist/umd/authentication/setCurrentUser.d.ts +14 -0
- package/dist/umd/authentication/waitUntil.d.ts +3 -0
- package/dist/umd/computeSyncState.d.ts +4 -0
- package/dist/umd/createSharedValueObservable.d.ts +3 -0
- package/dist/umd/currentUserEmitter.d.ts +3 -0
- package/dist/umd/db/DexieCloudDB.d.ts +61 -0
- package/dist/umd/db/entities/BaseRevisionMapEntry.d.ts +5 -0
- package/dist/umd/db/entities/EntityCommon.d.ts +5 -0
- package/dist/umd/db/entities/GuardedJob.d.ts +5 -0
- package/dist/umd/db/entities/Member.d.ts +19 -0
- package/dist/umd/db/entities/PersistedSyncState.d.ts +22 -0
- package/dist/umd/db/entities/Realm.d.ts +14 -0
- package/dist/umd/db/entities/Role.d.ts +11 -0
- package/dist/umd/db/entities/UserLogin.d.ts +23 -0
- package/dist/umd/default-ui/Dialog.d.ts +5 -0
- package/dist/umd/default-ui/LoginDialog.d.ts +3 -0
- package/dist/umd/default-ui/Styles.d.ts +3 -0
- package/dist/umd/default-ui/index.d.ts +24 -0
- package/dist/umd/define-ydoc-trigger.d.ts +3 -0
- package/dist/umd/dexie-cloud-addon.d.ts +3 -0
- package/dist/umd/dexie-cloud-addon.js +687 -14
- package/dist/umd/dexie-cloud-addon.js.gz +0 -0
- 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.gz +0 -0
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/dexie-cloud-client.d.ts +23 -0
- package/dist/umd/errors/HttpError.d.ts +5 -0
- package/dist/umd/extend-dexie-interface.d.ts +23 -0
- package/dist/umd/getGlobalRolesObservable.d.ts +5 -0
- package/dist/umd/getInternalAccessControlObservable.d.ts +12 -0
- package/dist/umd/getInvitesObservable.d.ts +23 -0
- package/dist/umd/getPermissionsLookupObservable.d.ts +16 -0
- package/dist/umd/getTiedRealmId.d.ts +2 -0
- package/dist/umd/helpers/BroadcastedAndLocalEvent.d.ts +8 -0
- package/dist/umd/helpers/CancelToken.d.ts +4 -0
- package/dist/umd/helpers/IS_SERVICE_WORKER.d.ts +1 -0
- package/dist/umd/helpers/SWBroadcastChannel.d.ts +12 -0
- package/dist/umd/helpers/allSettled.d.ts +1 -0
- package/dist/umd/helpers/bulkUpdate.d.ts +4 -0
- package/dist/umd/helpers/computeRealmSetHash.d.ts +2 -0
- package/dist/umd/helpers/date-constants.d.ts +5 -0
- package/dist/umd/helpers/flatten.d.ts +1 -0
- package/dist/umd/helpers/getMutationTable.d.ts +1 -0
- package/dist/umd/helpers/getSyncableTables.d.ts +4 -0
- package/dist/umd/helpers/getTableFromMutationTable.d.ts +1 -0
- package/dist/umd/helpers/makeArray.d.ts +1 -0
- package/dist/umd/helpers/randomString.d.ts +1 -0
- package/dist/umd/helpers/resolveText.d.ts +16 -0
- package/dist/umd/helpers/throwVersionIncrementNeeded.d.ts +1 -0
- package/dist/umd/helpers/visibilityState.d.ts +1 -0
- package/dist/umd/isEagerSyncDisabled.d.ts +2 -0
- package/dist/umd/isFirefox.d.ts +1 -0
- package/dist/umd/isSafari.d.ts +2 -0
- package/dist/umd/mapValueObservable.d.ts +5 -0
- package/dist/umd/mergePermissions.d.ts +2 -0
- package/dist/umd/middleware-helpers/guardedTable.d.ts +11 -0
- package/dist/umd/middleware-helpers/idGenerationHelpers.d.ts +18 -0
- package/dist/umd/middlewares/createIdGenerationMiddleware.d.ts +3 -0
- package/dist/umd/middlewares/createImplicitPropSetterMiddleware.d.ts +3 -0
- package/dist/umd/middlewares/createMutationTrackingMiddleware.d.ts +17 -0
- package/dist/umd/middlewares/outstandingTransaction.d.ts +4 -0
- package/dist/umd/overrideParseStoresSpec.d.ts +4 -0
- package/dist/umd/performInitialSync.d.ts +4 -0
- package/dist/umd/permissions.d.ts +9 -0
- package/dist/umd/prodLog.d.ts +9 -0
- package/dist/umd/service-worker.d.ts +1 -0
- package/dist/umd/service-worker.js +687 -14
- 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/dist/umd/sync/DEXIE_CLOUD_SYNCER_ID.d.ts +1 -0
- package/dist/umd/sync/LocalSyncWorker.d.ts +7 -0
- package/dist/umd/sync/SyncRequiredError.d.ts +3 -0
- package/dist/umd/sync/applyServerChanges.d.ts +3 -0
- package/dist/umd/sync/connectWebSocket.d.ts +2 -0
- package/dist/umd/sync/encodeIdsForServer.d.ts +4 -0
- package/dist/umd/sync/extractRealm.d.ts +2 -0
- package/dist/umd/sync/getLatestRevisionsPerTable.d.ts +6 -0
- package/dist/umd/sync/getTablesToSyncify.d.ts +3 -0
- package/dist/umd/sync/isOnline.d.ts +1 -0
- package/dist/umd/sync/isSyncNeeded.d.ts +2 -0
- package/dist/umd/sync/listClientChanges.d.ts +9 -0
- package/dist/umd/sync/listSyncifiedChanges.d.ts +5 -0
- package/dist/umd/sync/messageConsumerIsReady.d.ts +2 -0
- package/dist/umd/sync/messagesFromServerQueue.d.ts +8 -0
- package/dist/umd/sync/modifyLocalObjectsWithNewUserId.d.ts +4 -0
- package/dist/umd/sync/myId.d.ts +1 -0
- package/dist/umd/sync/numUnsyncedMutations.d.ts +2 -0
- package/dist/umd/sync/old_startSyncingClientChanges.d.ts +39 -0
- package/dist/umd/sync/performGuardedJob.d.ts +2 -0
- package/dist/umd/sync/ratelimit.d.ts +3 -0
- package/dist/umd/sync/registerSyncEvent.d.ts +3 -0
- package/dist/umd/sync/sync.d.ts +15 -0
- package/dist/umd/sync/syncIfPossible.d.ts +5 -0
- package/dist/umd/sync/syncWithServer.d.ts +6 -0
- package/dist/umd/sync/triggerSync.d.ts +2 -0
- package/dist/umd/sync/updateBaseRevs.d.ts +5 -0
- package/dist/umd/types/DXCAlert.d.ts +25 -0
- package/dist/umd/types/DXCInputField.d.ts +11 -0
- package/dist/umd/types/DXCUserInteraction.d.ts +93 -0
- package/dist/umd/types/NewIdOptions.d.ts +3 -0
- package/dist/umd/types/SWMessageEvent.d.ts +3 -0
- package/dist/umd/types/SWSyncEvent.d.ts +4 -0
- package/dist/umd/types/SyncState.d.ts +9 -0
- package/dist/umd/types/TXExpandos.d.ts +11 -0
- package/dist/umd/updateSchemaFromOptions.d.ts +3 -0
- package/dist/umd/userIsActive.d.ts +7 -0
- package/dist/umd/verifyConfig.d.ts +2 -0
- package/dist/umd/verifySchema.d.ts +2 -0
- package/dist/umd/yjs/YDexieCloudSyncState.d.ts +3 -0
- package/dist/umd/yjs/YTable.d.ts +3 -0
- package/dist/umd/yjs/applyYMessages.d.ts +9 -0
- package/dist/umd/yjs/awareness.d.ts +3 -0
- package/dist/umd/yjs/createYClientUpdateObservable.d.ts +4 -0
- package/dist/umd/yjs/createYHandler.d.ts +2 -0
- package/dist/umd/yjs/downloadYDocsFromServer.d.ts +3 -0
- package/dist/umd/yjs/getUpdatesTable.d.ts +3 -0
- package/dist/umd/yjs/listUpdatesSince.d.ts +3 -0
- package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +26 -0
- package/dist/umd/yjs/reopenDocSignal.d.ts +10 -0
- package/dist/umd/yjs/updateYSyncStates.d.ts +6 -0
- package/oauth_flow.md +307 -0
- 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 {
|
|
3
|
-
|
|
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;
|