@better-auth/expo 1.3.26 → 1.3.28

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.cjs CHANGED
@@ -112,12 +112,61 @@ function getOrigin(scheme) {
112
112
  const schemeURI = Linking__namespace.createURL("", { scheme });
113
113
  return schemeURI;
114
114
  }
115
+ function hasSessionCookieChanged(prevCookie, newCookie) {
116
+ if (!prevCookie) return true;
117
+ try {
118
+ const prev = JSON.parse(prevCookie);
119
+ const next = JSON.parse(newCookie);
120
+ const sessionKeys = /* @__PURE__ */ new Set();
121
+ Object.keys(prev).forEach((key) => {
122
+ if (key.includes("session_token") || key.includes("session_data")) {
123
+ sessionKeys.add(key);
124
+ }
125
+ });
126
+ Object.keys(next).forEach((key) => {
127
+ if (key.includes("session_token") || key.includes("session_data")) {
128
+ sessionKeys.add(key);
129
+ }
130
+ });
131
+ for (const key of sessionKeys) {
132
+ const prevValue = prev[key]?.value;
133
+ const nextValue = next[key]?.value;
134
+ if (prevValue !== nextValue) {
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ } catch {
140
+ return true;
141
+ }
142
+ }
143
+ function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
144
+ const cookies = parseSetCookieHeader(setCookieHeader);
145
+ const sessionCookieSuffixes = ["session_token", "session_data"];
146
+ for (const name of cookies.keys()) {
147
+ const nameWithoutSecure = name.startsWith("__Secure-") ? name.slice(9) : name;
148
+ for (const suffix of sessionCookieSuffixes) {
149
+ if (cookiePrefix) {
150
+ if (nameWithoutSecure === `${cookiePrefix}.${suffix}`) {
151
+ return true;
152
+ }
153
+ } else {
154
+ if (nameWithoutSecure.endsWith(suffix)) {
155
+ return true;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ return false;
161
+ }
115
162
  const expoClient = (opts) => {
116
163
  let store = null;
117
- const cookieName = `${opts?.storagePrefix || "better-auth"}_cookie`;
118
- const localCacheName = `${opts?.storagePrefix || "better-auth"}_session_data`;
164
+ const storagePrefix = opts?.storagePrefix || "better-auth";
165
+ const cookieName = `${storagePrefix}_cookie`;
166
+ const localCacheName = `${storagePrefix}_session_data`;
119
167
  const storage = opts?.storage;
120
168
  const isWeb = reactNative.Platform.OS === "web";
169
+ const cookiePrefix = opts?.cookiePrefix || "better-auth";
121
170
  const rawScheme = opts?.scheme || Constants__default.expoConfig?.scheme || Constants__default.platform?.scheme;
122
171
  const scheme = Array.isArray(rawScheme) ? rawScheme[0] : rawScheme;
123
172
  if (!scheme && !isWeb) {
@@ -160,13 +209,19 @@ const expoClient = (opts) => {
160
209
  if (isWeb) return;
161
210
  const setCookie = context.response.headers.get("set-cookie");
162
211
  if (setCookie) {
163
- const prevCookie = await storage.getItem(cookieName);
164
- const toSetCookie = getSetCookie(
165
- setCookie || "",
166
- prevCookie ?? void 0
167
- );
168
- await storage.setItem(cookieName, toSetCookie);
169
- store?.notify("$sessionSignal");
212
+ if (hasBetterAuthCookies(setCookie, cookiePrefix)) {
213
+ const prevCookie = await storage.getItem(cookieName);
214
+ const toSetCookie = getSetCookie(
215
+ setCookie || "",
216
+ prevCookie ?? void 0
217
+ );
218
+ if (hasSessionCookieChanged(prevCookie, toSetCookie)) {
219
+ await storage.setItem(cookieName, toSetCookie);
220
+ store?.notify("$sessionSignal");
221
+ } else {
222
+ await storage.setItem(cookieName, toSetCookie);
223
+ }
224
+ }
170
225
  }
171
226
  if (context.request.url.toString().includes("/get-session") && !opts?.disableCache) {
172
227
  const data = context.data;
@@ -263,4 +318,5 @@ const expoClient = (opts) => {
263
318
  exports.expoClient = expoClient;
264
319
  exports.getCookie = getCookie;
265
320
  exports.getSetCookie = getSetCookie;
321
+ exports.hasBetterAuthCookies = hasBetterAuthCookies;
266
322
  exports.parseSetCookieHeader = parseSetCookieHeader;
package/dist/client.d.cts CHANGED
@@ -1,6 +1,6 @@
1
+ import * as better_auth_types from 'better-auth/types';
1
2
  import * as _better_fetch_fetch from '@better-fetch/fetch';
2
3
  import { BetterFetchOption } from '@better-fetch/fetch';
3
- import { Store } from 'better-auth/types';
4
4
 
5
5
  interface CookieAttributes {
6
6
  value: string;
@@ -19,14 +19,41 @@ interface ExpoClientOptions {
19
19
  setItem: (key: string, value: string) => any;
20
20
  getItem: (key: string) => string | null;
21
21
  };
22
+ /**
23
+ * Prefix for local storage keys (e.g., "my-app_cookie", "my-app_session_data")
24
+ * @default "better-auth"
25
+ */
22
26
  storagePrefix?: string;
27
+ /**
28
+ * Prefix for server cookie names to filter (e.g., "better-auth.session_token")
29
+ * This is used to identify which cookies belong to better-auth to prevent
30
+ * infinite refetching when third-party cookies are set.
31
+ * @default "better-auth"
32
+ */
33
+ cookiePrefix?: string;
23
34
  disableCache?: boolean;
24
35
  }
25
36
  declare function getSetCookie(header: string, prevCookie?: string): string;
26
37
  declare function getCookie(cookie: string): string;
38
+ /**
39
+ * Check if the Set-Cookie header contains session-related better-auth cookies.
40
+ * Only triggers session updates when session_token or session_data cookies are present.
41
+ * This prevents infinite refetching when non-session cookies (like third-party cookies) change.
42
+ *
43
+ * Supports multiple cookie naming patterns:
44
+ * - Default: "better-auth.session_token", "__Secure-better-auth.session_token"
45
+ * - Custom prefix: "myapp.session_token", "__Secure-myapp.session_token"
46
+ * - Custom full names: "my_custom_session_token", "custom_session_data"
47
+ * - No prefix (cookiePrefix=""): "session_token", "my_session_token", etc.
48
+ *
49
+ * @param setCookieHeader - The Set-Cookie header value
50
+ * @param cookiePrefix - The cookie prefix to check for. Can be empty string for custom cookie names.
51
+ * @returns true if the header contains session-related cookies, false otherwise
52
+ */
53
+ declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: string): boolean;
27
54
  declare const expoClient: (opts: ExpoClientOptions) => {
28
55
  id: "expo";
29
- getActions(_: _better_fetch_fetch.BetterFetch, $store: Store): {
56
+ getActions(_: _better_fetch_fetch.BetterFetch, $store: better_auth_types.ClientStore): {
30
57
  /**
31
58
  * Get the stored cookie.
32
59
  *
@@ -110,4 +137,4 @@ declare const expoClient: (opts: ExpoClientOptions) => {
110
137
  }[];
111
138
  };
112
139
 
113
- export { expoClient, getCookie, getSetCookie, parseSetCookieHeader };
140
+ export { expoClient, getCookie, getSetCookie, hasBetterAuthCookies, parseSetCookieHeader };
package/dist/client.d.mts CHANGED
@@ -1,6 +1,6 @@
1
+ import * as better_auth_types from 'better-auth/types';
1
2
  import * as _better_fetch_fetch from '@better-fetch/fetch';
2
3
  import { BetterFetchOption } from '@better-fetch/fetch';
3
- import { Store } from 'better-auth/types';
4
4
 
5
5
  interface CookieAttributes {
6
6
  value: string;
@@ -19,14 +19,41 @@ interface ExpoClientOptions {
19
19
  setItem: (key: string, value: string) => any;
20
20
  getItem: (key: string) => string | null;
21
21
  };
22
+ /**
23
+ * Prefix for local storage keys (e.g., "my-app_cookie", "my-app_session_data")
24
+ * @default "better-auth"
25
+ */
22
26
  storagePrefix?: string;
27
+ /**
28
+ * Prefix for server cookie names to filter (e.g., "better-auth.session_token")
29
+ * This is used to identify which cookies belong to better-auth to prevent
30
+ * infinite refetching when third-party cookies are set.
31
+ * @default "better-auth"
32
+ */
33
+ cookiePrefix?: string;
23
34
  disableCache?: boolean;
24
35
  }
25
36
  declare function getSetCookie(header: string, prevCookie?: string): string;
26
37
  declare function getCookie(cookie: string): string;
38
+ /**
39
+ * Check if the Set-Cookie header contains session-related better-auth cookies.
40
+ * Only triggers session updates when session_token or session_data cookies are present.
41
+ * This prevents infinite refetching when non-session cookies (like third-party cookies) change.
42
+ *
43
+ * Supports multiple cookie naming patterns:
44
+ * - Default: "better-auth.session_token", "__Secure-better-auth.session_token"
45
+ * - Custom prefix: "myapp.session_token", "__Secure-myapp.session_token"
46
+ * - Custom full names: "my_custom_session_token", "custom_session_data"
47
+ * - No prefix (cookiePrefix=""): "session_token", "my_session_token", etc.
48
+ *
49
+ * @param setCookieHeader - The Set-Cookie header value
50
+ * @param cookiePrefix - The cookie prefix to check for. Can be empty string for custom cookie names.
51
+ * @returns true if the header contains session-related cookies, false otherwise
52
+ */
53
+ declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: string): boolean;
27
54
  declare const expoClient: (opts: ExpoClientOptions) => {
28
55
  id: "expo";
29
- getActions(_: _better_fetch_fetch.BetterFetch, $store: Store): {
56
+ getActions(_: _better_fetch_fetch.BetterFetch, $store: better_auth_types.ClientStore): {
30
57
  /**
31
58
  * Get the stored cookie.
32
59
  *
@@ -110,4 +137,4 @@ declare const expoClient: (opts: ExpoClientOptions) => {
110
137
  }[];
111
138
  };
112
139
 
113
- export { expoClient, getCookie, getSetCookie, parseSetCookieHeader };
140
+ export { expoClient, getCookie, getSetCookie, hasBetterAuthCookies, parseSetCookieHeader };
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import * as better_auth_types from 'better-auth/types';
1
2
  import * as _better_fetch_fetch from '@better-fetch/fetch';
2
3
  import { BetterFetchOption } from '@better-fetch/fetch';
3
- import { Store } from 'better-auth/types';
4
4
 
5
5
  interface CookieAttributes {
6
6
  value: string;
@@ -19,14 +19,41 @@ interface ExpoClientOptions {
19
19
  setItem: (key: string, value: string) => any;
20
20
  getItem: (key: string) => string | null;
21
21
  };
22
+ /**
23
+ * Prefix for local storage keys (e.g., "my-app_cookie", "my-app_session_data")
24
+ * @default "better-auth"
25
+ */
22
26
  storagePrefix?: string;
27
+ /**
28
+ * Prefix for server cookie names to filter (e.g., "better-auth.session_token")
29
+ * This is used to identify which cookies belong to better-auth to prevent
30
+ * infinite refetching when third-party cookies are set.
31
+ * @default "better-auth"
32
+ */
33
+ cookiePrefix?: string;
23
34
  disableCache?: boolean;
24
35
  }
25
36
  declare function getSetCookie(header: string, prevCookie?: string): string;
26
37
  declare function getCookie(cookie: string): string;
38
+ /**
39
+ * Check if the Set-Cookie header contains session-related better-auth cookies.
40
+ * Only triggers session updates when session_token or session_data cookies are present.
41
+ * This prevents infinite refetching when non-session cookies (like third-party cookies) change.
42
+ *
43
+ * Supports multiple cookie naming patterns:
44
+ * - Default: "better-auth.session_token", "__Secure-better-auth.session_token"
45
+ * - Custom prefix: "myapp.session_token", "__Secure-myapp.session_token"
46
+ * - Custom full names: "my_custom_session_token", "custom_session_data"
47
+ * - No prefix (cookiePrefix=""): "session_token", "my_session_token", etc.
48
+ *
49
+ * @param setCookieHeader - The Set-Cookie header value
50
+ * @param cookiePrefix - The cookie prefix to check for. Can be empty string for custom cookie names.
51
+ * @returns true if the header contains session-related cookies, false otherwise
52
+ */
53
+ declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: string): boolean;
27
54
  declare const expoClient: (opts: ExpoClientOptions) => {
28
55
  id: "expo";
29
- getActions(_: _better_fetch_fetch.BetterFetch, $store: Store): {
56
+ getActions(_: _better_fetch_fetch.BetterFetch, $store: better_auth_types.ClientStore): {
30
57
  /**
31
58
  * Get the stored cookie.
32
59
  *
@@ -110,4 +137,4 @@ declare const expoClient: (opts: ExpoClientOptions) => {
110
137
  }[];
111
138
  };
112
139
 
113
- export { expoClient, getCookie, getSetCookie, parseSetCookieHeader };
140
+ export { expoClient, getCookie, getSetCookie, hasBetterAuthCookies, parseSetCookieHeader };
package/dist/client.mjs CHANGED
@@ -93,12 +93,61 @@ function getOrigin(scheme) {
93
93
  const schemeURI = Linking.createURL("", { scheme });
94
94
  return schemeURI;
95
95
  }
96
+ function hasSessionCookieChanged(prevCookie, newCookie) {
97
+ if (!prevCookie) return true;
98
+ try {
99
+ const prev = JSON.parse(prevCookie);
100
+ const next = JSON.parse(newCookie);
101
+ const sessionKeys = /* @__PURE__ */ new Set();
102
+ Object.keys(prev).forEach((key) => {
103
+ if (key.includes("session_token") || key.includes("session_data")) {
104
+ sessionKeys.add(key);
105
+ }
106
+ });
107
+ Object.keys(next).forEach((key) => {
108
+ if (key.includes("session_token") || key.includes("session_data")) {
109
+ sessionKeys.add(key);
110
+ }
111
+ });
112
+ for (const key of sessionKeys) {
113
+ const prevValue = prev[key]?.value;
114
+ const nextValue = next[key]?.value;
115
+ if (prevValue !== nextValue) {
116
+ return true;
117
+ }
118
+ }
119
+ return false;
120
+ } catch {
121
+ return true;
122
+ }
123
+ }
124
+ function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
125
+ const cookies = parseSetCookieHeader(setCookieHeader);
126
+ const sessionCookieSuffixes = ["session_token", "session_data"];
127
+ for (const name of cookies.keys()) {
128
+ const nameWithoutSecure = name.startsWith("__Secure-") ? name.slice(9) : name;
129
+ for (const suffix of sessionCookieSuffixes) {
130
+ if (cookiePrefix) {
131
+ if (nameWithoutSecure === `${cookiePrefix}.${suffix}`) {
132
+ return true;
133
+ }
134
+ } else {
135
+ if (nameWithoutSecure.endsWith(suffix)) {
136
+ return true;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ return false;
142
+ }
96
143
  const expoClient = (opts) => {
97
144
  let store = null;
98
- const cookieName = `${opts?.storagePrefix || "better-auth"}_cookie`;
99
- const localCacheName = `${opts?.storagePrefix || "better-auth"}_session_data`;
145
+ const storagePrefix = opts?.storagePrefix || "better-auth";
146
+ const cookieName = `${storagePrefix}_cookie`;
147
+ const localCacheName = `${storagePrefix}_session_data`;
100
148
  const storage = opts?.storage;
101
149
  const isWeb = Platform.OS === "web";
150
+ const cookiePrefix = opts?.cookiePrefix || "better-auth";
102
151
  const rawScheme = opts?.scheme || Constants.expoConfig?.scheme || Constants.platform?.scheme;
103
152
  const scheme = Array.isArray(rawScheme) ? rawScheme[0] : rawScheme;
104
153
  if (!scheme && !isWeb) {
@@ -141,13 +190,19 @@ const expoClient = (opts) => {
141
190
  if (isWeb) return;
142
191
  const setCookie = context.response.headers.get("set-cookie");
143
192
  if (setCookie) {
144
- const prevCookie = await storage.getItem(cookieName);
145
- const toSetCookie = getSetCookie(
146
- setCookie || "",
147
- prevCookie ?? void 0
148
- );
149
- await storage.setItem(cookieName, toSetCookie);
150
- store?.notify("$sessionSignal");
193
+ if (hasBetterAuthCookies(setCookie, cookiePrefix)) {
194
+ const prevCookie = await storage.getItem(cookieName);
195
+ const toSetCookie = getSetCookie(
196
+ setCookie || "",
197
+ prevCookie ?? void 0
198
+ );
199
+ if (hasSessionCookieChanged(prevCookie, toSetCookie)) {
200
+ await storage.setItem(cookieName, toSetCookie);
201
+ store?.notify("$sessionSignal");
202
+ } else {
203
+ await storage.setItem(cookieName, toSetCookie);
204
+ }
205
+ }
151
206
  }
152
207
  if (context.request.url.toString().includes("/get-session") && !opts?.disableCache) {
153
208
  const data = context.data;
@@ -241,4 +296,4 @@ const expoClient = (opts) => {
241
296
  };
242
297
  };
243
298
 
244
- export { expoClient, getCookie, getSetCookie, parseSetCookieHeader };
299
+ export { expoClient, getCookie, getSetCookie, hasBetterAuthCookies, parseSetCookieHeader };
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as better_call from 'better-call';
2
- import * as better_auth_types from 'better-auth/types';
2
+ import * as better_auth from 'better-auth';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface ExpoOptions {
@@ -10,17 +10,23 @@ interface ExpoOptions {
10
10
  }
11
11
  declare const expo: (options?: ExpoOptions) => {
12
12
  id: "expo";
13
- init: (ctx: better_auth_types.AuthContext) => {
13
+ init: (ctx: better_auth.AuthContext) => {
14
14
  options: {
15
15
  trustedOrigins: string[];
16
16
  };
17
17
  };
18
- onRequest(request: Request, ctx: better_auth_types.AuthContext): Promise<{
18
+ onRequest(request: Request, ctx: better_auth.AuthContext): Promise<{
19
19
  request: Request;
20
20
  } | undefined>;
21
21
  hooks: {
22
22
  after: {
23
- matcher(context: better_auth_types.HookEndpointContext): boolean;
23
+ matcher(context: better_call.EndpointContext<string, any> & Omit<better_call.InputContext<string, any>, "method"> & {
24
+ context: better_auth.AuthContext & {
25
+ returned?: unknown;
26
+ responseHeaders?: Headers;
27
+ };
28
+ headers?: Headers;
29
+ }): boolean;
24
30
  handler: (inputContext: better_call.MiddlewareInputContext<better_call.MiddlewareOptions>) => Promise<void>;
25
31
  }[];
26
32
  };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as better_call from 'better-call';
2
- import * as better_auth_types from 'better-auth/types';
2
+ import * as better_auth from 'better-auth';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface ExpoOptions {
@@ -10,17 +10,23 @@ interface ExpoOptions {
10
10
  }
11
11
  declare const expo: (options?: ExpoOptions) => {
12
12
  id: "expo";
13
- init: (ctx: better_auth_types.AuthContext) => {
13
+ init: (ctx: better_auth.AuthContext) => {
14
14
  options: {
15
15
  trustedOrigins: string[];
16
16
  };
17
17
  };
18
- onRequest(request: Request, ctx: better_auth_types.AuthContext): Promise<{
18
+ onRequest(request: Request, ctx: better_auth.AuthContext): Promise<{
19
19
  request: Request;
20
20
  } | undefined>;
21
21
  hooks: {
22
22
  after: {
23
- matcher(context: better_auth_types.HookEndpointContext): boolean;
23
+ matcher(context: better_call.EndpointContext<string, any> & Omit<better_call.InputContext<string, any>, "method"> & {
24
+ context: better_auth.AuthContext & {
25
+ returned?: unknown;
26
+ responseHeaders?: Headers;
27
+ };
28
+ headers?: Headers;
29
+ }): boolean;
24
30
  handler: (inputContext: better_call.MiddlewareInputContext<better_call.MiddlewareOptions>) => Promise<void>;
25
31
  }[];
26
32
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as better_call from 'better-call';
2
- import * as better_auth_types from 'better-auth/types';
2
+ import * as better_auth from 'better-auth';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface ExpoOptions {
@@ -10,17 +10,23 @@ interface ExpoOptions {
10
10
  }
11
11
  declare const expo: (options?: ExpoOptions) => {
12
12
  id: "expo";
13
- init: (ctx: better_auth_types.AuthContext) => {
13
+ init: (ctx: better_auth.AuthContext) => {
14
14
  options: {
15
15
  trustedOrigins: string[];
16
16
  };
17
17
  };
18
- onRequest(request: Request, ctx: better_auth_types.AuthContext): Promise<{
18
+ onRequest(request: Request, ctx: better_auth.AuthContext): Promise<{
19
19
  request: Request;
20
20
  } | undefined>;
21
21
  hooks: {
22
22
  after: {
23
- matcher(context: better_auth_types.HookEndpointContext): boolean;
23
+ matcher(context: better_call.EndpointContext<string, any> & Omit<better_call.InputContext<string, any>, "method"> & {
24
+ context: better_auth.AuthContext & {
25
+ returned?: unknown;
26
+ responseHeaders?: Headers;
27
+ };
28
+ headers?: Headers;
29
+ }): boolean;
24
30
  handler: (inputContext: better_call.MiddlewareInputContext<better_call.MiddlewareOptions>) => Promise<void>;
25
31
  }[];
26
32
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/expo",
3
- "version": "1.3.26",
3
+ "version": "1.3.28",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "license": "MIT",
46
46
  "devDependencies": {
47
- "@better-fetch/fetch": "^1.1.18",
47
+ "@better-fetch/fetch": "1.1.18",
48
48
  "@types/better-sqlite3": "^7.6.13",
49
49
  "better-sqlite3": "^12.2.0",
50
50
  "expo-constants": "~17.1.7",
@@ -54,7 +54,7 @@
54
54
  "expo-web-browser": "~14.2.0",
55
55
  "react-native": "~0.80.2",
56
56
  "unbuild": "^3.6.1",
57
- "better-auth": "1.3.26"
57
+ "better-auth": "1.3.28"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "expo-constants": ">=17.0.0",
@@ -62,10 +62,10 @@
62
62
  "expo-linking": ">=7.0.0",
63
63
  "expo-secure-store": ">=14.0.0",
64
64
  "expo-web-browser": ">=14.0.0",
65
- "better-auth": "1.3.26"
65
+ "better-auth": "1.3.28"
66
66
  },
67
67
  "dependencies": {
68
- "@better-fetch/fetch": "^1.1.18",
68
+ "@better-fetch/fetch": "1.1.18",
69
69
  "zod": "^4.1.5"
70
70
  },
71
71
  "files": [