@better-auth/expo 1.7.0-beta.1 → 1.7.0-beta.10
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/client.d.ts +21 -7
- package/dist/client.js +81 -23
- package/dist/index.js +12 -4
- package/dist/plugins/index.js +1 -1
- package/dist/{version-CxmVFmt9.js → version-CXjBN8D3.js} +1 -1
- package/package.json +13 -13
package/dist/client.d.ts
CHANGED
|
@@ -84,10 +84,23 @@ declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: str
|
|
|
84
84
|
declare function normalizeCookieName(name: string): string;
|
|
85
85
|
declare function storageAdapter(storage: {
|
|
86
86
|
getItem: (name: string) => string | null;
|
|
87
|
-
setItem: (name: string, value: string) =>
|
|
87
|
+
setItem: (name: string, value: string) => unknown;
|
|
88
88
|
}): {
|
|
89
|
+
/**
|
|
90
|
+
* Reads a value, reassembling it if it was split across chunk keys. A value
|
|
91
|
+
* that fit is returned as-is (values written before chunking still read
|
|
92
|
+
* back); a missing chunk returns `null` so a torn write fails closed.
|
|
93
|
+
*/
|
|
89
94
|
getItem: (name: string) => string | null;
|
|
90
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Stores `value`, splitting it across chunk keys when it exceeds the
|
|
97
|
+
* per-write limit. The base key is cleared before the chunks are rewritten
|
|
98
|
+
* and set to the marker last, as the commit point, so a write interrupted
|
|
99
|
+
* partway through reads as absent rather than a mix of old and new chunks.
|
|
100
|
+
* Failures are logged, not thrown: persistence is best-effort and must not
|
|
101
|
+
* break the request.
|
|
102
|
+
*/
|
|
103
|
+
setItem: (name: string, value: string) => Promise<void>;
|
|
91
104
|
};
|
|
92
105
|
declare const expoClient: (opts: ExpoClientOptions) => {
|
|
93
106
|
id: "expo";
|
|
@@ -119,11 +132,6 @@ declare const expoClient: (opts: ExpoClientOptions) => {
|
|
|
119
132
|
init(url: string, options: ({
|
|
120
133
|
priority?: RequestPriority | undefined;
|
|
121
134
|
method?: string | undefined;
|
|
122
|
-
headers?: (HeadersInit & (HeadersInit | {
|
|
123
|
-
accept: "application/json" | "text/plain" | "application/octet-stream";
|
|
124
|
-
"content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
|
|
125
|
-
authorization: "Bearer" | "Basic";
|
|
126
|
-
})) | undefined;
|
|
127
135
|
redirect?: RequestRedirect | undefined;
|
|
128
136
|
window?: null | undefined;
|
|
129
137
|
cache?: RequestCache | undefined;
|
|
@@ -159,6 +167,12 @@ declare const expoClient: (opts: ExpoClientOptions) => {
|
|
|
159
167
|
prefix: string | (() => string | undefined) | undefined;
|
|
160
168
|
value: string | (() => string | undefined) | undefined;
|
|
161
169
|
}) | undefined;
|
|
170
|
+
headers?: {} | {
|
|
171
|
+
[x: string]: string | undefined;
|
|
172
|
+
accept?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream") | undefined;
|
|
173
|
+
"content-type"?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream" | "application/x-www-form-urlencoded" | "multipart/form-data") | undefined;
|
|
174
|
+
authorization?: ((string & {}) | `Bearer ${string}` | `Basic ${string}`) | undefined;
|
|
175
|
+
} | undefined;
|
|
162
176
|
body?: any;
|
|
163
177
|
query?: any;
|
|
164
178
|
params?: any;
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-CXjBN8D3.js";
|
|
2
2
|
import { safeJSONParse } from "@better-auth/core/utils/json";
|
|
3
3
|
import { SECURE_COOKIE_PREFIX, parseSetCookieHeader, parseSetCookieHeader as parseSetCookieHeader$1, stripSecureCookiePrefix } from "better-auth/cookies";
|
|
4
4
|
import Constants from "expo-constants";
|
|
@@ -195,13 +195,54 @@ function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
|
|
|
195
195
|
function normalizeCookieName(name) {
|
|
196
196
|
return name.replace(/:/g, "_");
|
|
197
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Max characters written per `setItem`. Native secure stores silently reject
|
|
200
|
+
* oversized writes (iOS Keychain refuses values above ~2KB), losing the cookie,
|
|
201
|
+
* so a larger value is split across keys here. Mirrors the server's
|
|
202
|
+
* `chunkCookie`/`joinChunks` in `session-store.ts`; keep the two in sync.
|
|
203
|
+
*
|
|
204
|
+
* @see https://github.com/better-auth/better-auth/issues/9151
|
|
205
|
+
*/
|
|
206
|
+
const STORAGE_VALUE_LIMIT = 1800;
|
|
207
|
+
/**
|
|
208
|
+
* Marks a base key whose value is split across `<key>.0..N` chunks. The leading
|
|
209
|
+
* control char can't start a JSON value (so it never collides) and, unlike NUL,
|
|
210
|
+
* survives the native storage bridge without C-string truncation.
|
|
211
|
+
*/
|
|
212
|
+
const CHUNK_MARKER = "ba-chunks:";
|
|
198
213
|
function storageAdapter(storage) {
|
|
199
214
|
return {
|
|
200
215
|
getItem: (name) => {
|
|
201
|
-
|
|
216
|
+
const key = normalizeCookieName(name);
|
|
217
|
+
const stored = storage.getItem(key);
|
|
218
|
+
if (stored == null || !stored.startsWith(CHUNK_MARKER)) return stored;
|
|
219
|
+
const count = Number(stored.slice(11));
|
|
220
|
+
if (!Number.isInteger(count) || count < 1) return null;
|
|
221
|
+
let value = "";
|
|
222
|
+
for (let i = 0; i < count; i++) {
|
|
223
|
+
const chunk = storage.getItem(`${key}.${i}`);
|
|
224
|
+
if (chunk == null) return null;
|
|
225
|
+
value += chunk;
|
|
226
|
+
}
|
|
227
|
+
return value;
|
|
202
228
|
},
|
|
203
|
-
setItem: (name, value) => {
|
|
204
|
-
|
|
229
|
+
setItem: async (name, value) => {
|
|
230
|
+
const key = normalizeCookieName(name);
|
|
231
|
+
try {
|
|
232
|
+
if (value.length <= STORAGE_VALUE_LIMIT) {
|
|
233
|
+
await storage.setItem(key, value);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
await storage.setItem(key, "");
|
|
237
|
+
const count = Math.ceil(value.length / STORAGE_VALUE_LIMIT);
|
|
238
|
+
for (let i = 0; i < count; i++) {
|
|
239
|
+
const start = i * STORAGE_VALUE_LIMIT;
|
|
240
|
+
await storage.setItem(`${key}.${i}`, value.slice(start, start + STORAGE_VALUE_LIMIT));
|
|
241
|
+
}
|
|
242
|
+
await storage.setItem(key, `${CHUNK_MARKER}${count}`);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(`[better-auth/expo] failed to persist "${key}" to storage`, error);
|
|
245
|
+
}
|
|
205
246
|
}
|
|
206
247
|
};
|
|
207
248
|
}
|
|
@@ -213,6 +254,16 @@ const expoClient = (opts) => {
|
|
|
213
254
|
const storage = storageAdapter(opts?.storage);
|
|
214
255
|
const isWeb = Platform.OS === "web";
|
|
215
256
|
const cookiePrefix = opts?.cookiePrefix || "better-auth";
|
|
257
|
+
const clearSessionCache = async () => {
|
|
258
|
+
await storage.setItem(cookieName, "{}");
|
|
259
|
+
store?.atoms.session?.set({
|
|
260
|
+
...store.atoms.session.get(),
|
|
261
|
+
data: null,
|
|
262
|
+
error: null,
|
|
263
|
+
isPending: false
|
|
264
|
+
});
|
|
265
|
+
await storage.setItem(localCacheName, "{}");
|
|
266
|
+
};
|
|
216
267
|
const rawScheme = opts?.scheme || Constants.expoConfig?.scheme || Constants.platform?.scheme;
|
|
217
268
|
const scheme = Array.isArray(rawScheme) ? rawScheme[0] : rawScheme;
|
|
218
269
|
if (!scheme && !isWeb) throw new Error("Scheme not found in app.json. Please provide a scheme in the options.");
|
|
@@ -221,6 +272,18 @@ const expoClient = (opts) => {
|
|
|
221
272
|
version: PACKAGE_VERSION,
|
|
222
273
|
getActions(_, $store) {
|
|
223
274
|
store = $store;
|
|
275
|
+
const sessionAtom = $store.atoms.session;
|
|
276
|
+
if (!isWeb && !opts?.disableCache && sessionAtom) {
|
|
277
|
+
const raw = storage.getItem(localCacheName);
|
|
278
|
+
const cached = raw ? safeJSONParse(raw) : null;
|
|
279
|
+
const exp = cached?.session?.expiresAt;
|
|
280
|
+
const expMs = exp ? new Date(exp).getTime() : NaN;
|
|
281
|
+
if (!!cached?.user?.id && !!cached.session?.id && expMs > Date.now()) sessionAtom.set({
|
|
282
|
+
...sessionAtom.get(),
|
|
283
|
+
data: cached,
|
|
284
|
+
error: null
|
|
285
|
+
});
|
|
286
|
+
}
|
|
224
287
|
return { getCookie: () => {
|
|
225
288
|
return getCookie(storage.getItem(cookieName) || "{}");
|
|
226
289
|
} };
|
|
@@ -236,15 +299,16 @@ const expoClient = (opts) => {
|
|
|
236
299
|
const prevCookie = storage.getItem(cookieName);
|
|
237
300
|
const toSetCookie = getSetCookie(setCookie || "", prevCookie ?? void 0);
|
|
238
301
|
if (hasSessionCookieChanged(prevCookie, toSetCookie)) {
|
|
239
|
-
storage.setItem(cookieName, toSetCookie);
|
|
302
|
+
await storage.setItem(cookieName, toSetCookie);
|
|
240
303
|
store?.notify("$sessionSignal");
|
|
241
|
-
} else storage.setItem(cookieName, toSetCookie);
|
|
304
|
+
} else await storage.setItem(cookieName, toSetCookie);
|
|
242
305
|
}
|
|
243
306
|
}
|
|
244
307
|
if (context.request.url.toString().includes("/get-session") && !opts?.disableCache) {
|
|
245
308
|
const data = context.data;
|
|
246
|
-
storage.setItem(localCacheName, JSON.stringify(data));
|
|
309
|
+
await storage.setItem(localCacheName, JSON.stringify(data));
|
|
247
310
|
}
|
|
311
|
+
if (context.request.url.toString().includes("/sign-out")) await clearSessionCache();
|
|
248
312
|
if (context.data?.redirect && (context.request.url.toString().includes("/sign-in") || context.request.url.toString().includes("/link-social")) && !context.request?.body.includes("idToken")) {
|
|
249
313
|
const to = JSON.parse(context.request.body)?.callbackURL;
|
|
250
314
|
const signInURL = context.data?.url;
|
|
@@ -270,7 +334,7 @@ const expoClient = (opts) => {
|
|
|
270
334
|
const cookie = new URL(result.url).searchParams.get("cookie");
|
|
271
335
|
if (!cookie) return;
|
|
272
336
|
const toSetCookie = getSetCookie(cookie, storage.getItem(cookieName) ?? void 0);
|
|
273
|
-
storage.setItem(cookieName, toSetCookie);
|
|
337
|
+
await storage.setItem(cookieName, toSetCookie);
|
|
274
338
|
store?.notify("$sessionSignal");
|
|
275
339
|
}
|
|
276
340
|
} },
|
|
@@ -281,11 +345,14 @@ const expoClient = (opts) => {
|
|
|
281
345
|
};
|
|
282
346
|
options = options || {};
|
|
283
347
|
options.credentials = "omit";
|
|
284
|
-
if (options.body?.idToken !== void 0)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
348
|
+
if (options.body?.idToken !== void 0) {
|
|
349
|
+
const cookie = url.includes("/link-social") ? getCookie(storage.getItem(cookieName) || "{}") : "";
|
|
350
|
+
options.headers = {
|
|
351
|
+
...options.headers,
|
|
352
|
+
...cookie ? { cookie } : {},
|
|
353
|
+
"x-skip-oauth-proxy": "true"
|
|
354
|
+
};
|
|
355
|
+
} else {
|
|
289
356
|
const cookie = getCookie(storage.getItem(cookieName) || "{}");
|
|
290
357
|
options.headers = {
|
|
291
358
|
...options.headers,
|
|
@@ -311,16 +378,7 @@ const expoClient = (opts) => {
|
|
|
311
378
|
options.body.errorCallbackURL = url;
|
|
312
379
|
}
|
|
313
380
|
}
|
|
314
|
-
if (url.includes("/sign-out"))
|
|
315
|
-
storage.setItem(cookieName, "{}");
|
|
316
|
-
store?.atoms.session?.set({
|
|
317
|
-
...store.atoms.session.get(),
|
|
318
|
-
data: null,
|
|
319
|
-
error: null,
|
|
320
|
-
isPending: false
|
|
321
|
-
});
|
|
322
|
-
storage.setItem(localCacheName, "{}");
|
|
323
|
-
}
|
|
381
|
+
if (url.includes("/sign-out")) await clearSessionCache();
|
|
324
382
|
}
|
|
325
383
|
return {
|
|
326
384
|
url,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-CXjBN8D3.js";
|
|
2
2
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
3
3
|
import { HIDE_METADATA } from "better-auth";
|
|
4
4
|
import { APIError, createAuthEndpoint } from "better-auth/api";
|
|
@@ -12,14 +12,22 @@ const expoAuthorizationProxy = createAuthEndpoint("/expo-authorization-proxy", {
|
|
|
12
12
|
}),
|
|
13
13
|
metadata: HIDE_METADATA
|
|
14
14
|
}, async (ctx) => {
|
|
15
|
+
const { authorizationURL } = ctx.query;
|
|
16
|
+
if (authorizationURL.includes("#")) throw new APIError("BAD_REQUEST", { message: "Invalid authorizationURL" });
|
|
17
|
+
let url;
|
|
18
|
+
try {
|
|
19
|
+
url = new URL(authorizationURL);
|
|
20
|
+
} catch {
|
|
21
|
+
throw new APIError("BAD_REQUEST", { message: "Invalid authorizationURL" });
|
|
22
|
+
}
|
|
23
|
+
if (url.protocol !== "https:" || url.origin === new URL(ctx.context.baseURL).origin) throw new APIError("BAD_REQUEST", { message: "Invalid authorizationURL" });
|
|
15
24
|
const { oauthState } = ctx.query;
|
|
16
25
|
if (oauthState) {
|
|
17
26
|
const oauthStateCookie = ctx.context.createAuthCookie("oauth_state", { maxAge: 600 });
|
|
18
27
|
ctx.setCookie(oauthStateCookie.name, oauthState, oauthStateCookie.attributes);
|
|
19
|
-
return ctx.redirect(
|
|
28
|
+
return ctx.redirect(authorizationURL);
|
|
20
29
|
}
|
|
21
|
-
const
|
|
22
|
-
const state = new URL(authorizationURL).searchParams.get("state");
|
|
30
|
+
const state = url.searchParams.get("state");
|
|
23
31
|
if (!state) throw new APIError("BAD_REQUEST", { message: "Unexpected error" });
|
|
24
32
|
const stateCookie = ctx.context.createAuthCookie("state", { maxAge: 300 });
|
|
25
33
|
await ctx.setSignedCookie(stateCookie.name, state, ctx.context.secret, stateCookie.attributes);
|
package/dist/plugins/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/expo",
|
|
3
|
-
"version": "1.7.0-beta.
|
|
3
|
+
"version": "1.7.0-beta.10",
|
|
4
4
|
"description": "Better Auth integration for Expo and React Native applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,28 +58,28 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@better-fetch/fetch": "1.1
|
|
62
|
-
"better-call": "1.3.
|
|
61
|
+
"@better-fetch/fetch": "1.3.1",
|
|
62
|
+
"better-call": "1.3.7",
|
|
63
63
|
"zod": "^4.3.6"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@better-fetch/fetch": "1.1
|
|
67
|
-
"expo-constants": "~
|
|
68
|
-
"expo-linking": "~
|
|
69
|
-
"expo-network": "~
|
|
70
|
-
"expo-web-browser": "~
|
|
71
|
-
"react-native": "~0.
|
|
66
|
+
"@better-fetch/fetch": "1.3.1",
|
|
67
|
+
"expo-constants": "~56.0.18",
|
|
68
|
+
"expo-linking": "~56.0.14",
|
|
69
|
+
"expo-network": "~56.0.5",
|
|
70
|
+
"expo-web-browser": "~56.0.5",
|
|
71
|
+
"react-native": "~0.86.0",
|
|
72
72
|
"tsdown": "0.21.1",
|
|
73
|
-
"@better-auth/core": "1.7.0-beta.
|
|
74
|
-
"better-auth": "1.7.0-beta.
|
|
73
|
+
"@better-auth/core": "1.7.0-beta.10",
|
|
74
|
+
"better-auth": "1.7.0-beta.10"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"expo-constants": ">=17.0.0",
|
|
78
78
|
"expo-linking": ">=7.0.0",
|
|
79
79
|
"expo-network": ">=8.0.7",
|
|
80
80
|
"expo-web-browser": ">=14.0.0",
|
|
81
|
-
"@better-auth/core": "^1.7.0-beta.
|
|
82
|
-
"better-auth": "^1.7.0-beta.
|
|
81
|
+
"@better-auth/core": "^1.7.0-beta.10",
|
|
82
|
+
"better-auth": "^1.7.0-beta.10"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
85
85
|
"expo-constants": {
|