-
-
Notifications
You must be signed in to change notification settings - Fork 262
fix: add web platform support for expoClient storage #845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: add web platform support for expoClient storage #845
Conversation
|
@AkaraChen is attempting to deploy a commit to the Better T Stack Team on Vercel. A member of the Team first needs to authorize it. |
|
I have no idea why the generated template file has so many diffs. |
WalkthroughIntroduces platform-aware storage selection in authClient templates. Add Platform import and modify storage logic to conditionally use window.localStorage on web and SecureStore on native platforms. No public API changes. Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/template-generator/templates/auth/better-auth/native/base/lib/auth-client.ts.hbs (1)
14-16: Consistent implementation with the convex variant — same verification applies.The storage interface compatibility concern raised in the convex template applies here as well. Once verified that
expoClientaccepts bothlocalStorageandSecureStoreinterfaces, this implementation correctly addresses the web platform support objective.
| storage: Platform.OS === "web" | ||
| ? window.localStorage | ||
| : SecureStore, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
better-auth expo client storage interface documentation
💡 Result:
Better Auth’s Expo client plugin (expoClient) documents the storage option in the Expo Integration guide, where it shows passing expo-secure-store as the storage provider. [1]
In practice, that means storage should be an object that looks like Expo SecureStore’s module API—i.e. it can store, read, and delete string values by key using:
setItemAsync(key: string, value: string, options?)getItemAsync(key: string, options?)deleteItemAsync(key: string, options?)[2]
Example from the Better Auth docs (passing SecureStore directly):
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";
import * as SecureStore from "expo-secure-store";
export const authClient = createAuthClient({
baseURL: "http://localhost:8081",
plugins: [
expoClient({
scheme: "myapp",
storagePrefix: "myapp",
storage: SecureStore,
}),
],
});[1]
Note: Expo SecureStore keys are restricted (alphanumeric plus ., -, _), so if your Better Auth storage keys don’t meet that requirement you may need an adapter / different prefixing strategy. [2][3]
Sources: [1][2][3]
🌐 Web query:
@better-auth/expo SecureStore localStorage compatibility
💡 Result:
@better-auth/expo’s client plugin (expoClient) expects you to pass a “storage” implementation (their docs show expo-secure-store) to cache session data/cookies. (better-auth.com)
1) SecureStore is not a drop-in “localStorage-compatible” solution (especially on web)
expo-secure-storehas long-standing limitations on web (it doesn’t behave like “store in localStorage and it works the same”). (github.com)- It also enforces key-name restrictions: keys must match
^[\w.-]+$(only alphanumeric/underscore plus.and-), so characters like:are invalid. (app.unpkg.com)
2) Better Auth + SecureStore key incompatibility (the : problem)
A reported Better Auth Expo issue notes that the plugin generates storage keys with colon separators like myapp:cookie / myapp:session_data, which triggers SecureStore’s “Invalid key” error and breaks persistence. (github.com)
What to do
- On native (iOS/Android): you can use SecureStore, but you may need a key-mangling adapter (e.g., replace
:with_) until/if the plugin avoids colons. (github.com) - On web: use a web storage implementation (e.g.,
localStorage/sessionStorage) instead of SecureStore, by passing a differentstoragetoexpoClientonPlatform.OS === "web"(Better Auth explicitly supports providing your own storage). (better-auth.com)
Citations:
- 1: https://www.better-auth.com/docs/integrations/expo?utm_source=openai
- 2: SecureStore isn't working in web expo/expo#7744
- 3: https://app.unpkg.com/expo-secure-store%4013.0.2/files/src/SecureStore.ts?utm_source=openai
- 4: [Expo] SecureStore fails with "Invalid key" error - storage keys use colons better-auth/better-auth#5426
- 5: [Expo] SecureStore fails with "Invalid key" error - storage keys use colons better-auth/better-auth#5426
- 6: https://www.better-auth.com/docs/integrations/expo?utm_source=openai
🌐 Web query:
better-auth storage options async sync compatibility
💡 Result:
Better Auth “storage” (where it keeps sessions / other auth state) typically falls into three buckets:
- Database (primary storage)
- Default when you configure
database: sessions are stored in the DB. (better-auth.com)
- Secondary storage (key-value store like Redis/KV)
- Configure
secondaryStorage: { get, set, delete }and Better Auth will store sessions in the secondary store by default (instead of the DB), unless you force DB storage withsession.storeSessionInDatabase: true. (better-auth.com) - The documented
SecondaryStorageinterface is async (Promise-based) (get/set/deletereturnPromise). (better-auth.com)
- Cookie cache / stateless
- You can enable cookie caching (
session.cookieCache) to reduce DB hits, or run fully stateless (no DB config) where session data is stored/validated via signed/encrypted cookies. (better-auth.com)
Async vs sync compatibility
- Global
secondaryStorage: documented as Promise-returning (async). (better-auth.com) - Some plugin-level “custom storage” hooks (example: API Key plugin
customStorage) explicitly allow either sync or async methods (returnPromise<…> | …). (better-auth.com)
Citations:
- 1: https://www.better-auth.com/docs/concepts/session-management?utm_source=openai
- 2: https://www.better-auth.com/docs/concepts/session-management?utm_source=openai
- 3: https://www.better-auth.com/docs/concepts/database?utm_source=openai
- 4: https://www.better-auth.com/docs/concepts/session-management?utm_source=openai
- 5: https://www.better-auth.com/docs/concepts/database?utm_source=openai
- 6: https://www.better-auth.com/docs/plugins/api-key?utm_source=openai
Fix storage interface incompatibility: expoClient expects async storage, but localStorage is synchronous.
expoClient is designed for async storage (SecureStore-like API with getItemAsync, setItemAsync, deleteItemAsync). Passing window.localStorage on web violates this expectation. Additionally, better-auth generates storage keys with colons (e.g., myapp:cookie), which SecureStore rejects due to its key restrictions (alphanumeric, ., - only).
For web, pass an async storage implementation to expoClient (e.g., AsyncStorage or a custom async wrapper). For native, either use SecureStore with a key-mangling adapter to replace colons, or consider an alternative storage strategy. See: better-auth issue #5426.
|
I encountered the same issue with expo web but I believe the better fix is this: Because automatically by default BetterAuth sends proper cookie headers and the browser automatically responds with it. Instead of storing the cookie in localStorage for web we should override where we call authClient.getCookie for web. Though i get the intention that this repo does not really support expo web since its not a web option from the cli |
Summary
window.localStorageas fallback whenPlatform.OS === "web"Closes #844
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.