i18n: change `storageStrategy` default to `'none'` (regulatory + opt-in)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 56/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bien especificado
- Estado de actividad
- Tranquilo
- Stack tecnológico
- react, typescript
- Área
- frontend
Línea de trabajo
Empieza en packages/javascript/src/models/config.ts y packages/react/src/contexts/I18n/I18nProvider.tsx, leyendo el tipo storageStrategy, la documentación, el fallback en tiempo de ejecución, setCookie y las rutas de createStorageAdapter. Confirma que los valores predeterminados declarados y los de tiempo de ejecución pasen a ser 'none', que las opciones explícitas de cookie y localStorage sigan funcionando y que se actualicen las notas de la versión o la documentación afectadas; valida esto con las comprobaciones existentes del paquete React.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
I18nPreferences.storageStrategy defaults to 'cookie'. As a result, mounting <AsgardeoProvider> writes an asgardeo-i18n-language cookie to the user's browser on the very first render, before the user has interacted with the app, regardless of whether the app exposes a language switcher.
This puts every SDK consumer serving EU users (and equivalent jurisdictions) into non-compliant-by-default territory under ePrivacy Directive Article 5(3) and GDPR. And the consumer cannot fix it themselves, because the write happens before any consent UI can react.
Proposing: change the default to 'none'. Apps that genuinely surface language selection can opt into persistence with one line of config.
The regulatory problem
Under the EU ePrivacy Directive (Art. 5(3)) and GDPR (recital 30 + Art. 7), cookies fall into two categories:
- Strictly necessary: required for a service the user explicitly requested (auth session, CSRF, shopping cart). Allowed without consent.
- Non-essential: everything else (analytics, preferences, marketing). Require prior, freely given, specific, informed, unambiguous consent before the cookie is written.
A language-preference cookie is unambiguously non-essential:
- It doesn't gate authentication. The OAuth flow works without it.
- It's a UI preference. The 2020 European Data Protection Board guidance (01/2020 on consent) is explicit that preference cookies require opt-in unless the user actively set the preference themselves.
- Many apps that mount
<AsgardeoProvider>never expose any language UI at all, in which case the cookie isn't even reflecting a user choice, just a stored browser-locale guess.
Equivalent regimes that apply the same rule: UK GDPR + PECR, Brazil LGPD, Switzerland nFADP, and in spirit CCPA/CPRA (less strict on cookies specifically but converging).
Why consumers can't fix this themselves
The cookie is written inside an useEffect in I18nProvider, which runs on the first render of <AsgardeoProvider>. That render happens at the top of the React tree, typically before any consent banner has loaded, let alone been interacted with.
No amount of consumer-side code can hook in between <AsgardeoProvider> mounting and the cookie being written. The opt-out paths available today (passing preferences.i18n.storageStrategy: 'none') require the consumer to know that:
- The cookie exists at all (it has no console warning, no docs callout).
- The exact nested config shape (which changed between SDK 0.22.x and 0.23.x without prominent changelog flagging).
- To set it before the provider mounts (only
'none'actually stops the write;'localStorage'still writes to some store on mount).
Even teams that find the cookie and try to disable it routinely ship 2-3 no-op patches before getting the nesting right, because any mistake silently falls back to the cookie default with no warning.
Current behavior
packages/javascript/src/models/config.ts:
export type I18nStorageStrategy = 'cookie' | 'localStorage' | 'none';
/**
* The storage strategy to use for persisting the user's language selection.
* @default 'cookie'
*/
storageStrategy?: I18nStorageStrategy;
packages/react/src/contexts/I18n/I18nProvider.tsx:
const storageStrategy = preferences?.storageStrategy ?? 'cookie';
The cookie writer uses a 365-day Max-Age, scoped to the eTLD+1 of the current hostname (so app.example.com writes for .example.com, affecting every subdomain), SameSite=Lax + Secure.
Proposed change
/**
* The storage strategy to use for persisting the user's language selection.
*
* Defaults to `'none'` so the SDK does not write client-side storage without
* the consuming app explicitly asking for it. This keeps consumers compliant
* with cookie-consent regimes (EU ePrivacy / GDPR, UK PECR, LGPD, etc.) by
* default, since a non-essential preference cookie may not be set before the
* user has consented.
*
* Set explicitly to `'cookie'` or `'localStorage'` if your app exposes a
* language switcher and wants the choice to persist across sessions. In that
* case the consuming app is responsible for gating the call behind user
* consent where required.
*
* @default 'none'
*/
storageStrategy?: I18nStorageStrategy;
Runtime default:
const storageStrategy = preferences?.storageStrategy ?? 'none';
Why this is the right fix (vs. alternatives)
- Keep
'cookie', document it more loudly. Doesn't address that consumers physically cannot avoid the cookie via configuration. Documentation does not satisfy ePrivacy Art. 5(3). - Default to
'localStorage'. Better on consent grounds (some regulators treat localStorage and cookies identically under ePrivacy, though enforcement has been less consistent), but still writes client-side state for apps that don't use the feature. Doesn't fix principle-of-least-surprise. - Default to
'none'(this proposal). Side-effect free on mount. Apps that want persistence opt in explicitly and take responsibility for consent. Matches how most modern SDKs handle optional preference persistence.
Principle of least surprise (secondary argument)
Beyond the legal angle: SDKs should not write persistent client-side state for features the consuming app isn't actively using. An app that mounts <AsgardeoProvider> purely for OAuth, with no useTranslation call anywhere and no language switcher UI, gets a year-long cookie under the parent domain. That's surprising behavior.
Migration impact
For consumers who do surface language selection today and rely on persistence, the default change is a behavior change: they'd need to add
preferences: { i18n: { storageStrategy: 'localStorage' } } // or 'cookie' if they want the current behavior
to keep the prior behavior. Worth a prominent callout in the release notes. Could be staged as:
- Emit a console warning whenever the default is being used (so consumers see "you're getting a cookie you may not have asked for; set
storageStrategyexplicitly"). - Flip the default to
'none'in the next minor.
Refs
- Cookie writer + storage adapter:
packages/react/src/contexts/I18n/I18nProvider.tsx(setCookie,createStorageAdapter) - EDPB Guidelines 01/2020 on consent: https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_en.pdf
- ePrivacy Directive Art. 5(3): https://eur-lex.europa.eu/eli/dir/2002/58/oj
Happy to send a PR if maintainers are on board.
- Lenguaje dominante
- TypeScript
- Estrellas
- 18
- Forks
- 67
- Merge medio
- 3 h 40 min
- PR fusionados (30 d)
- 15
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de asgardeo/javascript
-
Bug: SignInButton render prop usage in "react-tanstack-router" sample missing "onClick={signIn}"Posiblemente ocupada @KavishkaVenuka la tomó hace 14 días. AbiertoType/Bug
Dificultad 1/5 Menos de una hora Aptitud para principiantes 92/100
asgardeo/javascript#572 ·
Los mantenedores suelen responder en 1 día
-
Type/Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
asgardeo/javascript#571 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
asgardeo/javascript#485 ·
Los mantenedores suelen responder en 1 día
-
signInSilently() always resolves false: getAuthorizeRequestUrlParams overwrites the "sign-in-silently" state with instance_<id>Posiblemente ocupada @Dumindu-Kanchana la tomó hace 4 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 78/100
asgardeo/javascript#577 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 52/100
asgardeo/javascript#527 ·
Los mantenedores suelen responder en 1 día
Todos los issues de asgardeo/javascript
Issues similares
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Dificultad 1/5 Menos de una hora Aptitud para principiantes 95/100
lingdojo/kana-dojo#31666 · 1 comentario · 5 reacciones ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
-
Bug: lockTtlSeconds / lockHeartbeatIntervalSeconds accept non-positive and non-finite valuesPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
CopilotKit/CopilotKit#7618 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
wimpysworld/sidra#287 ·
Los mantenedores suelen responder en 1 día
-
bug ready-for-agent
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
sleeyax/paseo-plugins#112 ·
Los mantenedores suelen responder en 2 días