@redacto.io/consent-sdk-react 10.1.0-beta.0 → 10.1.0-beta.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/dist/index.js CHANGED
@@ -155,7 +155,7 @@ var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
155
155
 
156
156
  // src/RedactoNoticeConsent/api/sandbox.ts
157
157
  var resolveSandboxAuth = (params) => {
158
- var _a, _b, _c, _d, _e;
158
+ var _a, _b, _c, _d, _e, _f;
159
159
  const token = (_a = params.token) == null ? void 0 : _a.trim();
160
160
  if (!token) {
161
161
  return null;
@@ -163,16 +163,17 @@ var resolveSandboxAuth = (params) => {
163
163
  const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
164
164
  const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
165
165
  const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
166
- const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
166
+ const email = (_e = params.sandboxEmail) == null ? void 0 : _e.trim();
167
+ const mobile = (_f = params.sandboxMobile) == null ? void 0 : _f.trim();
167
168
  if (!organisationUuid || !workspaceUuid) {
168
169
  throw new Error(
169
170
  "Sandbox mode requires organisationUuid and workspaceUuid props"
170
171
  );
171
172
  }
172
- if (!ucic && !subject) {
173
+ if (!ucic && !email && !mobile) {
173
174
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
174
175
  }
175
- return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
176
+ return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
176
177
  };
177
178
  var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
178
179
  const sandbox = resolveSandboxAuth(sandboxParams);
@@ -204,10 +205,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
204
205
  return { Authorization: `Bearer ${accessToken}` };
205
206
  };
206
207
  var sandboxBodyIdentity = (sandbox) => {
208
+ const payload = {};
207
209
  if (sandbox.ucic) {
208
- return { org_user_id: sandbox.ucic };
210
+ payload.org_user_id = sandbox.ucic;
211
+ }
212
+ if (sandbox.email) {
213
+ payload.primary_email = sandbox.email;
214
+ }
215
+ if (sandbox.mobile) {
216
+ payload.primary_mobile = sandbox.mobile;
209
217
  }
210
- return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
218
+ return payload;
211
219
  };
212
220
  var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
213
221
 
@@ -335,7 +343,7 @@ var fetchConsentContent = async ({
335
343
  }
336
344
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
337
345
  const apiBaseUrl = baseUrl || BASE_URL;
338
- const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
346
+ const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
339
347
  const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
340
348
  if (validate_against === "all" && !specific_uuid) {
341
349
  const cachedData = getCachedData(cacheKey);
@@ -355,7 +363,9 @@ var fetchConsentContent = async ({
355
363
  }
356
364
  if (sandbox) {
357
365
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
358
- url.searchParams.append(key, value);
366
+ if (value) {
367
+ url.searchParams.append(key, value);
368
+ }
359
369
  }
360
370
  }
361
371
  const response = await fetch(url.toString(), {
@@ -399,7 +409,7 @@ var submitConsentEvent = async ({
399
409
  throw new Error("accessToken or token is required");
400
410
  }
401
411
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
402
- const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
412
+ const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
403
413
  const validatedPurposes = purposes.map((purpose) => {
404
414
  if (!purpose.uuid) {
405
415
  throw new Error(`Invalid purpose data: missing uuid`);
@@ -465,7 +475,9 @@ var fetchTTSAudioUrls = async ({
465
475
  );
466
476
  if (sandbox) {
467
477
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
468
- url.searchParams.append(key, value);
478
+ if (value) {
479
+ url.searchParams.append(key, value);
480
+ }
469
481
  }
470
482
  }
471
483
  const response = await fetch(url.toString(), {
@@ -3082,7 +3094,8 @@ var RedactoNoticeConsent = ({
3082
3094
  () => ({
3083
3095
  token,
3084
3096
  sandboxUcic: ucic,
3085
- sandboxSubject: email || mobile,
3097
+ sandboxEmail: email,
3098
+ sandboxMobile: mobile,
3086
3099
  organisationUuid,
3087
3100
  workspaceUuid
3088
3101
  }),
package/dist/index.mjs CHANGED
@@ -82,7 +82,7 @@ async function parseApiError(response, fallbackMessage) {
82
82
 
83
83
  // src/RedactoNoticeConsent/api/sandbox.ts
84
84
  var resolveSandboxAuth = (params) => {
85
- var _a, _b, _c, _d, _e;
85
+ var _a, _b, _c, _d, _e, _f;
86
86
  const token = (_a = params.token) == null ? void 0 : _a.trim();
87
87
  if (!token) {
88
88
  return null;
@@ -90,16 +90,17 @@ var resolveSandboxAuth = (params) => {
90
90
  const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
91
91
  const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
92
92
  const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
93
- const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
93
+ const email = (_e = params.sandboxEmail) == null ? void 0 : _e.trim();
94
+ const mobile = (_f = params.sandboxMobile) == null ? void 0 : _f.trim();
94
95
  if (!organisationUuid || !workspaceUuid) {
95
96
  throw new Error(
96
97
  "Sandbox mode requires organisationUuid and workspaceUuid props"
97
98
  );
98
99
  }
99
- if (!ucic && !subject) {
100
+ if (!ucic && !email && !mobile) {
100
101
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
101
102
  }
102
- return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
103
+ return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
103
104
  };
104
105
  var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
105
106
  const sandbox = resolveSandboxAuth(sandboxParams);
@@ -131,10 +132,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
131
132
  return { Authorization: `Bearer ${accessToken}` };
132
133
  };
133
134
  var sandboxBodyIdentity = (sandbox) => {
135
+ const payload = {};
134
136
  if (sandbox.ucic) {
135
- return { org_user_id: sandbox.ucic };
137
+ payload.org_user_id = sandbox.ucic;
138
+ }
139
+ if (sandbox.email) {
140
+ payload.primary_email = sandbox.email;
141
+ }
142
+ if (sandbox.mobile) {
143
+ payload.primary_mobile = sandbox.mobile;
136
144
  }
137
- return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
145
+ return payload;
138
146
  };
139
147
  var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
140
148
 
@@ -262,7 +270,7 @@ var fetchConsentContent = async ({
262
270
  }
263
271
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
264
272
  const apiBaseUrl = baseUrl || BASE_URL;
265
- const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
273
+ const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
266
274
  const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
267
275
  if (validate_against === "all" && !specific_uuid) {
268
276
  const cachedData = getCachedData(cacheKey);
@@ -282,7 +290,9 @@ var fetchConsentContent = async ({
282
290
  }
283
291
  if (sandbox) {
284
292
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
285
- url.searchParams.append(key, value);
293
+ if (value) {
294
+ url.searchParams.append(key, value);
295
+ }
286
296
  }
287
297
  }
288
298
  const response = await fetch(url.toString(), {
@@ -326,7 +336,7 @@ var submitConsentEvent = async ({
326
336
  throw new Error("accessToken or token is required");
327
337
  }
328
338
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
329
- const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
339
+ const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
330
340
  const validatedPurposes = purposes.map((purpose) => {
331
341
  if (!purpose.uuid) {
332
342
  throw new Error(`Invalid purpose data: missing uuid`);
@@ -392,7 +402,9 @@ var fetchTTSAudioUrls = async ({
392
402
  );
393
403
  if (sandbox) {
394
404
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
395
- url.searchParams.append(key, value);
405
+ if (value) {
406
+ url.searchParams.append(key, value);
407
+ }
396
408
  }
397
409
  }
398
410
  const response = await fetch(url.toString(), {
@@ -2808,7 +2820,8 @@ var RedactoNoticeConsent = ({
2808
2820
  () => ({
2809
2821
  token,
2810
2822
  sandboxUcic: ucic,
2811
- sandboxSubject: email || mobile,
2823
+ sandboxEmail: email,
2824
+ sandboxMobile: mobile,
2812
2825
  organisationUuid,
2813
2826
  workspaceUuid
2814
2827
  }),
@@ -57,8 +57,15 @@ declare function createConsentClient(baseUrl: string, ledgerBaseUrl?: string): {
57
57
  createOtp(organisationId: string, workspaceId: string, email: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
58
58
  /**
59
59
  * Verify OTP (public)
60
+ *
61
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
62
+ * `auth`-scoped token (and to resolve shared-contact principals via the
63
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
64
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
65
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
66
+ * then rejects it with 403 "Token scope is not permitted".
60
67
  */
61
- verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
68
+ verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">, purpose?: "login" | "consent"): Promise<unknown>;
62
69
  /**
63
70
  * Exchange one-time token for JWT tokens (public)
64
71
  */
@@ -472,6 +479,8 @@ type ManageConsentParams = {
472
479
  dataElementUuids?: string[];
473
480
  };
474
481
  type Activity = {
482
+ /** Stable per-activity id (source consent-event / case uuid) for keying and de-duplication. */
483
+ id?: string;
475
484
  activity_type: string;
476
485
  activity_type_display?: string;
477
486
  timestamp: string;
@@ -722,10 +731,12 @@ type AuthProviderProps = Readonly<{
722
731
  * payload, and never calls `/otp/refresh`.
723
732
  */
724
733
  token?: string;
725
- /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
734
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence for the principal. */
726
735
  sandboxUcic?: string;
727
- /** Sandbox subject (email or mobile) — carried in the request payload. */
728
- sandboxSubject?: string;
736
+ /** Sandbox email — carried on every request, like a live JWT's `user_data.primary_email`. */
737
+ sandboxEmail?: string;
738
+ /** Sandbox mobile — carried on every request, like a live JWT's `user_data.primary_mobile`. */
739
+ sandboxMobile?: string;
729
740
  /** Sandbox org UUID (replaces the JWT-decoded org). */
730
741
  organisationUuid?: string;
731
742
  /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
@@ -57,8 +57,15 @@ declare function createConsentClient(baseUrl: string, ledgerBaseUrl?: string): {
57
57
  createOtp(organisationId: string, workspaceId: string, email: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
58
58
  /**
59
59
  * Verify OTP (public)
60
+ *
61
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
62
+ * `auth`-scoped token (and to resolve shared-contact principals via the
63
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
64
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
65
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
66
+ * then rejects it with 403 "Token scope is not permitted".
60
67
  */
61
- verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
68
+ verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">, purpose?: "login" | "consent"): Promise<unknown>;
62
69
  /**
63
70
  * Exchange one-time token for JWT tokens (public)
64
71
  */
@@ -472,6 +479,8 @@ type ManageConsentParams = {
472
479
  dataElementUuids?: string[];
473
480
  };
474
481
  type Activity = {
482
+ /** Stable per-activity id (source consent-event / case uuid) for keying and de-duplication. */
483
+ id?: string;
475
484
  activity_type: string;
476
485
  activity_type_display?: string;
477
486
  timestamp: string;
@@ -722,10 +731,12 @@ type AuthProviderProps = Readonly<{
722
731
  * payload, and never calls `/otp/refresh`.
723
732
  */
724
733
  token?: string;
725
- /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
734
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence for the principal. */
726
735
  sandboxUcic?: string;
727
- /** Sandbox subject (email or mobile) — carried in the request payload. */
728
- sandboxSubject?: string;
736
+ /** Sandbox email — carried on every request, like a live JWT's `user_data.primary_email`. */
737
+ sandboxEmail?: string;
738
+ /** Sandbox mobile — carried on every request, like a live JWT's `user_data.primary_mobile`. */
739
+ sandboxMobile?: string;
729
740
  /** Sandbox org UUID (replaces the JWT-decoded org). */
730
741
  organisationUuid?: string;
731
742
  /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
@@ -8140,13 +8140,20 @@ var sandboxBodyIdentity = () => {
8140
8140
  if (!sandboxConfig) {
8141
8141
  return null;
8142
8142
  }
8143
+ const payload = {};
8143
8144
  if (sandboxConfig.ucic) {
8144
- return { org_user_id: sandboxConfig.ucic };
8145
+ payload.org_user_id = sandboxConfig.ucic;
8145
8146
  }
8146
- return sandboxConfig.subject.includes("@") ? { primary_email: sandboxConfig.subject } : { primary_mobile: sandboxConfig.subject };
8147
+ if (sandboxConfig.email) {
8148
+ payload.primary_email = sandboxConfig.email;
8149
+ }
8150
+ if (sandboxConfig.mobile) {
8151
+ payload.primary_mobile = sandboxConfig.mobile;
8152
+ }
8153
+ return payload;
8147
8154
  };
8148
8155
  var sandboxQueryIdentity = () => sandboxBodyIdentity();
8149
- var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.subject : null;
8156
+ var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.email || sandboxConfig.mobile || null : null;
8150
8157
 
8151
8158
  // src/RedactoPrivacyCenter/api/fetcher.ts
8152
8159
  var ApiError = class _ApiError extends Error {
@@ -9871,7 +9878,7 @@ var seedSessionStorage = (data) => {
9871
9878
  if (data.contact) sessionStorage.setItem("email", data.contact);
9872
9879
  };
9873
9880
  var configureSandbox = (props) => {
9874
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
9881
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
9875
9882
  const token = (_a = props.token) == null ? void 0 : _a.trim();
9876
9883
  if (!token) {
9877
9884
  setSandboxConfig(null);
@@ -9880,12 +9887,14 @@ var configureSandbox = (props) => {
9880
9887
  const organisationUuid = (_c = (_b = props.organisationUuid) == null ? void 0 : _b.trim()) != null ? _c : "";
9881
9888
  const workspaceUuid = (_e = (_d = props.workspaceUuid) == null ? void 0 : _d.trim()) != null ? _e : "";
9882
9889
  const ucic = (_g = (_f = props.sandboxUcic) == null ? void 0 : _f.trim()) != null ? _g : "";
9883
- const subject = (_i = (_h = props.sandboxSubject) == null ? void 0 : _h.trim()) != null ? _i : "";
9884
- const slug = (_k = (_j = props.slug) == null ? void 0 : _j.trim()) != null ? _k : "";
9890
+ const email = (_i = (_h = props.sandboxEmail) == null ? void 0 : _h.trim()) != null ? _i : "";
9891
+ const mobile = (_k = (_j = props.sandboxMobile) == null ? void 0 : _j.trim()) != null ? _k : "";
9892
+ const slug = (_m = (_l = props.slug) == null ? void 0 : _l.trim()) != null ? _m : "";
9885
9893
  setSandboxConfig({
9886
9894
  token,
9887
9895
  ucic: ucic || void 0,
9888
- subject,
9896
+ email: email || void 0,
9897
+ mobile: mobile || void 0,
9889
9898
  organisationUuid,
9890
9899
  workspaceUuid,
9891
9900
  slug
@@ -9895,7 +9904,8 @@ var configureSandbox = (props) => {
9895
9904
  orgId: organisationUuid,
9896
9905
  workspaceId: workspaceUuid,
9897
9906
  slug,
9898
- contact: ucic || subject
9907
+ // The principal precedence (UCIC > email > mobile) the server also applies.
9908
+ contact: ucic || email || mobile
9899
9909
  });
9900
9910
  return true;
9901
9911
  };
@@ -9920,7 +9930,8 @@ var AuthProvider = (props) => {
9920
9930
  refreshToken: initialRefreshToken,
9921
9931
  token: sandboxToken,
9922
9932
  sandboxUcic,
9923
- sandboxSubject,
9933
+ sandboxEmail,
9934
+ sandboxMobile,
9924
9935
  organisationUuid,
9925
9936
  workspaceUuid,
9926
9937
  slug,
@@ -9986,7 +9997,7 @@ var AuthProvider = (props) => {
9986
9997
  }
9987
9998
  configureSandbox(props);
9988
9999
  return () => setSandboxConfig(null);
9989
- }, [sandboxActive, sandboxToken, sandboxUcic, sandboxSubject, organisationUuid, workspaceUuid, slug]);
10000
+ }, [sandboxActive, sandboxToken, sandboxUcic, sandboxEmail, sandboxMobile, organisationUuid, workspaceUuid, slug]);
9990
10001
  const updateTokens = (0, import_react3.useCallback)((newAccessToken, newRefreshToken) => {
9991
10002
  setAccessToken(newAccessToken);
9992
10003
  if (newRefreshToken) {
@@ -10024,7 +10035,7 @@ var import_react4 = require("react");
10024
10035
  // src/RedactoPrivacyCenter/api/client.ts
10025
10036
  function createConsentClient(baseUrl, ledgerBaseUrl) {
10026
10037
  const ledgerConfigured = Boolean(ledgerBaseUrl);
10027
- const useLedger = () => ledgerConfigured && !isSandboxMode();
10038
+ const useLedger = () => ledgerConfigured;
10028
10039
  const ledgerBase = (ledgerBaseUrl != null ? ledgerBaseUrl : "").replace(/\/$/, "");
10029
10040
  async function privacyCenterApiFetch(url, options) {
10030
10041
  const { authToken, getAuthToken, headers = {}, ...fetchOptions } = options;
@@ -10149,7 +10160,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10149
10160
  const identity = sandboxQueryIdentity();
10150
10161
  if (identity) {
10151
10162
  Object.entries(identity).forEach(([key, value]) => {
10152
- queryParams.append(key, value);
10163
+ if (value) {
10164
+ queryParams.append(key, value);
10165
+ }
10153
10166
  });
10154
10167
  }
10155
10168
  }
@@ -10220,13 +10233,20 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10220
10233
  },
10221
10234
  /**
10222
10235
  * Verify OTP (public)
10236
+ *
10237
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
10238
+ * `auth`-scoped token (and to resolve shared-contact principals via the
10239
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
10240
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
10241
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
10242
+ * then rejects it with 403 "Token scope is not permitted".
10223
10243
  */
10224
- async verifyOtp(organisationId, workspaceId, otpId, otp, options) {
10244
+ async verifyOtp(organisationId, workspaceId, otpId, otp, options, purpose = "login") {
10225
10245
  const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
10226
10246
  return apiFetch(url, {
10227
10247
  ...options,
10228
10248
  method: "POST",
10229
- body: JSON.stringify({ uuid: otpId, otp })
10249
+ body: JSON.stringify({ uuid: otpId, otp, purpose })
10230
10250
  });
10231
10251
  },
10232
10252
  /**
@@ -10334,6 +10354,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10334
10354
  * used with its `{ purpose_uuid, contact, ... }` payload.
10335
10355
  */
10336
10356
  async manageConsent(baseRequest, purposeId, contact, status, authToken, options, nominatorContact, dataElementUuids, productUuid, noticeUuid, dataElements, language) {
10357
+ var _a;
10337
10358
  if (!useLedger()) {
10338
10359
  const url2 = privacyCenterUrl(baseRequest, `/manage-consent?status=${status}`);
10339
10360
  const body2 = { purpose_uuid: purposeId, contact };
@@ -10373,6 +10394,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10373
10394
  });
10374
10395
  const body = {
10375
10396
  notice_uuid: noticeUuid,
10397
+ // Sandbox has no JWT, so the ledger resolves the acting test principal
10398
+ // from this identity in the body; null (spread no-op) on the live JWT path.
10399
+ ...(_a = sandboxBodyIdentity()) != null ? _a : {},
10376
10400
  partial: true,
10377
10401
  declined: false,
10378
10402
  select_all_mandatory: false,
@@ -10450,6 +10474,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10450
10474
  if (params.created_after) queryParams.append("created_after", params.created_after);
10451
10475
  if (params.created_before) queryParams.append("created_before", params.created_before);
10452
10476
  if (params.language) queryParams.append("language", params.language);
10477
+ appendSandboxIdentity(queryParams);
10453
10478
  const queryString = queryParams.toString();
10454
10479
  const receiptsSuffix = `/receipts${queryString ? `?${queryString}` : ""}`;
10455
10480
  const url = useLedger() ? ledgerUrl(baseRequest, receiptsSuffix) : privacyCenterUrl(baseRequest, receiptsSuffix);
@@ -10467,7 +10492,13 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10467
10492
  * and the Authorization header must be sent (anchor navigation wouldn't carry it).
10468
10493
  */
10469
10494
  async getReceiptPdf(baseRequest, receiptUuid, authToken) {
10470
- const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : privacyCenterUrl(baseRequest, `/receipts/${receiptUuid}/pdf`);
10495
+ const url = useLedger() ? ledgerUrl(
10496
+ baseRequest,
10497
+ `/receipts/${receiptUuid}/download${sandboxIdentitySuffix()}`
10498
+ ) : privacyCenterUrl(
10499
+ baseRequest,
10500
+ `/receipts/${receiptUuid}/pdf${sandboxIdentitySuffix()}`
10501
+ );
10471
10502
  const token = resolveAuthToken(authToken, baseRequest);
10472
10503
  const requestHeaders = new Headers();
10473
10504
  requestHeaders.set("Accept", "application/pdf");
@@ -10554,7 +10585,11 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10554
10585
  * Submit a document against a handler document request (privacy center)
10555
10586
  */
10556
10587
  async submitDocumentForRequest(baseRequest, caseUuid, requestEventUuid, documentUuid, body, authToken) {
10557
- const url = caseUrl(baseRequest, caseUuid, `/document-requests/${requestEventUuid}/submit`);
10588
+ const url = caseUrl(
10589
+ baseRequest,
10590
+ caseUuid,
10591
+ `/document-requests/${requestEventUuid}/submit${sandboxIdentitySuffix()}`
10592
+ );
10558
10593
  return privacyCenterApiFetch(url, {
10559
10594
  method: "POST",
10560
10595
  body: JSON.stringify({ document_uuid: documentUuid, body }),
@@ -10565,7 +10600,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10565
10600
  * Get document info/download URL for a case document (privacy center)
10566
10601
  */
10567
10602
  async getCaseDocument(baseRequest, caseUuid, documentUuid, authToken) {
10568
- const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}`);
10603
+ const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}${sandboxIdentitySuffix()}`);
10569
10604
  return privacyCenterApiFetch(url, {
10570
10605
  authToken: resolveAuthToken(authToken, baseRequest)
10571
10606
  });
@@ -18953,27 +18988,38 @@ var uploadFileWithS2S = async ({
18953
18988
  authToken,
18954
18989
  baseUrl
18955
18990
  }) => {
18991
+ var _a;
18956
18992
  try {
18957
- if (!authToken) {
18958
- throw new Error("Auth token is required for document upload");
18959
- }
18960
18993
  if (!baseUrl) {
18961
18994
  throw new Error("Base URL is required for document upload");
18962
18995
  }
18963
- const decodedToken = decodeJwtToken(authToken);
18964
- if (!decodedToken) {
18965
- throw new Error("Invalid access token");
18966
- }
18967
- const { organisation_uuid, workspace_uuid } = decodedToken;
18968
- if (!organisation_uuid || !workspace_uuid) {
18969
- throw new Error("Invalid token: missing organization or workspace UUID");
18996
+ const sandbox = getSandboxConfig();
18997
+ let organisation_uuid;
18998
+ let workspace_uuid;
18999
+ let authHeaders;
19000
+ if (sandbox) {
19001
+ organisation_uuid = sandbox.organisationUuid;
19002
+ workspace_uuid = sandbox.workspaceUuid;
19003
+ authHeaders = (_a = sandboxAuthHeaders()) != null ? _a : {};
19004
+ } else {
19005
+ if (!authToken) {
19006
+ throw new Error("Auth token is required for document upload");
19007
+ }
19008
+ const decodedToken = decodeJwtToken(authToken);
19009
+ if (!decodedToken) {
19010
+ throw new Error("Invalid access token");
19011
+ }
19012
+ if (!decodedToken.organisation_uuid || !decodedToken.workspace_uuid) {
19013
+ throw new Error("Invalid token: missing organization or workspace UUID");
19014
+ }
19015
+ organisation_uuid = decodedToken.organisation_uuid;
19016
+ workspace_uuid = decodedToken.workspace_uuid;
19017
+ authHeaders = { Authorization: `Bearer ${authToken}` };
18970
19018
  }
18971
19019
  const url = `${baseUrl}/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/dsar/privacy-center/upload-document`;
18972
19020
  const response = await fetch(url, {
18973
19021
  method: "POST",
18974
- headers: {
18975
- "Authorization": `Bearer ${authToken}`
18976
- },
19022
+ headers: authHeaders,
18977
19023
  body: formData
18978
19024
  });
18979
19025
  if (!response.ok) {
@@ -22826,13 +22872,16 @@ function ActivityTimeline({
22826
22872
  }
22827
22873
  }
22828
22874
  ),
22829
- group.items.map((activity, index) => /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
22830
- TimelineRow,
22831
- {
22832
- activity
22833
- },
22834
- `${group.key}-${index}`
22835
- ))
22875
+ group.items.map((activity, index) => {
22876
+ var _a;
22877
+ return /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
22878
+ TimelineRow,
22879
+ {
22880
+ activity
22881
+ },
22882
+ (_a = activity.id) != null ? _a : `${group.key}-${index}`
22883
+ );
22884
+ })
22836
22885
  ] })
22837
22886
  ]
22838
22887
  },
@@ -28088,7 +28137,8 @@ function RedactoPrivacyCenter({
28088
28137
  refreshToken,
28089
28138
  token,
28090
28139
  sandboxUcic: ucic,
28091
- sandboxSubject: email || mobile,
28140
+ sandboxEmail: email,
28141
+ sandboxMobile: mobile,
28092
28142
  organisationUuid,
28093
28143
  workspaceUuid,
28094
28144
  slug,
@@ -8072,13 +8072,20 @@ var sandboxBodyIdentity = () => {
8072
8072
  if (!sandboxConfig) {
8073
8073
  return null;
8074
8074
  }
8075
+ const payload = {};
8075
8076
  if (sandboxConfig.ucic) {
8076
- return { org_user_id: sandboxConfig.ucic };
8077
+ payload.org_user_id = sandboxConfig.ucic;
8077
8078
  }
8078
- return sandboxConfig.subject.includes("@") ? { primary_email: sandboxConfig.subject } : { primary_mobile: sandboxConfig.subject };
8079
+ if (sandboxConfig.email) {
8080
+ payload.primary_email = sandboxConfig.email;
8081
+ }
8082
+ if (sandboxConfig.mobile) {
8083
+ payload.primary_mobile = sandboxConfig.mobile;
8084
+ }
8085
+ return payload;
8079
8086
  };
8080
8087
  var sandboxQueryIdentity = () => sandboxBodyIdentity();
8081
- var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.subject : null;
8088
+ var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.email || sandboxConfig.mobile || null : null;
8082
8089
 
8083
8090
  // src/RedactoPrivacyCenter/api/fetcher.ts
8084
8091
  var ApiError = class _ApiError extends Error {
@@ -9803,7 +9810,7 @@ var seedSessionStorage = (data) => {
9803
9810
  if (data.contact) sessionStorage.setItem("email", data.contact);
9804
9811
  };
9805
9812
  var configureSandbox = (props) => {
9806
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
9813
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
9807
9814
  const token = (_a = props.token) == null ? void 0 : _a.trim();
9808
9815
  if (!token) {
9809
9816
  setSandboxConfig(null);
@@ -9812,12 +9819,14 @@ var configureSandbox = (props) => {
9812
9819
  const organisationUuid = (_c = (_b = props.organisationUuid) == null ? void 0 : _b.trim()) != null ? _c : "";
9813
9820
  const workspaceUuid = (_e = (_d = props.workspaceUuid) == null ? void 0 : _d.trim()) != null ? _e : "";
9814
9821
  const ucic = (_g = (_f = props.sandboxUcic) == null ? void 0 : _f.trim()) != null ? _g : "";
9815
- const subject = (_i = (_h = props.sandboxSubject) == null ? void 0 : _h.trim()) != null ? _i : "";
9816
- const slug = (_k = (_j = props.slug) == null ? void 0 : _j.trim()) != null ? _k : "";
9822
+ const email = (_i = (_h = props.sandboxEmail) == null ? void 0 : _h.trim()) != null ? _i : "";
9823
+ const mobile = (_k = (_j = props.sandboxMobile) == null ? void 0 : _j.trim()) != null ? _k : "";
9824
+ const slug = (_m = (_l = props.slug) == null ? void 0 : _l.trim()) != null ? _m : "";
9817
9825
  setSandboxConfig({
9818
9826
  token,
9819
9827
  ucic: ucic || void 0,
9820
- subject,
9828
+ email: email || void 0,
9829
+ mobile: mobile || void 0,
9821
9830
  organisationUuid,
9822
9831
  workspaceUuid,
9823
9832
  slug
@@ -9827,7 +9836,8 @@ var configureSandbox = (props) => {
9827
9836
  orgId: organisationUuid,
9828
9837
  workspaceId: workspaceUuid,
9829
9838
  slug,
9830
- contact: ucic || subject
9839
+ // The principal precedence (UCIC > email > mobile) the server also applies.
9840
+ contact: ucic || email || mobile
9831
9841
  });
9832
9842
  return true;
9833
9843
  };
@@ -9852,7 +9862,8 @@ var AuthProvider = (props) => {
9852
9862
  refreshToken: initialRefreshToken,
9853
9863
  token: sandboxToken,
9854
9864
  sandboxUcic,
9855
- sandboxSubject,
9865
+ sandboxEmail,
9866
+ sandboxMobile,
9856
9867
  organisationUuid,
9857
9868
  workspaceUuid,
9858
9869
  slug,
@@ -9918,7 +9929,7 @@ var AuthProvider = (props) => {
9918
9929
  }
9919
9930
  configureSandbox(props);
9920
9931
  return () => setSandboxConfig(null);
9921
- }, [sandboxActive, sandboxToken, sandboxUcic, sandboxSubject, organisationUuid, workspaceUuid, slug]);
9932
+ }, [sandboxActive, sandboxToken, sandboxUcic, sandboxEmail, sandboxMobile, organisationUuid, workspaceUuid, slug]);
9922
9933
  const updateTokens = useCallback2((newAccessToken, newRefreshToken) => {
9923
9934
  setAccessToken(newAccessToken);
9924
9935
  if (newRefreshToken) {
@@ -9956,7 +9967,7 @@ import { createContext as createContext3, useContext as useContext3, useMemo as
9956
9967
  // src/RedactoPrivacyCenter/api/client.ts
9957
9968
  function createConsentClient(baseUrl, ledgerBaseUrl) {
9958
9969
  const ledgerConfigured = Boolean(ledgerBaseUrl);
9959
- const useLedger = () => ledgerConfigured && !isSandboxMode();
9970
+ const useLedger = () => ledgerConfigured;
9960
9971
  const ledgerBase = (ledgerBaseUrl != null ? ledgerBaseUrl : "").replace(/\/$/, "");
9961
9972
  async function privacyCenterApiFetch(url, options) {
9962
9973
  const { authToken, getAuthToken, headers = {}, ...fetchOptions } = options;
@@ -10081,7 +10092,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10081
10092
  const identity = sandboxQueryIdentity();
10082
10093
  if (identity) {
10083
10094
  Object.entries(identity).forEach(([key, value]) => {
10084
- queryParams.append(key, value);
10095
+ if (value) {
10096
+ queryParams.append(key, value);
10097
+ }
10085
10098
  });
10086
10099
  }
10087
10100
  }
@@ -10152,13 +10165,20 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10152
10165
  },
10153
10166
  /**
10154
10167
  * Verify OTP (public)
10168
+ *
10169
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
10170
+ * `auth`-scoped token (and to resolve shared-contact principals via the
10171
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
10172
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
10173
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
10174
+ * then rejects it with 403 "Token scope is not permitted".
10155
10175
  */
10156
- async verifyOtp(organisationId, workspaceId, otpId, otp, options) {
10176
+ async verifyOtp(organisationId, workspaceId, otpId, otp, options, purpose = "login") {
10157
10177
  const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
10158
10178
  return apiFetch(url, {
10159
10179
  ...options,
10160
10180
  method: "POST",
10161
- body: JSON.stringify({ uuid: otpId, otp })
10181
+ body: JSON.stringify({ uuid: otpId, otp, purpose })
10162
10182
  });
10163
10183
  },
10164
10184
  /**
@@ -10266,6 +10286,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10266
10286
  * used with its `{ purpose_uuid, contact, ... }` payload.
10267
10287
  */
10268
10288
  async manageConsent(baseRequest, purposeId, contact, status, authToken, options, nominatorContact, dataElementUuids, productUuid, noticeUuid, dataElements, language) {
10289
+ var _a;
10269
10290
  if (!useLedger()) {
10270
10291
  const url2 = privacyCenterUrl(baseRequest, `/manage-consent?status=${status}`);
10271
10292
  const body2 = { purpose_uuid: purposeId, contact };
@@ -10305,6 +10326,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10305
10326
  });
10306
10327
  const body = {
10307
10328
  notice_uuid: noticeUuid,
10329
+ // Sandbox has no JWT, so the ledger resolves the acting test principal
10330
+ // from this identity in the body; null (spread no-op) on the live JWT path.
10331
+ ...(_a = sandboxBodyIdentity()) != null ? _a : {},
10308
10332
  partial: true,
10309
10333
  declined: false,
10310
10334
  select_all_mandatory: false,
@@ -10382,6 +10406,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10382
10406
  if (params.created_after) queryParams.append("created_after", params.created_after);
10383
10407
  if (params.created_before) queryParams.append("created_before", params.created_before);
10384
10408
  if (params.language) queryParams.append("language", params.language);
10409
+ appendSandboxIdentity(queryParams);
10385
10410
  const queryString = queryParams.toString();
10386
10411
  const receiptsSuffix = `/receipts${queryString ? `?${queryString}` : ""}`;
10387
10412
  const url = useLedger() ? ledgerUrl(baseRequest, receiptsSuffix) : privacyCenterUrl(baseRequest, receiptsSuffix);
@@ -10399,7 +10424,13 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10399
10424
  * and the Authorization header must be sent (anchor navigation wouldn't carry it).
10400
10425
  */
10401
10426
  async getReceiptPdf(baseRequest, receiptUuid, authToken) {
10402
- const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : privacyCenterUrl(baseRequest, `/receipts/${receiptUuid}/pdf`);
10427
+ const url = useLedger() ? ledgerUrl(
10428
+ baseRequest,
10429
+ `/receipts/${receiptUuid}/download${sandboxIdentitySuffix()}`
10430
+ ) : privacyCenterUrl(
10431
+ baseRequest,
10432
+ `/receipts/${receiptUuid}/pdf${sandboxIdentitySuffix()}`
10433
+ );
10403
10434
  const token = resolveAuthToken(authToken, baseRequest);
10404
10435
  const requestHeaders = new Headers();
10405
10436
  requestHeaders.set("Accept", "application/pdf");
@@ -10486,7 +10517,11 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10486
10517
  * Submit a document against a handler document request (privacy center)
10487
10518
  */
10488
10519
  async submitDocumentForRequest(baseRequest, caseUuid, requestEventUuid, documentUuid, body, authToken) {
10489
- const url = caseUrl(baseRequest, caseUuid, `/document-requests/${requestEventUuid}/submit`);
10520
+ const url = caseUrl(
10521
+ baseRequest,
10522
+ caseUuid,
10523
+ `/document-requests/${requestEventUuid}/submit${sandboxIdentitySuffix()}`
10524
+ );
10490
10525
  return privacyCenterApiFetch(url, {
10491
10526
  method: "POST",
10492
10527
  body: JSON.stringify({ document_uuid: documentUuid, body }),
@@ -10497,7 +10532,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10497
10532
  * Get document info/download URL for a case document (privacy center)
10498
10533
  */
10499
10534
  async getCaseDocument(baseRequest, caseUuid, documentUuid, authToken) {
10500
- const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}`);
10535
+ const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}${sandboxIdentitySuffix()}`);
10501
10536
  return privacyCenterApiFetch(url, {
10502
10537
  authToken: resolveAuthToken(authToken, baseRequest)
10503
10538
  });
@@ -18905,27 +18940,38 @@ var uploadFileWithS2S = async ({
18905
18940
  authToken,
18906
18941
  baseUrl
18907
18942
  }) => {
18943
+ var _a;
18908
18944
  try {
18909
- if (!authToken) {
18910
- throw new Error("Auth token is required for document upload");
18911
- }
18912
18945
  if (!baseUrl) {
18913
18946
  throw new Error("Base URL is required for document upload");
18914
18947
  }
18915
- const decodedToken = decodeJwtToken(authToken);
18916
- if (!decodedToken) {
18917
- throw new Error("Invalid access token");
18918
- }
18919
- const { organisation_uuid, workspace_uuid } = decodedToken;
18920
- if (!organisation_uuid || !workspace_uuid) {
18921
- throw new Error("Invalid token: missing organization or workspace UUID");
18948
+ const sandbox = getSandboxConfig();
18949
+ let organisation_uuid;
18950
+ let workspace_uuid;
18951
+ let authHeaders;
18952
+ if (sandbox) {
18953
+ organisation_uuid = sandbox.organisationUuid;
18954
+ workspace_uuid = sandbox.workspaceUuid;
18955
+ authHeaders = (_a = sandboxAuthHeaders()) != null ? _a : {};
18956
+ } else {
18957
+ if (!authToken) {
18958
+ throw new Error("Auth token is required for document upload");
18959
+ }
18960
+ const decodedToken = decodeJwtToken(authToken);
18961
+ if (!decodedToken) {
18962
+ throw new Error("Invalid access token");
18963
+ }
18964
+ if (!decodedToken.organisation_uuid || !decodedToken.workspace_uuid) {
18965
+ throw new Error("Invalid token: missing organization or workspace UUID");
18966
+ }
18967
+ organisation_uuid = decodedToken.organisation_uuid;
18968
+ workspace_uuid = decodedToken.workspace_uuid;
18969
+ authHeaders = { Authorization: `Bearer ${authToken}` };
18922
18970
  }
18923
18971
  const url = `${baseUrl}/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/dsar/privacy-center/upload-document`;
18924
18972
  const response = await fetch(url, {
18925
18973
  method: "POST",
18926
- headers: {
18927
- "Authorization": `Bearer ${authToken}`
18928
- },
18974
+ headers: authHeaders,
18929
18975
  body: formData
18930
18976
  });
18931
18977
  if (!response.ok) {
@@ -22789,13 +22835,16 @@ function ActivityTimeline({
22789
22835
  }
22790
22836
  }
22791
22837
  ),
22792
- group.items.map((activity, index) => /* @__PURE__ */ jsx71(
22793
- TimelineRow,
22794
- {
22795
- activity
22796
- },
22797
- `${group.key}-${index}`
22798
- ))
22838
+ group.items.map((activity, index) => {
22839
+ var _a;
22840
+ return /* @__PURE__ */ jsx71(
22841
+ TimelineRow,
22842
+ {
22843
+ activity
22844
+ },
22845
+ (_a = activity.id) != null ? _a : `${group.key}-${index}`
22846
+ );
22847
+ })
22799
22848
  ] })
22800
22849
  ]
22801
22850
  },
@@ -28055,7 +28104,8 @@ function RedactoPrivacyCenter({
28055
28104
  refreshToken,
28056
28105
  token,
28057
28106
  sandboxUcic: ucic,
28058
- sandboxSubject: email || mobile,
28107
+ sandboxEmail: email,
28108
+ sandboxMobile: mobile,
28059
28109
  organisationUuid,
28060
28110
  workspaceUuid,
28061
28111
  slug,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-react",
3
- "version": "10.1.0-beta.0",
3
+ "version": "10.1.0-beta.2",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",