dexie-cloud-addon 4.4.3 → 4.4.4
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/dist/modern/DexieCloudAPI.d.ts +13 -0
- package/dist/modern/authentication/exchangeOAuthCode.d.ts +2 -0
- package/dist/modern/authentication/interactWithUser.d.ts +1 -1
- package/dist/modern/dexie-cloud-addon.js +175 -75
- 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/PolicyRejectionError.d.ts +23 -0
- package/dist/modern/extend-dexie-interface.d.ts +1 -1
- package/dist/modern/service-worker.js +175 -75
- 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/DXCAlert.d.ts +1 -1
- package/dist/umd/dexie-cloud-addon.js +176 -76
- 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 +176 -76
- 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/package.json +2 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Error codes for server-side policy rejections.
|
|
2
|
+
*
|
|
3
|
+
* These are returned by the server as structured 403 JSON responses.
|
|
4
|
+
* Use a switch statement on `code` to display translated or custom messages.
|
|
5
|
+
*/
|
|
6
|
+
export type PolicyErrorCode = 'USER_NOT_REGISTERED' | 'USER_NOT_ACCEPTED' | 'NO_SEATS_AVAILABLE' | 'USER_DEACTIVATED' | 'WEBHOOK_ERROR';
|
|
7
|
+
export interface PolicyErrorBody {
|
|
8
|
+
code: PolicyErrorCode;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
/** Thrown when the server rejects a user due to a policy rule.
|
|
12
|
+
*
|
|
13
|
+
* Unlike a generic 403, this error carries a machine-readable `code` so that
|
|
14
|
+
* the addon can convert it into a DXCUserInteraction challenge rather than
|
|
15
|
+
* simply throwing.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PolicyRejectionError extends Error {
|
|
18
|
+
readonly code: PolicyErrorCode;
|
|
19
|
+
constructor(body: PolicyErrorBody);
|
|
20
|
+
get name(): string;
|
|
21
|
+
}
|
|
22
|
+
/** Returns true when a plain fetch Response contains a structured PolicyError body. */
|
|
23
|
+
export declare function isPolicyErrorBody(value: unknown): value is PolicyErrorBody;
|
|
@@ -10,7 +10,7 @@ declare module 'dexie' {
|
|
|
10
10
|
roles: Table<DBRealmRole, [string, string], Optional<DBRealmRole, 'owner'>>;
|
|
11
11
|
}
|
|
12
12
|
interface Table {
|
|
13
|
-
newId(options
|
|
13
|
+
newId(options?: NewIdOptions): string;
|
|
14
14
|
idPrefix(): string;
|
|
15
15
|
}
|
|
16
16
|
interface DexieConstructor {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.4.
|
|
11
|
+
* Version 4.4.4, Wed Mar 25 2026
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -1845,9 +1845,10 @@ function alertUser(userInteraction, title, ...alerts) {
|
|
|
1845
1845
|
cancelLabel: null,
|
|
1846
1846
|
});
|
|
1847
1847
|
}
|
|
1848
|
-
function promptForEmail(userInteraction, title, emailHint) {
|
|
1848
|
+
function promptForEmail(userInteraction, title, emailHint, initialAlert) {
|
|
1849
1849
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1850
1850
|
let email = emailHint || '';
|
|
1851
|
+
let firstPrompt = true;
|
|
1851
1852
|
// Regular expression for email validation
|
|
1852
1853
|
// ^[\w-+.]+@([\w-]+\.)+[\w-]{2,10}(\sas\s[\w-+.]+@([\w-]+\.)+[\w-]{2,10})?$
|
|
1853
1854
|
//
|
|
@@ -1870,19 +1871,21 @@ function promptForEmail(userInteraction, title, emailHint) {
|
|
|
1870
1871
|
// and GLOBAL_WRITE permissions on the database. The email will be checked on the server before
|
|
1871
1872
|
// allowing it and giving out a token for email2, using the OTP sent to email1.
|
|
1872
1873
|
while (!email || !/^[\w-+.]+@([\w-]+\.)+[\w-]{2,10}(\sas\s[\w-+.]+@([\w-]+\.)+[\w-]{2,10})?$/.test(email)) {
|
|
1874
|
+
const alerts = [];
|
|
1875
|
+
if (firstPrompt && initialAlert)
|
|
1876
|
+
alerts.push(initialAlert);
|
|
1877
|
+
if (email)
|
|
1878
|
+
alerts.push({
|
|
1879
|
+
type: 'error',
|
|
1880
|
+
messageCode: 'INVALID_EMAIL',
|
|
1881
|
+
message: 'Please enter a valid email address',
|
|
1882
|
+
messageParams: {},
|
|
1883
|
+
});
|
|
1884
|
+
firstPrompt = false;
|
|
1873
1885
|
email = (yield interactWithUser(userInteraction, {
|
|
1874
1886
|
type: 'email',
|
|
1875
1887
|
title,
|
|
1876
|
-
alerts
|
|
1877
|
-
? [
|
|
1878
|
-
{
|
|
1879
|
-
type: 'error',
|
|
1880
|
-
messageCode: 'INVALID_EMAIL',
|
|
1881
|
-
message: 'Please enter a valid email address',
|
|
1882
|
-
messageParams: {},
|
|
1883
|
-
},
|
|
1884
|
-
]
|
|
1885
|
-
: [],
|
|
1888
|
+
alerts,
|
|
1886
1889
|
fields: {
|
|
1887
1890
|
email: {
|
|
1888
1891
|
type: 'email',
|
|
@@ -2025,6 +2028,29 @@ class OAuthRedirectError extends Error {
|
|
|
2025
2028
|
}
|
|
2026
2029
|
}
|
|
2027
2030
|
|
|
2031
|
+
/** Thrown when the server rejects a user due to a policy rule.
|
|
2032
|
+
*
|
|
2033
|
+
* Unlike a generic 403, this error carries a machine-readable `code` so that
|
|
2034
|
+
* the addon can convert it into a DXCUserInteraction challenge rather than
|
|
2035
|
+
* simply throwing.
|
|
2036
|
+
*/
|
|
2037
|
+
class PolicyRejectionError extends Error {
|
|
2038
|
+
constructor(body) {
|
|
2039
|
+
super(body.message);
|
|
2040
|
+
this.code = body.code;
|
|
2041
|
+
}
|
|
2042
|
+
get name() {
|
|
2043
|
+
return 'PolicyRejectionError';
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
/** Returns true when a plain fetch Response contains a structured PolicyError body. */
|
|
2047
|
+
function isPolicyErrorBody(value) {
|
|
2048
|
+
return (typeof value === 'object' &&
|
|
2049
|
+
value !== null &&
|
|
2050
|
+
typeof value.code === 'string' &&
|
|
2051
|
+
typeof value.message === 'string');
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2028
2054
|
const SECONDS = 1000;
|
|
2029
2055
|
const MINUTES = 60 * SECONDS;
|
|
2030
2056
|
|
|
@@ -2199,6 +2225,10 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
2199
2225
|
if (error instanceof OAuthRedirectError || (error === null || error === void 0 ? void 0 : error.name) === 'OAuthRedirectError') {
|
|
2200
2226
|
throw error; // Re-throw without logging
|
|
2201
2227
|
}
|
|
2228
|
+
// Policy rejections have already been shown to the user as a challenge
|
|
2229
|
+
if (error instanceof PolicyRejectionError || (error === null || error === void 0 ? void 0 : error.name) === 'PolicyRejectionError') {
|
|
2230
|
+
throw error;
|
|
2231
|
+
}
|
|
2202
2232
|
if (error instanceof TokenErrorResponseError) {
|
|
2203
2233
|
yield alertUser(userInteraction, error.title, {
|
|
2204
2234
|
type: 'error',
|
|
@@ -4677,13 +4707,8 @@ class OAuthError extends Error {
|
|
|
4677
4707
|
*/
|
|
4678
4708
|
function exchangeOAuthCode(options) {
|
|
4679
4709
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4680
|
-
const { databaseUrl, code, publicKey, scopes = ['ACCESS_DB'] } = options;
|
|
4681
|
-
const tokenRequest = {
|
|
4682
|
-
grant_type: 'authorization_code',
|
|
4683
|
-
code,
|
|
4684
|
-
public_key: publicKey,
|
|
4685
|
-
scopes,
|
|
4686
|
-
};
|
|
4710
|
+
const { databaseUrl, code, publicKey, scopes = ['ACCESS_DB'], intent } = options;
|
|
4711
|
+
const tokenRequest = Object.assign({ grant_type: 'authorization_code', code, public_key: publicKey, scopes }, (intent !== undefined ? { intent } : {}));
|
|
4687
4712
|
try {
|
|
4688
4713
|
const res = yield fetch(`${databaseUrl}/token`, {
|
|
4689
4714
|
method: 'POST',
|
|
@@ -4694,6 +4719,20 @@ function exchangeOAuthCode(options) {
|
|
|
4694
4719
|
if (!res.ok) {
|
|
4695
4720
|
// Read body once as text to avoid stream consumption issues
|
|
4696
4721
|
const bodyText = yield res.text().catch(() => res.statusText);
|
|
4722
|
+
// Check for structured policy rejection (403 with JSON body)
|
|
4723
|
+
if (res.status === 403) {
|
|
4724
|
+
try {
|
|
4725
|
+
const body = JSON.parse(bodyText);
|
|
4726
|
+
if (isPolicyErrorBody(body)) {
|
|
4727
|
+
throw new PolicyRejectionError(body);
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
catch (e) {
|
|
4731
|
+
if (e instanceof PolicyRejectionError)
|
|
4732
|
+
throw e;
|
|
4733
|
+
// Fall through to generic error
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4697
4736
|
if (res.status === 400 || res.status === 401) {
|
|
4698
4737
|
// Try to parse error response as JSON
|
|
4699
4738
|
try {
|
|
@@ -4829,32 +4868,59 @@ function startOAuthRedirect(options) {
|
|
|
4829
4868
|
|
|
4830
4869
|
function otpFetchTokenCallback(db) {
|
|
4831
4870
|
const { userInteraction } = db.cloud;
|
|
4832
|
-
|
|
4833
|
-
|
|
4871
|
+
/**
|
|
4872
|
+
* Core authentication function.
|
|
4873
|
+
*
|
|
4874
|
+
* @param public_key - RSA public key PEM for the session
|
|
4875
|
+
* @param hints - Optional login hints from the caller
|
|
4876
|
+
* @param policyAlert - When set, a previous attempt was rejected by a server
|
|
4877
|
+
* policy rule. The alert is injected into the first
|
|
4878
|
+
* interactive prompt so the user sees why they were
|
|
4879
|
+
* rejected without changing any other flow logic.
|
|
4880
|
+
*/
|
|
4881
|
+
function otpAuthenticate(_a, policyAlert_1) {
|
|
4882
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }, policyAlert) {
|
|
4834
4883
|
var _b, _c;
|
|
4835
4884
|
let tokenRequest;
|
|
4836
4885
|
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
4837
4886
|
if (!url)
|
|
4838
4887
|
throw new Error(`No database URL given.`);
|
|
4888
|
+
const intent = hints === null || hints === void 0 ? void 0 : hints.intent;
|
|
4889
|
+
// ── Non-interactive paths ──────────────────────────────────────────────
|
|
4890
|
+
// These paths POST directly without prompting the user. If a policyAlert
|
|
4891
|
+
// exists (from a previous rejected attempt), show it with a message-alert
|
|
4892
|
+
// before proceeding so the user understands what happened.
|
|
4839
4893
|
// Handle OAuth code exchange (from redirect/deep link flows)
|
|
4840
4894
|
if ((hints === null || hints === void 0 ? void 0 : hints.oauthCode) && hints.provider) {
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4895
|
+
try {
|
|
4896
|
+
return yield exchangeOAuthCode({
|
|
4897
|
+
databaseUrl: url,
|
|
4898
|
+
code: hints.oauthCode,
|
|
4899
|
+
publicKey: public_key,
|
|
4900
|
+
scopes: ['ACCESS_DB'],
|
|
4901
|
+
intent,
|
|
4902
|
+
});
|
|
4903
|
+
}
|
|
4904
|
+
catch (err) {
|
|
4905
|
+
if (err instanceof PolicyRejectionError) {
|
|
4906
|
+
return yield otpAuthenticate({ public_key, hints: undefined }, toPolicyAlert(err));
|
|
4907
|
+
}
|
|
4908
|
+
throw err;
|
|
4909
|
+
}
|
|
4847
4910
|
}
|
|
4848
|
-
// Handle OAuth provider login via redirect
|
|
4911
|
+
// Handle OAuth provider login via redirect (programmatic, no interaction)
|
|
4849
4912
|
if (hints === null || hints === void 0 ? void 0 : hints.provider) {
|
|
4913
|
+
if (policyAlert) {
|
|
4914
|
+
// A previous OAuth attempt was rejected. Fall through to the
|
|
4915
|
+
// interactive flow — policyAlert will be shown inside the prompt.
|
|
4916
|
+
return yield otpAuthenticate({ public_key, hints: undefined }, policyAlert);
|
|
4917
|
+
}
|
|
4850
4918
|
let resolvedRedirectUri = undefined;
|
|
4851
4919
|
if (hints.redirectPath) {
|
|
4852
|
-
// If redirectPath is absolute, use as is. If relative, resolve against current location
|
|
4853
4920
|
if (/^https?:\/\//i.test(hints.redirectPath)) {
|
|
4854
4921
|
resolvedRedirectUri = hints.redirectPath;
|
|
4855
4922
|
}
|
|
4856
4923
|
else if (typeof window !== 'undefined' && window.location) {
|
|
4857
|
-
// Use URL constructor to resolve relative path
|
|
4858
4924
|
resolvedRedirectUri = new URL(hints.redirectPath, window.location.href).toString();
|
|
4859
4925
|
}
|
|
4860
4926
|
else if (typeof location !== 'undefined' && location.href) {
|
|
@@ -4862,23 +4928,27 @@ function otpFetchTokenCallback(db) {
|
|
|
4862
4928
|
}
|
|
4863
4929
|
}
|
|
4864
4930
|
initiateOAuthRedirect(db, hints.provider, resolvedRedirectUri);
|
|
4865
|
-
// This function never returns - page navigates away
|
|
4866
4931
|
throw new OAuthRedirectError(hints.provider);
|
|
4867
4932
|
}
|
|
4933
|
+
// ── Interactive paths ──────────────────────────────────────────────────
|
|
4934
|
+
// policyAlert (if set) is injected into the first prompt so the user sees
|
|
4935
|
+
// it alongside the normal auth UI — no separate error screen needed.
|
|
4868
4936
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
4869
|
-
const demo_user = yield promptForEmail(userInteraction, 'Enter a demo user email', (hints === null || hints === void 0 ? void 0 : hints.email) || (hints === null || hints === void 0 ? void 0 : hints.userId));
|
|
4937
|
+
const demo_user = yield promptForEmail(userInteraction, 'Enter a demo user email', (hints === null || hints === void 0 ? void 0 : hints.email) || (hints === null || hints === void 0 ? void 0 : hints.userId), policyAlert);
|
|
4870
4938
|
tokenRequest = {
|
|
4871
4939
|
demo_user,
|
|
4872
4940
|
grant_type: 'demo',
|
|
4873
4941
|
scopes: ['ACCESS_DB'],
|
|
4874
|
-
public_key
|
|
4942
|
+
public_key,
|
|
4875
4943
|
};
|
|
4876
4944
|
}
|
|
4877
4945
|
else if ((hints === null || hints === void 0 ? void 0 : hints.otpId) && hints.otp) {
|
|
4878
|
-
//
|
|
4879
|
-
//
|
|
4880
|
-
|
|
4881
|
-
|
|
4946
|
+
// Magic-link flow: OTP already supplied by the caller (e.g. from email).
|
|
4947
|
+
// No interaction — show alert as a plain message if there is one.
|
|
4948
|
+
if (policyAlert) {
|
|
4949
|
+
yield alertUser(userInteraction, 'Access Denied', policyAlert);
|
|
4950
|
+
return yield otpAuthenticate({ public_key, hints: undefined }, policyAlert);
|
|
4951
|
+
}
|
|
4882
4952
|
tokenRequest = {
|
|
4883
4953
|
grant_type: 'otp',
|
|
4884
4954
|
otp_id: hints.otpId,
|
|
@@ -4888,56 +4958,52 @@ function otpFetchTokenCallback(db) {
|
|
|
4888
4958
|
};
|
|
4889
4959
|
}
|
|
4890
4960
|
else if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'otp' || (hints === null || hints === void 0 ? void 0 : hints.email)) {
|
|
4891
|
-
//
|
|
4892
|
-
const email = (hints === null || hints === void 0 ? void 0 : hints.email) ||
|
|
4961
|
+
// Caller explicitly requested OTP — skip provider selection.
|
|
4962
|
+
const email = (hints === null || hints === void 0 ? void 0 : hints.email) ||
|
|
4963
|
+
(yield promptForEmail(userInteraction, 'Enter email address', undefined, policyAlert));
|
|
4893
4964
|
if (/@demo.local$/.test(email)) {
|
|
4894
4965
|
tokenRequest = {
|
|
4895
4966
|
demo_user: email,
|
|
4896
4967
|
grant_type: 'demo',
|
|
4897
4968
|
scopes: ['ACCESS_DB'],
|
|
4898
|
-
public_key
|
|
4969
|
+
public_key,
|
|
4899
4970
|
};
|
|
4900
4971
|
}
|
|
4901
4972
|
else {
|
|
4902
|
-
tokenRequest = {
|
|
4903
|
-
email,
|
|
4904
|
-
grant_type: 'otp',
|
|
4905
|
-
scopes: ['ACCESS_DB'],
|
|
4906
|
-
};
|
|
4973
|
+
tokenRequest = Object.assign({ email, grant_type: 'otp', scopes: ['ACCESS_DB'] }, (intent !== undefined ? { intent } : {}));
|
|
4907
4974
|
}
|
|
4908
4975
|
}
|
|
4909
4976
|
else {
|
|
4910
|
-
//
|
|
4977
|
+
// Default path: check for OAuth providers, then fall back to OTP.
|
|
4911
4978
|
const socialAuthEnabled = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.socialAuth) !== false;
|
|
4912
4979
|
const authProviders = yield fetchAuthProviders(url, socialAuthEnabled);
|
|
4913
|
-
// If we have OAuth providers available, prompt for selection
|
|
4914
4980
|
if (authProviders.providers.length > 0) {
|
|
4915
|
-
const
|
|
4981
|
+
const providerAlerts = policyAlert ? [policyAlert] : [];
|
|
4982
|
+
const selection = yield promptForProvider(userInteraction, authProviders.providers, authProviders.otpEnabled, 'Sign in', providerAlerts);
|
|
4916
4983
|
if (selection.type === 'provider') {
|
|
4917
|
-
// User selected an OAuth provider - initiate redirect
|
|
4918
4984
|
initiateOAuthRedirect(db, selection.provider);
|
|
4919
|
-
// This function never returns - page navigates away
|
|
4920
4985
|
throw new OAuthRedirectError(selection.provider);
|
|
4921
4986
|
}
|
|
4922
|
-
// User chose OTP
|
|
4987
|
+
// User chose OTP — fall through to email prompt (no policyAlert here;
|
|
4988
|
+
// it was already shown in the provider prompt above).
|
|
4923
4989
|
}
|
|
4924
|
-
const email = yield promptForEmail(userInteraction, 'Enter email address', hints === null || hints === void 0 ? void 0 : hints.email
|
|
4990
|
+
const email = yield promptForEmail(userInteraction, 'Enter email address', hints === null || hints === void 0 ? void 0 : hints.email,
|
|
4991
|
+
// Show policyAlert in email prompt only if there were no providers
|
|
4992
|
+
// (otherwise it was already shown in the provider selection above).
|
|
4993
|
+
authProviders.providers.length === 0 ? policyAlert : undefined);
|
|
4925
4994
|
if (/@demo.local$/.test(email)) {
|
|
4926
4995
|
tokenRequest = {
|
|
4927
4996
|
demo_user: email,
|
|
4928
4997
|
grant_type: 'demo',
|
|
4929
4998
|
scopes: ['ACCESS_DB'],
|
|
4930
|
-
public_key
|
|
4999
|
+
public_key,
|
|
4931
5000
|
};
|
|
4932
5001
|
}
|
|
4933
5002
|
else {
|
|
4934
|
-
tokenRequest = {
|
|
4935
|
-
email,
|
|
4936
|
-
grant_type: 'otp',
|
|
4937
|
-
scopes: ['ACCESS_DB'],
|
|
4938
|
-
};
|
|
5003
|
+
tokenRequest = Object.assign({ email, grant_type: 'otp', scopes: ['ACCESS_DB'] }, (intent !== undefined ? { intent } : {}));
|
|
4939
5004
|
}
|
|
4940
5005
|
}
|
|
5006
|
+
// ── POST /token (step 1) ───────────────────────────────────────────────
|
|
4941
5007
|
const res1 = yield fetch(`${url}/token`, {
|
|
4942
5008
|
body: JSON.stringify(tokenRequest),
|
|
4943
5009
|
method: 'post',
|
|
@@ -4945,19 +5011,22 @@ function otpFetchTokenCallback(db) {
|
|
|
4945
5011
|
mode: 'cors',
|
|
4946
5012
|
});
|
|
4947
5013
|
if (res1.status !== 200) {
|
|
5014
|
+
const alert = yield tryParsePolicyAlert(res1);
|
|
5015
|
+
if (alert) {
|
|
5016
|
+
// Policy rejection — restart the flow with the error injected.
|
|
5017
|
+
return yield otpAuthenticate({ public_key, hints: undefined }, alert);
|
|
5018
|
+
}
|
|
4948
5019
|
const errMsg = yield res1.text();
|
|
4949
|
-
yield alertUser(userInteraction,
|
|
5020
|
+
yield alertUser(userInteraction, 'Token request failed', {
|
|
4950
5021
|
type: 'error',
|
|
4951
5022
|
messageCode: 'GENERIC_ERROR',
|
|
4952
5023
|
message: errMsg,
|
|
4953
|
-
messageParams: {}
|
|
5024
|
+
messageParams: {},
|
|
4954
5025
|
}).catch(() => { });
|
|
4955
5026
|
throw new HttpError(res1, errMsg);
|
|
4956
5027
|
}
|
|
4957
5028
|
const response = yield res1.json();
|
|
4958
5029
|
if (response.type === 'tokens' || response.type === 'error') {
|
|
4959
|
-
// Demo user request can get a "tokens" response right away
|
|
4960
|
-
// Error can also be returned right away.
|
|
4961
5030
|
return response;
|
|
4962
5031
|
}
|
|
4963
5032
|
else if (tokenRequest.grant_type === 'otp' && 'email' in tokenRequest) {
|
|
@@ -4965,6 +5034,7 @@ function otpFetchTokenCallback(db) {
|
|
|
4965
5034
|
throw new Error(`Unexpected response from ${url}/token`);
|
|
4966
5035
|
const otp = yield promptForOTP(userInteraction, tokenRequest.email);
|
|
4967
5036
|
const tokenRequest2 = Object.assign(Object.assign({}, tokenRequest), { otp: otp || '', otp_id: response.otp_id, public_key });
|
|
5037
|
+
// ── POST /token (step 2: OTP verification) ─────────────────────────
|
|
4968
5038
|
let res2 = yield fetch(`${url}/token`, {
|
|
4969
5039
|
body: JSON.stringify(tokenRequest2),
|
|
4970
5040
|
method: 'post',
|
|
@@ -4977,7 +5047,7 @@ function otpFetchTokenCallback(db) {
|
|
|
4977
5047
|
type: 'error',
|
|
4978
5048
|
messageCode: 'INVALID_OTP',
|
|
4979
5049
|
message: errorText,
|
|
4980
|
-
messageParams: {}
|
|
5050
|
+
messageParams: {},
|
|
4981
5051
|
});
|
|
4982
5052
|
res2 = yield fetch(`${url}/token`, {
|
|
4983
5053
|
body: JSON.stringify(tokenRequest2),
|
|
@@ -4987,6 +5057,10 @@ function otpFetchTokenCallback(db) {
|
|
|
4987
5057
|
});
|
|
4988
5058
|
}
|
|
4989
5059
|
if (res2.status !== 200) {
|
|
5060
|
+
const alert = yield tryParsePolicyAlert(res2);
|
|
5061
|
+
if (alert) {
|
|
5062
|
+
return yield otpAuthenticate({ public_key, hints: undefined }, alert);
|
|
5063
|
+
}
|
|
4990
5064
|
const errMsg = yield res2.text();
|
|
4991
5065
|
throw new HttpError(res2, errMsg);
|
|
4992
5066
|
}
|
|
@@ -4997,14 +5071,11 @@ function otpFetchTokenCallback(db) {
|
|
|
4997
5071
|
throw new Error(`Unexpected response from ${url}/token`);
|
|
4998
5072
|
}
|
|
4999
5073
|
});
|
|
5000
|
-
}
|
|
5074
|
+
}
|
|
5075
|
+
return ({ public_key, hints }) => otpAuthenticate({ public_key, hints });
|
|
5001
5076
|
}
|
|
5002
5077
|
/**
|
|
5003
5078
|
* Initiates OAuth login via full page redirect.
|
|
5004
|
-
*
|
|
5005
|
-
* The page will navigate away to the OAuth provider. After authentication,
|
|
5006
|
-
* the user is redirected back with a dxc-auth query parameter that is
|
|
5007
|
-
* automatically detected by db.cloud.configure().
|
|
5008
5079
|
*/
|
|
5009
5080
|
function initiateOAuthRedirect(db, provider, redirectUriOverride) {
|
|
5010
5081
|
var _a, _b;
|
|
@@ -5014,17 +5085,44 @@ function initiateOAuthRedirect(db, provider, redirectUriOverride) {
|
|
|
5014
5085
|
const redirectUri = redirectUriOverride ||
|
|
5015
5086
|
((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
|
|
5016
5087
|
(typeof location !== 'undefined' ? location.href : undefined);
|
|
5017
|
-
// CodeRabbit suggested to fail fast here, but the only situation where
|
|
5018
|
-
// redirectUri would be undefined is in non-browser environments, and
|
|
5019
|
-
// in those environments OAuth redirect does not make sense anyway
|
|
5020
|
-
// and will fail fast in startOAuthRedirect().
|
|
5021
|
-
// Start OAuth redirect flow - page navigates away
|
|
5022
5088
|
startOAuthRedirect({
|
|
5023
5089
|
databaseUrl: url,
|
|
5024
5090
|
provider,
|
|
5025
5091
|
redirectUri,
|
|
5026
5092
|
});
|
|
5027
5093
|
}
|
|
5094
|
+
/**
|
|
5095
|
+
* Converts a PolicyRejectionError to a DXCAlert for injection into prompts.
|
|
5096
|
+
*/
|
|
5097
|
+
function toPolicyAlert(err) {
|
|
5098
|
+
return {
|
|
5099
|
+
type: 'error',
|
|
5100
|
+
messageCode: err.code,
|
|
5101
|
+
message: err.message,
|
|
5102
|
+
messageParams: {},
|
|
5103
|
+
};
|
|
5104
|
+
}
|
|
5105
|
+
/**
|
|
5106
|
+
* Tries to parse a failed Response as a structured PolicyError body.
|
|
5107
|
+
* Returns a DXCAlert if it is one, otherwise returns null.
|
|
5108
|
+
* Safe to call: reads body via clone() so the original Response is untouched.
|
|
5109
|
+
*/
|
|
5110
|
+
function tryParsePolicyAlert(res) {
|
|
5111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
5112
|
+
if (res.status !== 403)
|
|
5113
|
+
return null;
|
|
5114
|
+
try {
|
|
5115
|
+
const body = yield res.clone().json();
|
|
5116
|
+
if (isPolicyErrorBody(body)) {
|
|
5117
|
+
return toPolicyAlert(new PolicyRejectionError(body));
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
catch (_a) {
|
|
5121
|
+
// Not JSON
|
|
5122
|
+
}
|
|
5123
|
+
return null;
|
|
5124
|
+
});
|
|
5125
|
+
}
|
|
5028
5126
|
|
|
5029
5127
|
/** A way to log to console in production without terser stripping out
|
|
5030
5128
|
* it from the release bundle.
|
|
@@ -6038,6 +6136,7 @@ function createBlobResolvingCursor(cursor, table, blobSavingQueue, db) {
|
|
|
6038
6136
|
return cursor.start(() => {
|
|
6039
6137
|
const rawValue = cursor.value;
|
|
6040
6138
|
if (!rawValue || !hasUnresolvedBlobRefs(rawValue)) {
|
|
6139
|
+
wrappedCursor.value = rawValue;
|
|
6041
6140
|
onNext();
|
|
6042
6141
|
return;
|
|
6043
6142
|
}
|
|
@@ -6046,6 +6145,7 @@ function createBlobResolvingCursor(cursor, table, blobSavingQueue, db) {
|
|
|
6046
6145
|
onNext();
|
|
6047
6146
|
}, err => {
|
|
6048
6147
|
console.error('Failed to resolve BlobRefs for cursor value:', err);
|
|
6148
|
+
wrappedCursor.value = rawValue;
|
|
6049
6149
|
onNext();
|
|
6050
6150
|
});
|
|
6051
6151
|
});
|
|
@@ -8102,7 +8202,7 @@ function dexieCloud(dexie) {
|
|
|
8102
8202
|
const downloading$ = createDownloadingState();
|
|
8103
8203
|
dexie.cloud = {
|
|
8104
8204
|
// @ts-ignore
|
|
8105
|
-
version: "4.4.
|
|
8205
|
+
version: "4.4.4",
|
|
8106
8206
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
8107
8207
|
schema: null,
|
|
8108
8208
|
get currentUserId() {
|
|
@@ -8529,7 +8629,7 @@ function dexieCloud(dexie) {
|
|
|
8529
8629
|
}
|
|
8530
8630
|
}
|
|
8531
8631
|
// @ts-ignore
|
|
8532
|
-
dexieCloud.version = "4.4.
|
|
8632
|
+
dexieCloud.version = "4.4.4";
|
|
8533
8633
|
Dexie.Cloud = dexieCloud;
|
|
8534
8634
|
|
|
8535
8635
|
// In case the SW lives for a while, let it reuse already opened connections:
|