@redacto.io/consent-sdk-react-native 5.1.0 → 6.0.0
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/README.md +148 -28
- package/dist/chunk-VM4GNLMU.mjs +557 -0
- package/dist/index.d.mts +176 -47
- package/dist/index.d.ts +176 -47
- package/dist/index.js +10382 -2950
- package/dist/index.mjs +9996 -2994
- package/dist/privacy-center.d.mts +504 -168
- package/dist/privacy-center.d.ts +504 -168
- package/dist/privacy-center.js +6706 -2882
- package/dist/privacy-center.mjs +6020 -2411
- package/dist/types-BYp89x8C.d.mts +28 -0
- package/dist/types-BYp89x8C.d.ts +28 -0
- package/package.json +5 -1
- package/dist/chunk-B2TXWC2C.mjs +0 -60
package/README.md
CHANGED
|
@@ -85,6 +85,7 @@ Pass the sandbox `token`, org/workspace, and a test identity (`email`, `mobile`,
|
|
|
85
85
|
import { RedactoNoticeConsent } from "@redacto.io/consent-sdk-react-native";
|
|
86
86
|
|
|
87
87
|
<RedactoNoticeConsent
|
|
88
|
+
baseUrl={BASE_URL} // the API host, e.g. https://api.redacto.io
|
|
88
89
|
noticeId={NOTICE_UUID}
|
|
89
90
|
token={SANDBOX_TOKEN}
|
|
90
91
|
organisationUuid={ORG_UUID}
|
|
@@ -104,7 +105,6 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react-native/priva
|
|
|
104
105
|
|
|
105
106
|
<RedactoPrivacyCenter
|
|
106
107
|
baseUrl={BASE_URL}
|
|
107
|
-
slug="acme-co"
|
|
108
108
|
token={SANDBOX_TOKEN}
|
|
109
109
|
organisationUuid={ORG_UUID}
|
|
110
110
|
workspaceUuid={WORKSPACE_UUID}
|
|
@@ -131,7 +131,7 @@ Both `RedactoNoticeConsent` and `RedactoPrivacyCenter` accept the same sandbox p
|
|
|
131
131
|
|
|
132
132
|
- The SDK sends the sandbox token as an `X-Consent-Token` header and the test identity inside each request (query params on reads, body on writes) — no JWT decode, no token refresh (a `401` fails fast).
|
|
133
133
|
- The server records everything under `environment=test`, namespaced so sandbox data never mixes with — or reads — your live records.
|
|
134
|
-
-
|
|
134
|
+
- With the API host as `baseUrl`, sandbox requests go to the same services as live ones (see [`baseUrl` and service routing](#baseurl-and-service-routing)). With the deprecated consent server URL and no `ledgerBaseUrl`, sandbox talks to the consent server only.
|
|
135
135
|
|
|
136
136
|
### Going live
|
|
137
137
|
|
|
@@ -143,10 +143,10 @@ Sandbox and production use the same components. To ship, remove the sandbox prop
|
|
|
143
143
|
|
|
144
144
|
The SDK never talks to Redacto with your CMS API key directly — your backend acts as a token broker. Obtain your **CMS-API-Key** from the Redacto Dashboard, store it in your backend's environment, and expose two endpoints to your mobile app:
|
|
145
145
|
|
|
146
|
-
1. **Generate access token** — `POST /public/organisations/{org_uuid}/workspaces/{workspace_uuid}/tokens/generate`
|
|
147
|
-
2. **Refresh access token** — `POST
|
|
146
|
+
1. **Generate access token** — `POST {consent server}/public/organisations/{org_uuid}/workspaces/{workspace_uuid}/tokens/generate`
|
|
147
|
+
2. **Refresh access token** — `POST {consent server}/public/organisations/{org_uuid}/workspaces/{workspace_uuid}/tokens/refresh`
|
|
148
148
|
|
|
149
|
-
Both endpoints expect the `X-CMS-API-Key` header. Example (Express):
|
|
149
|
+
The consent server is `{API host}/consent`, e.g. `https://api.redacto.io/consent`: the backend calls it directly, so it takes the consent server URL even though the SDK takes the API host. Both endpoints expect the `X-CMS-API-Key` header. Example (Express), where `base_url` is the consent server URL:
|
|
150
150
|
|
|
151
151
|
```ts
|
|
152
152
|
import express from "express";
|
|
@@ -174,7 +174,7 @@ app.post("/api/consent/refresh-token", async (req, res) => {
|
|
|
174
174
|
const { refresh_token, organisation_uuid, workspace_uuid, base_url } = req.body;
|
|
175
175
|
|
|
176
176
|
const response = await axios.post(
|
|
177
|
-
`${base_url}/
|
|
177
|
+
`${base_url}/public/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/tokens/refresh`,
|
|
178
178
|
{ refresh_token },
|
|
179
179
|
{ headers: { "X-CMS-API-Key": CMS_API_KEY } }
|
|
180
180
|
);
|
|
@@ -195,6 +195,7 @@ Modal consent component. Supports initial consent, reconsent, age verification,
|
|
|
195
195
|
import { RedactoNoticeConsent } from "@redacto.io/consent-sdk-react-native";
|
|
196
196
|
|
|
197
197
|
<RedactoNoticeConsent
|
|
198
|
+
baseUrl="https://api.redacto.io"
|
|
198
199
|
noticeId="your-notice-uuid"
|
|
199
200
|
accessToken={accessToken}
|
|
200
201
|
refreshToken={refreshToken}
|
|
@@ -210,19 +211,49 @@ import { RedactoNoticeConsent } from "@redacto.io/consent-sdk-react-native";
|
|
|
210
211
|
| `noticeId` | `string` | ✓ | UUID of the consent notice |
|
|
211
212
|
| `accessToken` | `string` | ✓ | JWT access token |
|
|
212
213
|
| `refreshToken` | `string` | ✓ | JWT refresh token |
|
|
213
|
-
| `baseUrl` | `string` | |
|
|
214
|
-
| `ledgerBaseUrl` | `string` | |
|
|
215
|
-
| `settings` | `Partial<Settings>` | |
|
|
214
|
+
| `baseUrl` | `string` | | API host, e.g. `https://api.redacto.io` (recommended). The consent server URL (`…/consent`) still works but is deprecated. Defaults to `https://api.redacto.io/consent`. See [`baseUrl` and service routing](#baseurl-and-service-routing) |
|
|
215
|
+
| `ledgerBaseUrl` | `string` | | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases: it then routes consent writes and already-consented checks through the ledger. Ignored when `baseUrl` is the API host |
|
|
216
|
+
| `settings` | `Partial<Settings>` | | Overrides for the appearance set in the Redacto console, key by key (see [Appearance](#appearance)) |
|
|
216
217
|
| `language` | `string` | | Initial language code (e.g. `"en"`) |
|
|
217
218
|
| `blockUI` | `boolean` | | Block the modal from being dismissed |
|
|
218
219
|
| `applicationId` | `string` | | Application-specific UUID |
|
|
219
220
|
| `validateAgainst` | `"all" \| "required"` | | Validation mode |
|
|
220
221
|
| `includeFullyConsentedData` | `boolean` | | Include already-consented items (review mode) |
|
|
221
|
-
| `reviewModeButtonText` | `string` | |
|
|
222
|
+
| `reviewModeButtonText` | `string` | | With `includeFullyConsentedData`, the button shown when everything is already consented; it calls `onAccept`. Default `"Continue"` |
|
|
222
223
|
| `onAccept` | `() => void` | ✓ | Called on successful consent |
|
|
223
224
|
| `onDecline` | `() => void` | ✓ | Called when user declines |
|
|
224
225
|
| `onError` | `(error: Error) => void` | | Called on API errors |
|
|
225
226
|
|
|
227
|
+
#### Appearance
|
|
228
|
+
|
|
229
|
+
The notice renders the appearance configured in the Redacto console (`active_config.appearance`): style (`classic`, `glass`, `minimal`, `soft`), layout (centred modal or bottom sheet on phones; centre, corner or bottom bar on wider screens), palette and dark palette, corner radius, logo size and position, selection control (checkbox, radio or dropdown, on purposes, product headings and data elements; checkboxes can be drawn as switches), text scale, motion, collapsible sections and an optional "are you sure" step before submitting. The frame around the admin's copy (yes/no, the confirm step, the verification panel, hints and labels) is shown in the notice's language. With nothing configured it looks as it did before.
|
|
230
|
+
|
|
231
|
+
Every `settings` key overrides the console key by key:
|
|
232
|
+
|
|
233
|
+
| Key | Type | Description |
|
|
234
|
+
| --- | --- | --- |
|
|
235
|
+
| `theme` | `"classic" \| "glass" \| "minimal" \| "soft"` | Visual style |
|
|
236
|
+
| `selectionControl` | `"checkbox" \| "radio" \| "dropdown"` | Control on purposes, product headings and data elements |
|
|
237
|
+
| `controlStyle` | `"checkbox" \| "switch"` | How checkbox rows are drawn, in any style |
|
|
238
|
+
| `confirmBeforeSubmit` | `boolean` | Ask "are you sure" before Accept All, Accept Selected or Decline submits |
|
|
239
|
+
| `mobileLayout` | `"modal" \| "sheet"` | Phones: centred or docked to the bottom edge |
|
|
240
|
+
| `desktopPosition` | `"center" \| "bottom-right" \| "bottom-left" \| "bottom-bar"` | Wider screens |
|
|
241
|
+
| `backdrop` | `"dim" \| "none"` | Dim the screen behind the notice |
|
|
242
|
+
| `collapsibleSections` | `boolean` | Collapse the notice text and DPO block into disclosures |
|
|
243
|
+
| `phoneFooter` | `"stacked" \| "paired"` | Phone button layout |
|
|
244
|
+
| `autoDark`, `darkPalette` | `boolean`, palette | Follow the device's dark mode (`useColorScheme`) with this palette |
|
|
245
|
+
| `backgroundColor`, `surfaceColor`, `headingColor`, `textColor`, `mutedTextColor`, `borderColor`, `overlayColor`, `link` | `string` | Palette |
|
|
246
|
+
| `button.accept`, `button.acceptAll`, `button.acceptSelected`, `button.decline`, `button.language` | colours | Button colours |
|
|
247
|
+
| `toggle`, `ttsHighlight` | colours | Switch colours; the element being read aloud |
|
|
248
|
+
| `borderRadius`, `maxWidth`, `logoSize` | `string \| number` | Corner radius, wide-screen width, logo height, in px |
|
|
249
|
+
| `textScale` | `"sm" \| "md" \| "lg"` | Scales every text size |
|
|
250
|
+
| `motion` | `boolean` | `false` turns entrances and transitions off (they are also off while the device asks for reduced motion) |
|
|
251
|
+
| `font` | `string` | Font family, winning over the console's |
|
|
252
|
+
|
|
253
|
+
React Native has no blur, so `glass` draws a denser tinted surface instead of a frosted one. `customCss` is accepted for parity with the web SDK and ignored. A phone sheet, a corner card and a bottom bar size to their content, up to 92% (sheet) or 70% of the screen.
|
|
254
|
+
|
|
255
|
+
The language selector names each language in its own script ("हिन्दी (Hindi)"). If the notice fails to load, it stays on screen with the reason and a **Refresh** button (closing it counts as a decline), and `onError` is still called. With `includeFullyConsentedData`, a visitor who has already consented to everything sees a single **Continue** button (`reviewModeButtonText`) that calls `onAccept`.
|
|
256
|
+
|
|
226
257
|
### `RedactoNoticeConsentInline`
|
|
227
258
|
|
|
228
259
|
Inline consent component. Auto-submits when all required elements are checked and an access token is available.
|
|
@@ -231,6 +262,7 @@ Inline consent component. Auto-submits when all required elements are checked an
|
|
|
231
262
|
import { RedactoNoticeConsentInline } from "@redacto.io/consent-sdk-react-native";
|
|
232
263
|
|
|
233
264
|
<RedactoNoticeConsentInline
|
|
265
|
+
baseUrl="https://api.redacto.io"
|
|
234
266
|
org_uuid="org-uuid"
|
|
235
267
|
workspace_uuid="workspace-uuid"
|
|
236
268
|
notice_uuid="notice-uuid"
|
|
@@ -248,16 +280,20 @@ import { RedactoNoticeConsentInline } from "@redacto.io/consent-sdk-react-native
|
|
|
248
280
|
| `workspace_uuid` | `string` | ✓ | Workspace UUID |
|
|
249
281
|
| `notice_uuid` | `string` | ✓ | Notice UUID |
|
|
250
282
|
| `accessToken` | `string` | | JWT access token (triggers auto-submit when set) |
|
|
251
|
-
| `baseUrl` | `string` | |
|
|
252
|
-
| `ledgerBaseUrl` | `string` | |
|
|
283
|
+
| `baseUrl` | `string` | | API host, e.g. `https://api.redacto.io` (recommended). The consent server URL (`…/consent`) still works but is deprecated. Defaults to `https://api.redacto.io/consent`. See [`baseUrl` and service routing](#baseurl-and-service-routing) |
|
|
284
|
+
| `ledgerBaseUrl` | `string` | | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases: it then routes consent writes and already-consented checks through the ledger. Ignored when `baseUrl` is the API host |
|
|
253
285
|
| `language` | `string` | | Language code |
|
|
254
|
-
| `settings` | `Partial<Settings>` | |
|
|
286
|
+
| `settings` | `Partial<Settings>` | | Overrides for the console appearance (palette, text scale, selection control, `confirmBeforeSubmit`, …), as on `RedactoNoticeConsent` |
|
|
255
287
|
| `applicationId` | `string` | | Application-specific UUID |
|
|
256
288
|
| `onAccept` | `() => void` | | Called on successful consent |
|
|
257
289
|
| `onDecline` | `() => void` | | Called when user declines |
|
|
258
290
|
| `onError` | `(error: Error) => void` | | Called on errors |
|
|
259
291
|
| `onValidationChange` | `(isValid: boolean) => void` | | Fires when validation state changes |
|
|
260
292
|
|
|
293
|
+
The inline notice follows the console appearance too, and starts from the notice's pre-selection (`purpose_preselection`: nothing ticked, the required elements, or everything), so it can auto-submit on first paint when that satisfies the required elements and a token is present. With "confirm before submit" set, it asks before the auto-submit and records nothing if the visitor says no.
|
|
294
|
+
|
|
295
|
+
`RedactoNoticeAssisted` (operator-assisted capture) displays the console appearance as well and takes the same `settings` prop; what it submits is unchanged.
|
|
296
|
+
|
|
261
297
|
## TTS Audio
|
|
262
298
|
|
|
263
299
|
If `expo-audio` is installed, the consent notice will dynamically load it and enable text-to-speech playback. The SDK calls `setAudioModeAsync` and creates an audio player internally — you don't need to set anything up beyond installing the package. See the [expo-audio docs](https://docs.expo.dev/versions/latest/sdk/audio/) if you need to customise audio session behaviour app-wide.
|
|
@@ -277,7 +313,6 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react-native/priva
|
|
|
277
313
|
|
|
278
314
|
<RedactoPrivacyCenter
|
|
279
315
|
baseUrl={BASE_URL}
|
|
280
|
-
slug="acme-co"
|
|
281
316
|
accessToken={accessToken}
|
|
282
317
|
refreshToken={refreshToken}
|
|
283
318
|
theme="light"
|
|
@@ -293,15 +328,67 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react-native/priva
|
|
|
293
328
|
|
|
294
329
|
| Prop | Type | Required | Default | Description |
|
|
295
330
|
| -------------- | ----------------------------------------------------------------------------- | -------- | -------------------- | ---------------------------------------- |
|
|
296
|
-
| `baseUrl` | `string` | ✓ | - |
|
|
297
|
-
| `ledgerBaseUrl` | `string` | | - |
|
|
298
|
-
| `slug` | `string` |
|
|
331
|
+
| `baseUrl` | `string` | ✓ | - | API host, e.g. `https://api.redacto.io` (recommended). The consent server URL (`…/consent`) still works but is deprecated. See [`baseUrl` and service routing](#baseurl-and-service-routing) |
|
|
332
|
+
| `ledgerBaseUrl` | `string` | | - | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`), as in earlier releases: it then routes user consents, group purposes, receipts and manage-consent through the ledger; without it, the legacy `/dsar/privacy-center/*` routes are used. Ignored when `baseUrl` is the API host. See the note below on the DSAR form picker. |
|
|
333
|
+
| `slug` | `string` | | - | **Deprecated** and ignored, to be removed in a future major release; passing it logs a one-time console warning. The organisation, and its branding, come from the access token, or from `organisationUuid` and `workspaceUuid` |
|
|
299
334
|
| `accessToken` | `string` | ✓ | - | JWT access token |
|
|
300
335
|
| `refreshToken` | `string` | ✓ | - | JWT refresh token |
|
|
301
336
|
| `onError` | `(error: Error) => boolean` | ✓ | - | Error handler; return `true` if handled |
|
|
302
337
|
| `theme` | `"light" \| "dark"` | | `"light"` | Theme variant |
|
|
303
338
|
| `initialPage` | `"consent-manager" \| "form" \| "activity" \| "receipts" \| "case-details"` | | `"consent-manager"` | Page to render first |
|
|
304
339
|
| `onBack` | `"back" \| "signout"` | | - | Navbar back / signout control behaviour |
|
|
340
|
+
| `organisationUuid` / `workspaceUuid` | `string` | | - | Without a token, address the built-in sign-in and the published settings |
|
|
341
|
+
| `settings` | `PrivacyCenterSettings` | | - | Overrides for the settings published in the console, merged field by field per group |
|
|
342
|
+
| `builtInSignIn` | `boolean` | | `false` | With no token, show the built-in OTP sign-in instead of the loader |
|
|
343
|
+
| `onReady` | `(event: { signedIn: boolean }) => void` | | - | Called once, when the first screen (sign-in or the Privacy Center) is on show |
|
|
344
|
+
| `onConsentChange` | `(change: PrivacyCenterConsentChange) => void` | | - | After a consent was withdrawn, re-granted or renewed |
|
|
345
|
+
| `onRequestSubmit` | `(request: PrivacyCenterRequestSubmission) => void` | | - | After a data request (case) was created |
|
|
346
|
+
| `onSignOut` | `(event: { reason: "user" \| "timeout" \| "session-expired" }) => void` | | - | When the visitor is signed out, with the reason |
|
|
347
|
+
|
|
348
|
+
#### Published settings
|
|
349
|
+
|
|
350
|
+
`RedactoPrivacyCenter` fetches the settings published in the Redacto console (Privacy Center › Settings) once it knows the organisation (from the props, the token's claims or an earlier session), and again in each language the visitor switches to. It never waits for them: it renders with the defaults, which reproduce the Privacy Center as it was, and keeps them if the request fails. Responses are cached per organisation and language for five minutes.
|
|
351
|
+
|
|
352
|
+
What the settings drive:
|
|
353
|
+
|
|
354
|
+
- **Branding**: display name, logo and logo height, primary and accent colours, font (resolved through the shared font catalogue, as the notice does) and corner radius. Each inherits workspace branding until the console says otherwise.
|
|
355
|
+
- **Menu**: section order, labels and on/off. A switched-off section leaves the home screen and menu and can't be navigated to; switching off notifications removes the bell.
|
|
356
|
+
- **Welcome message** on the home screen, with `{brand}`, `{legal}`, `{year}`, `{name}` and `{contact}` filled in.
|
|
357
|
+
- **Requests**: the request types offered (order, labels, on/off, narrowed to what the server allows), the grievance categories, and whether an erasure also revokes consent (asked of the data principal unless the organisation decided).
|
|
358
|
+
- **Withdraw warning**: the organisation's default; a purpose's own warning still wins.
|
|
359
|
+
- **Languages**: the languages the switcher offers, and the one a visitor lands on.
|
|
360
|
+
- **Inactivity sign-out**: with `portal.session_timeout_minutes`, the visitor is signed out after that long without a touch, scroll or keystroke (including on return from the background past the deadline) and lands on the session-expired screen.
|
|
361
|
+
- **Footer**: links, privacy policy, grievance officer and Data Protection Board lines, copyright, and "Powered by".
|
|
362
|
+
- **Sign-in screen** (see below).
|
|
363
|
+
|
|
364
|
+
Urdu, Sindhi and Kashmiri lay out right to left once settings are published or overridden, using `direction` and `writingDirection` on the Privacy Center's own views: the app's `I18nManager` is never touched.
|
|
365
|
+
|
|
366
|
+
The `settings` prop takes overrides merged over each published group, so one value can change while the rest of what the console published stays; `settings.theme` is kept as an alias for `branding.style`. React Native has one Privacy Center look, so the style is read but changes nothing; custom CSS, the SEO page title and embed origins are web-only and not applied.
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
<RedactoPrivacyCenter
|
|
370
|
+
baseUrl={BASE_URL}
|
|
371
|
+
accessToken={accessToken}
|
|
372
|
+
refreshToken={refreshToken}
|
|
373
|
+
onError={() => false}
|
|
374
|
+
settings={{ portal: { welcome_enabled: true, welcome_title: "Hello from {brand}" } }}
|
|
375
|
+
/>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Built-in sign-in
|
|
379
|
+
|
|
380
|
+
With `builtInSignIn` and no token, the Privacy Center shows its own sign-in: the visitor gives a mobile number or email, receives a one-time code (resend after 50 seconds) and is signed straight in with the tokens it issues. Layout (`card`, `split` or `minimal`), background, copy, channels, default country code, notice line and support link come from the published `sign_in` settings; copy left at its default is shown in the visitor's language. The organisation and workspace come from `organisationUuid` / `workspaceUuid` or an earlier sign-in's session, never from `slug`; without them the sign-in says the organisation could not be loaded. When the session expires and the host gave no `onBack`, the session-expired screen offers signing in again. Without `builtInSignIn` the Privacy Center keeps its loader until the host passes a token, as before.
|
|
381
|
+
|
|
382
|
+
#### Lifecycle callbacks
|
|
383
|
+
|
|
384
|
+
`onReady`, `onConsentChange`, `onRequestSubmit` and `onSignOut` always call the latest handler passed, and a handler that throws is logged, never rethrown.
|
|
385
|
+
|
|
386
|
+
| Callback | Payload |
|
|
387
|
+
| --- | --- |
|
|
388
|
+
| `onReady` | `{ signedIn }` — false when the sign-in screen is what shows |
|
|
389
|
+
| `onConsentChange` | `{ action: "revoke" \| "regrant" \| "renew", purposeUuid, productUuid, dataElementUuids, nominatorUuid }` |
|
|
390
|
+
| `onRequestSubmit` | `{ caseId, caseUuid, requestType, productUuid }` |
|
|
391
|
+
| `onSignOut` | `{ reason: "user" \| "timeout" \| "session-expired" }` |
|
|
305
392
|
|
|
306
393
|
### Composable Components
|
|
307
394
|
|
|
@@ -319,7 +406,7 @@ If you want to compose your own shell, import the screen-level building blocks i
|
|
|
319
406
|
| `BottomTabBar` | Bottom tab navigation |
|
|
320
407
|
| `Footer` | Footer block |
|
|
321
408
|
|
|
322
|
-
These rely on the context providers (`AuthProvider`, `BaseUrlProvider`, `NavigationProvider`, `ThemeProvider`, and `QueryClientProvider` for React Query hooks) — wrap them yourself if you compose the pages directly.
|
|
409
|
+
These rely on the context providers (`AuthProvider`, `BaseUrlProvider`, `NavigationProvider`, `ThemeProvider`, and `QueryClientProvider` for React Query hooks) — wrap them yourself if you compose the pages directly. Pass `ThemeProvider` the `organisationUuid` to brand a composition: it ignores `slug`.
|
|
323
410
|
|
|
324
411
|
### Receipt downloads
|
|
325
412
|
|
|
@@ -339,11 +426,11 @@ For building custom screens that match Privacy Center styling, the package expor
|
|
|
339
426
|
|
|
340
427
|
For consumers who want fully custom UI, the headless core exposes the API client, orchestration helpers, hooks, and auth utilities:
|
|
341
428
|
|
|
342
|
-
- **API client**: `createConsentClient`
|
|
429
|
+
- **API client**: `createConsentClient(baseUrl, ledgerBaseUrl?)`, which takes `baseUrl` the same way as the components
|
|
343
430
|
- **Orchestration helpers** (handle auth retry / token refresh internally): `getFormData`, `createCaseRequest`, `fetchProducts`, `fetchUserConsents`, `manageConsent`, `fetchActivities`, `fetchCaseHistory`, `getCaseMessages`, `sendCaseMessage`, `uploadCaseDocument`, `submitDocumentForRequest`, `createOtp`, `verifyOtp`, `exchangeToken`
|
|
344
431
|
- **Auth utilities**: `decodeJwtToken`, `isJwtExpired`, `ensureValidToken`, `signOutPrivacyCenter`, `compareEmails`, `getActivityTypeStatus`, `formatActivityType`
|
|
345
432
|
- **React Query hooks**: `useCaseHistory`, `useCaseMessages`
|
|
346
|
-
- **Context providers**: `AuthProvider` + `useAuth`, `BaseUrlProvider` + `useBaseUrl`, `NavigationProvider` + `useInternalNavigation`, `ThemeProvider` + `useTheme`
|
|
433
|
+
- **Context providers**: `AuthProvider` + `useAuth`, `BaseUrlProvider` + `useBaseUrl`, `NavigationProvider` + `useInternalNavigation`, `ThemeProvider` + `useTheme`. Given the API host, `useBaseUrl()` returns the derived consent server URL as `baseUrl` and the derived ledger URL as `ledgerBaseUrl`
|
|
347
434
|
- **Types**: `Workspace`, `OrganizationDetail`, `DataElement`, `Purpose`, `UserConsent`, `Activity`, `CaseHistoryDetail`, `CaseMessage`, `MessageDocument`, `RequestTypeEnum`, `GrievanceTypeEnum`, `ConsentStatusEnum`, and more (see `privacy-center.ts` for the full export list)
|
|
348
435
|
|
|
349
436
|
## Configuration and Env Best Practices
|
|
@@ -351,19 +438,52 @@ For consumers who want fully custom UI, the headless core exposes the API client
|
|
|
351
438
|
- The SDK is runtime-configured via props and does not read from `.env` directly.
|
|
352
439
|
- Keep API keys and server secrets on your backend only.
|
|
353
440
|
- In Expo apps, values read from `EXPO_PUBLIC_*` are bundled into the app and should only contain non-sensitive config like UUIDs and URLs.
|
|
354
|
-
-
|
|
355
|
-
|
|
441
|
+
- Pass the API host of your environment as `baseUrl`, e.g. `https://api.redacto.io`. If the notice components get no `baseUrl`, they fall back to the built-in production default, `https://api.redacto.io/consent`, and log a one-time warning.
|
|
442
|
+
|
|
443
|
+
## `baseUrl` and service routing
|
|
444
|
+
|
|
445
|
+
Pass the Redacto **API host** as `baseUrl`, for example `https://api.redacto.io`. The SDK derives each service from it — the consent server at `{baseUrl}/consent`, the consent ledger at `{baseUrl}/consent-ledger` and the user service at `{baseUrl}/user/api/0` — and sends each request to the service that serves it:
|
|
446
|
+
|
|
447
|
+
- **Notice components** — consent writes (`submit-consent`) and the already-consented check (`check-consent`) go to the ledger. `RedactoNoticeConsent` reads the notice from the ledger in a JWT session, and from the consent server in the magic-link (`applicationId`) flow and in sandbox; its audio comes from the ledger, sandbox included. `RedactoNoticeConsentInline` reads the notice from the ledger when it has an `accessToken`, and from the consent server without one and in the magic-link flow. `RedactoNoticeAssisted`'s notice, OTP and audio, and guardian verification, go to the consent server.
|
|
448
|
+
- **Privacy Center** — user consents, group purposes, receipts (including the `/download` PDF path) and manage-consent (sent as a `partial: true` write) go to the ledger. DSAR form, cases, activities, OTP, token exchange, notifications and uploads go to the consent server; org branding comes from the user service.
|
|
449
|
+
|
|
450
|
+
A host behind a path prefix works the same way: `https://gateway.example.com/redacto` gives `https://gateway.example.com/redacto/consent` and so on. A trailing `/` is ignored, and a `baseUrl` ending in `/consent-ledger` resolves to its host.
|
|
356
451
|
|
|
357
|
-
|
|
452
|
+
If the already-consented check fails, the component carries on and submits as usual.
|
|
358
453
|
|
|
359
|
-
|
|
454
|
+
> **Known limitation.** Unlike the React SDK, the DSAR form's product picker is **not** overlaid from ledger-served consents. Against a workspace cut over to the ledger — where the legacy `form/data` response carries no product groups — the picker renders empty. If your DSAR form depends on the product picker, use the deprecated consent server URL as `baseUrl` without `ledgerBaseUrl` until this is addressed.
|
|
360
455
|
|
|
361
|
-
|
|
362
|
-
|
|
456
|
+
### Deprecated: the consent server URL and `ledgerBaseUrl`
|
|
457
|
+
|
|
458
|
+
Earlier releases took the consent server URL as `baseUrl` (`https://api.redacto.io/consent`) and the Go ledger as a separate `ledgerBaseUrl`. Both keep working exactly as before:
|
|
459
|
+
|
|
460
|
+
- A `baseUrl` ending in `/consent` is the consent server URL, and `ledgerBaseUrl`, when passed, the ledger. Requests route as in earlier releases: with `ledgerBaseUrl`, as above, except that the inline notice read and sandbox audio stay on the consent server; without it, everything goes to the consent server, on its legacy routes (`/dsar/privacy-center/*`, `manage-consent?status=`, receipts `/pdf`, and the HTTP 409 already-consented signal).
|
|
461
|
+
- With the API host as `baseUrl`, `ledgerBaseUrl` is ignored: the ledger is always `{baseUrl}/consent-ledger`, and the SDK logs a one-time warning asking you to remove it.
|
|
462
|
+
|
|
463
|
+
The SDK logs a one-time console warning when `baseUrl` is left out, when it ends in `/consent`, and when `ledgerBaseUrl` is passed. These forms will be removed in a future major release: pass the API host as `baseUrl` and drop `ledgerBaseUrl`.
|
|
464
|
+
|
|
465
|
+
### Your backend
|
|
466
|
+
|
|
467
|
+
Your token backend calls the consent server itself, so it still needs the consent server URL, `{API host}/consent` (see [Backend Server Setup](#backend-server-setup)). If your app passes one value to both, give the SDK the API host and send `${BASE_URL}/consent` to the backend.
|
|
468
|
+
|
|
469
|
+
### Local development
|
|
470
|
+
|
|
471
|
+
Put the services behind one local gateway, the way production does, and pass its address as `baseUrl`. For example, with [Caddy](https://caddyserver.com), the consent server on port 8000 and the ledger on port 8090:
|
|
472
|
+
|
|
473
|
+
```caddyfile
|
|
474
|
+
:8080 {
|
|
475
|
+
handle /consent-ledger/* {
|
|
476
|
+
reverse_proxy localhost:8090
|
|
477
|
+
}
|
|
478
|
+
handle /consent/* {
|
|
479
|
+
reverse_proxy localhost:8000
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
```
|
|
363
483
|
|
|
364
|
-
|
|
484
|
+
Then pass `baseUrl="http://localhost:8080"`. Both services serve their own path prefix (`/consent/…`, `/consent-ledger/…`), so the gateway forwards paths unchanged. Any reverse proxy works the same way (nginx, Traefik, your docker-compose gateway).
|
|
365
485
|
|
|
366
|
-
|
|
486
|
+
On the Android emulator the host machine is `http://10.0.2.2:8080`; the iOS simulator reaches it at `http://localhost:8080`. A physical device needs the machine's LAN IP.
|
|
367
487
|
|
|
368
488
|
## License
|
|
369
489
|
|