@redacto.io/consent-sdk-react-native 5.0.1-beta.0 → 5.0.1-beta.1

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
@@ -48,7 +48,7 @@ var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
48
48
 
49
49
  // src/RedactoNoticeConsent/api/sandbox.ts
50
50
  var resolveSandboxAuth = (params) => {
51
- var _a, _b, _c, _d, _e;
51
+ var _a, _b, _c, _d, _e, _f;
52
52
  const token = (_a = params.token) == null ? void 0 : _a.trim();
53
53
  if (!token) {
54
54
  return null;
@@ -56,16 +56,17 @@ var resolveSandboxAuth = (params) => {
56
56
  const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
57
57
  const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
58
58
  const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
59
- const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
59
+ const email = (_e = params.sandboxEmail) == null ? void 0 : _e.trim();
60
+ const mobile = (_f = params.sandboxMobile) == null ? void 0 : _f.trim();
60
61
  if (!organisationUuid || !workspaceUuid) {
61
62
  throw new Error(
62
63
  "Sandbox mode requires organisationUuid and workspaceUuid props"
63
64
  );
64
65
  }
65
- if (!ucic && !subject) {
66
+ if (!ucic && !email && !mobile) {
66
67
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
67
68
  }
68
- return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
69
+ return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
69
70
  };
70
71
  var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
71
72
  const sandbox = resolveSandboxAuth(sandboxParams);
@@ -97,10 +98,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
97
98
  return { Authorization: `Bearer ${accessToken}` };
98
99
  };
99
100
  var sandboxBodyIdentity = (sandbox) => {
101
+ const payload = {};
100
102
  if (sandbox.ucic) {
101
- return { org_user_id: sandbox.ucic };
103
+ payload.org_user_id = sandbox.ucic;
104
+ }
105
+ if (sandbox.email) {
106
+ payload.primary_email = sandbox.email;
102
107
  }
103
- return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
108
+ if (sandbox.mobile) {
109
+ payload.primary_mobile = sandbox.mobile;
110
+ }
111
+ return payload;
104
112
  };
105
113
  var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
106
114
 
@@ -199,7 +207,7 @@ var fetchConsentContent = async ({
199
207
  }
200
208
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
201
209
  const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
202
- const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
210
+ const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
203
211
  const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
204
212
  if (validate_against === "all" && !specific_uuid) {
205
213
  const cachedData = getCachedData(cacheKey);
@@ -219,7 +227,9 @@ var fetchConsentContent = async ({
219
227
  }
220
228
  if (sandbox) {
221
229
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
222
- url.searchParams.append(key, value);
230
+ if (value) {
231
+ url.searchParams.append(key, value);
232
+ }
223
233
  }
224
234
  }
225
235
  const response = await fetch(url.toString(), {
@@ -353,7 +363,9 @@ var fetchTTSAudioUrls = async ({
353
363
  );
354
364
  if (sandbox) {
355
365
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
356
- url.searchParams.append(key, value);
366
+ if (value) {
367
+ url.searchParams.append(key, value);
368
+ }
357
369
  }
358
370
  }
359
371
  const response = await fetch(url.toString(), {
@@ -1860,7 +1872,8 @@ var RedactoNoticeConsent = ({
1860
1872
  () => ({
1861
1873
  token,
1862
1874
  sandboxUcic: ucic,
1863
- sandboxSubject: email || mobile,
1875
+ sandboxEmail: email,
1876
+ sandboxMobile: mobile,
1864
1877
  organisationUuid,
1865
1878
  workspaceUuid
1866
1879
  }),
package/dist/index.mjs CHANGED
@@ -32,7 +32,7 @@ import { jwtDecode } from "jwt-decode";
32
32
 
33
33
  // src/RedactoNoticeConsent/api/sandbox.ts
34
34
  var resolveSandboxAuth = (params) => {
35
- var _a, _b, _c, _d, _e;
35
+ var _a, _b, _c, _d, _e, _f;
36
36
  const token = (_a = params.token) == null ? void 0 : _a.trim();
37
37
  if (!token) {
38
38
  return null;
@@ -40,16 +40,17 @@ var resolveSandboxAuth = (params) => {
40
40
  const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
41
41
  const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
42
42
  const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
43
- const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
43
+ const email = (_e = params.sandboxEmail) == null ? void 0 : _e.trim();
44
+ const mobile = (_f = params.sandboxMobile) == null ? void 0 : _f.trim();
44
45
  if (!organisationUuid || !workspaceUuid) {
45
46
  throw new Error(
46
47
  "Sandbox mode requires organisationUuid and workspaceUuid props"
47
48
  );
48
49
  }
49
- if (!ucic && !subject) {
50
+ if (!ucic && !email && !mobile) {
50
51
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
51
52
  }
52
- return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
53
+ return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
53
54
  };
54
55
  var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
55
56
  const sandbox = resolveSandboxAuth(sandboxParams);
@@ -81,10 +82,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
81
82
  return { Authorization: `Bearer ${accessToken}` };
82
83
  };
83
84
  var sandboxBodyIdentity = (sandbox) => {
85
+ const payload = {};
84
86
  if (sandbox.ucic) {
85
- return { org_user_id: sandbox.ucic };
87
+ payload.org_user_id = sandbox.ucic;
88
+ }
89
+ if (sandbox.email) {
90
+ payload.primary_email = sandbox.email;
86
91
  }
87
- return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
92
+ if (sandbox.mobile) {
93
+ payload.primary_mobile = sandbox.mobile;
94
+ }
95
+ return payload;
88
96
  };
89
97
  var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
90
98
 
@@ -139,7 +147,7 @@ var fetchConsentContent = async ({
139
147
  }
140
148
  const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
141
149
  const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
142
- const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
150
+ const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
143
151
  const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
144
152
  if (validate_against === "all" && !specific_uuid) {
145
153
  const cachedData = getCachedData(cacheKey);
@@ -159,7 +167,9 @@ var fetchConsentContent = async ({
159
167
  }
160
168
  if (sandbox) {
161
169
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
162
- url.searchParams.append(key, value);
170
+ if (value) {
171
+ url.searchParams.append(key, value);
172
+ }
163
173
  }
164
174
  }
165
175
  const response = await fetch(url.toString(), {
@@ -293,7 +303,9 @@ var fetchTTSAudioUrls = async ({
293
303
  );
294
304
  if (sandbox) {
295
305
  for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
296
- url.searchParams.append(key, value);
306
+ if (value) {
307
+ url.searchParams.append(key, value);
308
+ }
297
309
  }
298
310
  }
299
311
  const response = await fetch(url.toString(), {
@@ -1807,7 +1819,8 @@ var RedactoNoticeConsent = ({
1807
1819
  () => ({
1808
1820
  token,
1809
1821
  sandboxUcic: ucic,
1810
- sandboxSubject: email || mobile,
1822
+ sandboxEmail: email,
1823
+ sandboxMobile: mobile,
1811
1824
  organisationUuid,
1812
1825
  workspaceUuid
1813
1826
  }),
@@ -395,6 +395,8 @@ type ManageConsentParams = {
395
395
  dataElementUuids?: string[];
396
396
  };
397
397
  type Activity = {
398
+ /** Stable per-activity id (source event/case uuid) for list keys and de-duplication. */
399
+ id?: string;
398
400
  activity_type: string;
399
401
  activity_type_display?: string;
400
402
  timestamp: string;
@@ -691,8 +693,15 @@ declare function createConsentClient(baseUrl: string, ledgerBaseUrl?: string): {
691
693
  createOtp(organisationId: string, workspaceId: string, email: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
692
694
  /**
693
695
  * Verify OTP (public)
696
+ *
697
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
698
+ * `auth`-scoped token (and to resolve shared-contact principals via the
699
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
700
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
701
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
702
+ * then rejects it with 403 "Token scope is not permitted".
694
703
  */
695
- verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
704
+ verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">, purpose?: "login" | "consent"): Promise<unknown>;
696
705
  /**
697
706
  * Exchange one-time token for JWT tokens (public)
698
707
  */
@@ -943,10 +952,12 @@ interface AuthProviderProps {
943
952
  * payload, and never calls `/otp/refresh`.
944
953
  */
945
954
  token?: string;
946
- /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
955
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence for the principal. */
947
956
  sandboxUcic?: string;
948
- /** Sandbox subject (email or mobile) — carried in the request payload. */
949
- sandboxSubject?: string;
957
+ /** Sandbox email — carried on every request, like a live JWT's `user_data.primary_email`. */
958
+ sandboxEmail?: string;
959
+ /** Sandbox mobile — carried on every request, like a live JWT's `user_data.primary_mobile`. */
960
+ sandboxMobile?: string;
950
961
  /** Sandbox org UUID (replaces the JWT-decoded org). */
951
962
  organisationUuid?: string;
952
963
  /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
@@ -395,6 +395,8 @@ type ManageConsentParams = {
395
395
  dataElementUuids?: string[];
396
396
  };
397
397
  type Activity = {
398
+ /** Stable per-activity id (source event/case uuid) for list keys and de-duplication. */
399
+ id?: string;
398
400
  activity_type: string;
399
401
  activity_type_display?: string;
400
402
  timestamp: string;
@@ -691,8 +693,15 @@ declare function createConsentClient(baseUrl: string, ledgerBaseUrl?: string): {
691
693
  createOtp(organisationId: string, workspaceId: string, email: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
692
694
  /**
693
695
  * Verify OTP (public)
696
+ *
697
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
698
+ * `auth`-scoped token (and to resolve shared-contact principals via the
699
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
700
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
701
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
702
+ * then rejects it with 403 "Token scope is not permitted".
694
703
  */
695
- verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">): Promise<unknown>;
704
+ verifyOtp(organisationId: string, workspaceId: string, otpId: string, otp: string, options?: Omit<ApiFetchOptions, "getAuthToken">, purpose?: "login" | "consent"): Promise<unknown>;
696
705
  /**
697
706
  * Exchange one-time token for JWT tokens (public)
698
707
  */
@@ -943,10 +952,12 @@ interface AuthProviderProps {
943
952
  * payload, and never calls `/otp/refresh`.
944
953
  */
945
954
  token?: string;
946
- /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
955
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence for the principal. */
947
956
  sandboxUcic?: string;
948
- /** Sandbox subject (email or mobile) — carried in the request payload. */
949
- sandboxSubject?: string;
957
+ /** Sandbox email — carried on every request, like a live JWT's `user_data.primary_email`. */
958
+ sandboxEmail?: string;
959
+ /** Sandbox mobile — carried on every request, like a live JWT's `user_data.primary_mobile`. */
960
+ sandboxMobile?: string;
950
961
  /** Sandbox org UUID (replaces the JWT-decoded org). */
951
962
  organisationUuid?: string;
952
963
  /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
@@ -10046,13 +10046,20 @@ var sandboxBodyIdentity = () => {
10046
10046
  if (!sandboxConfig) {
10047
10047
  return null;
10048
10048
  }
10049
+ const payload = {};
10049
10050
  if (sandboxConfig.ucic) {
10050
- return { org_user_id: sandboxConfig.ucic };
10051
+ payload.org_user_id = sandboxConfig.ucic;
10051
10052
  }
10052
- return sandboxConfig.subject.includes("@") ? { primary_email: sandboxConfig.subject } : { primary_mobile: sandboxConfig.subject };
10053
+ if (sandboxConfig.email) {
10054
+ payload.primary_email = sandboxConfig.email;
10055
+ }
10056
+ if (sandboxConfig.mobile) {
10057
+ payload.primary_mobile = sandboxConfig.mobile;
10058
+ }
10059
+ return payload;
10053
10060
  };
10054
10061
  var sandboxQueryIdentity = () => sandboxBodyIdentity();
10055
- var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.subject : null;
10062
+ var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.email || sandboxConfig.mobile || null : null;
10056
10063
 
10057
10064
  // src/RedactoPrivacyCenter/api/fetcher.ts
10058
10065
  var ApiError = class _ApiError extends Error {
@@ -10214,7 +10221,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10214
10221
  const identity = sandboxQueryIdentity();
10215
10222
  if (identity) {
10216
10223
  Object.entries(identity).forEach(([key, value]) => {
10217
- queryParams.append(key, value);
10224
+ if (value) {
10225
+ queryParams.append(key, value);
10226
+ }
10218
10227
  });
10219
10228
  }
10220
10229
  }
@@ -10369,13 +10378,20 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10369
10378
  },
10370
10379
  /**
10371
10380
  * Verify OTP (public)
10381
+ *
10382
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
10383
+ * `auth`-scoped token (and to resolve shared-contact principals via the
10384
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
10385
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
10386
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
10387
+ * then rejects it with 403 "Token scope is not permitted".
10372
10388
  */
10373
- async verifyOtp(organisationId, workspaceId, otpId, otp, options) {
10389
+ async verifyOtp(organisationId, workspaceId, otpId, otp, options, purpose = "login") {
10374
10390
  const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
10375
10391
  return apiFetch(url, {
10376
10392
  ...options,
10377
10393
  method: "POST",
10378
- body: JSON.stringify({ uuid: otpId, otp })
10394
+ body: JSON.stringify({ uuid: otpId, otp, purpose })
10379
10395
  });
10380
10396
  },
10381
10397
  /**
@@ -10613,7 +10629,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10613
10629
  * the RN UI writes to a temp file to open/share. 404 => not found.
10614
10630
  */
10615
10631
  async getReceiptPdf(baseRequest, receiptUuid, authToken) {
10616
- const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/receipts/${receiptUuid}/pdf`;
10632
+ const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/receipts/${receiptUuid}/pdf${sandboxIdentitySuffix()}`;
10617
10633
  const token = authToken || baseRequest.authToken;
10618
10634
  const requestHeaders = new Headers();
10619
10635
  requestHeaders.set("Accept", "application/pdf");
@@ -12178,7 +12194,7 @@ function syncJwtDataToStore(token) {
12178
12194
  return authData;
12179
12195
  }
12180
12196
  function configureSandbox(props) {
12181
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12197
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
12182
12198
  const token = (_a3 = props.token) == null ? void 0 : _a3.trim();
12183
12199
  if (!token) {
12184
12200
  setSandboxConfig(null);
@@ -12187,23 +12203,25 @@ function configureSandbox(props) {
12187
12203
  const organisationUuid = (_c = (_b = props.organisationUuid) == null ? void 0 : _b.trim()) != null ? _c : "";
12188
12204
  const workspaceUuid = (_e = (_d = props.workspaceUuid) == null ? void 0 : _d.trim()) != null ? _e : "";
12189
12205
  const ucic = (_g = (_f = props.sandboxUcic) == null ? void 0 : _f.trim()) != null ? _g : "";
12190
- const subject = (_i = (_h = props.sandboxSubject) == null ? void 0 : _h.trim()) != null ? _i : "";
12191
- const slug = (_k = (_j = props.slug) == null ? void 0 : _j.trim()) != null ? _k : "";
12206
+ const email = (_i = (_h = props.sandboxEmail) == null ? void 0 : _h.trim()) != null ? _i : "";
12207
+ const mobile = (_k = (_j = props.sandboxMobile) == null ? void 0 : _j.trim()) != null ? _k : "";
12208
+ const slug = (_m = (_l = props.slug) == null ? void 0 : _l.trim()) != null ? _m : "";
12192
12209
  if (!organisationUuid || !workspaceUuid) {
12193
12210
  throw new Error("Sandbox mode requires organisationUuid and workspaceUuid props");
12194
12211
  }
12195
- if (!ucic && !subject) {
12212
+ if (!ucic && !email && !mobile) {
12196
12213
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
12197
12214
  }
12198
12215
  setSandboxConfig({
12199
12216
  token,
12200
12217
  ucic: ucic || void 0,
12201
- subject,
12218
+ email: email || void 0,
12219
+ mobile: mobile || void 0,
12202
12220
  organisationUuid,
12203
12221
  workspaceUuid,
12204
12222
  slug
12205
12223
  });
12206
- const contact = ucic || subject;
12224
+ const contact = ucic || email || mobile;
12207
12225
  setStoredValue("access_token", token);
12208
12226
  if (organisationUuid) setStoredValue("pc_org_id", organisationUuid);
12209
12227
  if (workspaceUuid) setStoredValue("pc_workspace_id", workspaceUuid);
@@ -12222,7 +12240,8 @@ function AuthProvider(props) {
12222
12240
  refreshToken: initialRefreshToken,
12223
12241
  token: sandboxToken,
12224
12242
  sandboxUcic,
12225
- sandboxSubject,
12243
+ sandboxEmail,
12244
+ sandboxMobile,
12226
12245
  organisationUuid,
12227
12246
  workspaceUuid,
12228
12247
  slug,
@@ -12265,7 +12284,7 @@ function AuthProvider(props) {
12265
12284
  setOrganizationName(seeded.organizationName);
12266
12285
  }
12267
12286
  return () => setSandboxConfig(null);
12268
- }, [sandboxActive, sandboxToken, sandboxUcic, sandboxSubject, organisationUuid, workspaceUuid, slug]);
12287
+ }, [sandboxActive, sandboxToken, sandboxUcic, sandboxEmail, sandboxMobile, organisationUuid, workspaceUuid, slug]);
12269
12288
  (0, import_react3.useEffect)(() => {
12270
12289
  if (sandboxActive) return;
12271
12290
  if (accessToken) {
@@ -16014,7 +16033,7 @@ function ActivitiesList() {
16014
16033
  {
16015
16034
  data: activities,
16016
16035
  style: { backgroundColor: colors.canvasSubtle },
16017
- keyExtractor: (item, idx) => `${item.case_id || item.timestamp}-${idx}`,
16036
+ keyExtractor: (item, idx) => item.id || `${item.case_id || item.timestamp}-${idx}`,
16018
16037
  onEndReached: handleLoadMore,
16019
16038
  onEndReachedThreshold: 0.3,
16020
16039
  contentContainerStyle: [styles30.content, { flexGrow: 1, paddingBottom: listBottom }],
@@ -21424,7 +21443,8 @@ function RedactoPrivacyCenter({
21424
21443
  refreshToken: refreshToken != null ? refreshToken : "",
21425
21444
  token,
21426
21445
  sandboxUcic: ucic,
21427
- sandboxSubject: email || mobile,
21446
+ sandboxEmail: email,
21447
+ sandboxMobile: mobile,
21428
21448
  organisationUuid,
21429
21449
  workspaceUuid,
21430
21450
  slug,
@@ -9931,13 +9931,20 @@ var sandboxBodyIdentity = () => {
9931
9931
  if (!sandboxConfig) {
9932
9932
  return null;
9933
9933
  }
9934
+ const payload = {};
9934
9935
  if (sandboxConfig.ucic) {
9935
- return { org_user_id: sandboxConfig.ucic };
9936
+ payload.org_user_id = sandboxConfig.ucic;
9936
9937
  }
9937
- return sandboxConfig.subject.includes("@") ? { primary_email: sandboxConfig.subject } : { primary_mobile: sandboxConfig.subject };
9938
+ if (sandboxConfig.email) {
9939
+ payload.primary_email = sandboxConfig.email;
9940
+ }
9941
+ if (sandboxConfig.mobile) {
9942
+ payload.primary_mobile = sandboxConfig.mobile;
9943
+ }
9944
+ return payload;
9938
9945
  };
9939
9946
  var sandboxQueryIdentity = () => sandboxBodyIdentity();
9940
- var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.subject : null;
9947
+ var sandboxContact = () => sandboxConfig ? sandboxConfig.ucic || sandboxConfig.email || sandboxConfig.mobile || null : null;
9941
9948
 
9942
9949
  // src/RedactoPrivacyCenter/api/fetcher.ts
9943
9950
  var ApiError = class _ApiError extends Error {
@@ -10099,7 +10106,9 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10099
10106
  const identity = sandboxQueryIdentity();
10100
10107
  if (identity) {
10101
10108
  Object.entries(identity).forEach(([key, value]) => {
10102
- queryParams.append(key, value);
10109
+ if (value) {
10110
+ queryParams.append(key, value);
10111
+ }
10103
10112
  });
10104
10113
  }
10105
10114
  }
@@ -10254,13 +10263,20 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10254
10263
  },
10255
10264
  /**
10256
10265
  * Verify OTP (public)
10266
+ *
10267
+ * Defaults to `purpose: "login"` — the branch the backend uses to mint an
10268
+ * `auth`-scoped token (and to resolve shared-contact principals via the
10269
+ * ledger). `purpose: "consent"` is reserved for consent flows. A login that
10270
+ * omits the purpose falls back server-side to `purpose: "consent"`, which
10271
+ * mints a scope-less notice token — every `/dsar/privacy-center/*` endpoint
10272
+ * then rejects it with 403 "Token scope is not permitted".
10257
10273
  */
10258
- async verifyOtp(organisationId, workspaceId, otpId, otp, options) {
10274
+ async verifyOtp(organisationId, workspaceId, otpId, otp, options, purpose = "login") {
10259
10275
  const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
10260
10276
  return apiFetch(url, {
10261
10277
  ...options,
10262
10278
  method: "POST",
10263
- body: JSON.stringify({ uuid: otpId, otp })
10279
+ body: JSON.stringify({ uuid: otpId, otp, purpose })
10264
10280
  });
10265
10281
  },
10266
10282
  /**
@@ -10498,7 +10514,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10498
10514
  * the RN UI writes to a temp file to open/share. 404 => not found.
10499
10515
  */
10500
10516
  async getReceiptPdf(baseRequest, receiptUuid, authToken) {
10501
- const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/receipts/${receiptUuid}/pdf`;
10517
+ const url = useLedger() ? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`) : `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/receipts/${receiptUuid}/pdf${sandboxIdentitySuffix()}`;
10502
10518
  const token = authToken || baseRequest.authToken;
10503
10519
  const requestHeaders = new Headers();
10504
10520
  requestHeaders.set("Accept", "application/pdf");
@@ -12063,7 +12079,7 @@ function syncJwtDataToStore(token) {
12063
12079
  return authData;
12064
12080
  }
12065
12081
  function configureSandbox(props) {
12066
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12082
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
12067
12083
  const token = (_a3 = props.token) == null ? void 0 : _a3.trim();
12068
12084
  if (!token) {
12069
12085
  setSandboxConfig(null);
@@ -12072,23 +12088,25 @@ function configureSandbox(props) {
12072
12088
  const organisationUuid = (_c = (_b = props.organisationUuid) == null ? void 0 : _b.trim()) != null ? _c : "";
12073
12089
  const workspaceUuid = (_e = (_d = props.workspaceUuid) == null ? void 0 : _d.trim()) != null ? _e : "";
12074
12090
  const ucic = (_g = (_f = props.sandboxUcic) == null ? void 0 : _f.trim()) != null ? _g : "";
12075
- const subject = (_i = (_h = props.sandboxSubject) == null ? void 0 : _h.trim()) != null ? _i : "";
12076
- const slug = (_k = (_j = props.slug) == null ? void 0 : _j.trim()) != null ? _k : "";
12091
+ const email = (_i = (_h = props.sandboxEmail) == null ? void 0 : _h.trim()) != null ? _i : "";
12092
+ const mobile = (_k = (_j = props.sandboxMobile) == null ? void 0 : _j.trim()) != null ? _k : "";
12093
+ const slug = (_m = (_l = props.slug) == null ? void 0 : _l.trim()) != null ? _m : "";
12077
12094
  if (!organisationUuid || !workspaceUuid) {
12078
12095
  throw new Error("Sandbox mode requires organisationUuid and workspaceUuid props");
12079
12096
  }
12080
- if (!ucic && !subject) {
12097
+ if (!ucic && !email && !mobile) {
12081
12098
  throw new Error("Sandbox mode requires a UCIC, email, or mobile");
12082
12099
  }
12083
12100
  setSandboxConfig({
12084
12101
  token,
12085
12102
  ucic: ucic || void 0,
12086
- subject,
12103
+ email: email || void 0,
12104
+ mobile: mobile || void 0,
12087
12105
  organisationUuid,
12088
12106
  workspaceUuid,
12089
12107
  slug
12090
12108
  });
12091
- const contact = ucic || subject;
12109
+ const contact = ucic || email || mobile;
12092
12110
  setStoredValue("access_token", token);
12093
12111
  if (organisationUuid) setStoredValue("pc_org_id", organisationUuid);
12094
12112
  if (workspaceUuid) setStoredValue("pc_workspace_id", workspaceUuid);
@@ -12107,7 +12125,8 @@ function AuthProvider(props) {
12107
12125
  refreshToken: initialRefreshToken,
12108
12126
  token: sandboxToken,
12109
12127
  sandboxUcic,
12110
- sandboxSubject,
12128
+ sandboxEmail,
12129
+ sandboxMobile,
12111
12130
  organisationUuid,
12112
12131
  workspaceUuid,
12113
12132
  slug,
@@ -12150,7 +12169,7 @@ function AuthProvider(props) {
12150
12169
  setOrganizationName(seeded.organizationName);
12151
12170
  }
12152
12171
  return () => setSandboxConfig(null);
12153
- }, [sandboxActive, sandboxToken, sandboxUcic, sandboxSubject, organisationUuid, workspaceUuid, slug]);
12172
+ }, [sandboxActive, sandboxToken, sandboxUcic, sandboxEmail, sandboxMobile, organisationUuid, workspaceUuid, slug]);
12154
12173
  useEffect(() => {
12155
12174
  if (sandboxActive) return;
12156
12175
  if (accessToken) {
@@ -15992,7 +16011,7 @@ function ActivitiesList() {
15992
16011
  {
15993
16012
  data: activities,
15994
16013
  style: { backgroundColor: colors.canvasSubtle },
15995
- keyExtractor: (item, idx) => `${item.case_id || item.timestamp}-${idx}`,
16014
+ keyExtractor: (item, idx) => item.id || `${item.case_id || item.timestamp}-${idx}`,
15996
16015
  onEndReached: handleLoadMore,
15997
16016
  onEndReachedThreshold: 0.3,
15998
16017
  contentContainerStyle: [styles30.content, { flexGrow: 1, paddingBottom: listBottom }],
@@ -21428,7 +21447,8 @@ function RedactoPrivacyCenter({
21428
21447
  refreshToken: refreshToken != null ? refreshToken : "",
21429
21448
  token,
21430
21449
  sandboxUcic: ucic,
21431
- sandboxSubject: email || mobile,
21450
+ sandboxEmail: email,
21451
+ sandboxMobile: mobile,
21432
21452
  organisationUuid,
21433
21453
  workspaceUuid,
21434
21454
  slug,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-react-native",
3
- "version": "5.0.1-beta.0",
3
+ "version": "5.0.1-beta.1",
4
4
  "description": "React Native SDK for integrating Redacto consent notices and privacy center in mobile apps.",
5
5
  "keywords": [
6
6
  "consent",