@payez/next-mvp 4.0.42 → 4.0.44

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.
@@ -27,6 +27,13 @@ export declare function buildBetterAuthProviders(config: IDPClientConfig): Recor
27
27
  * Use to add credentials/custom signin endpoints from the host app.
28
28
  */
29
29
  export type BetterAuthExtraPlugins = any[];
30
+ /**
31
+ * Optional overrides for createBetterAuthInstance.
32
+ */
33
+ export interface BetterAuthInstanceOptions {
34
+ /** Path suffix for the BA mount (default: '/api/auth'). Use '/api/ba-auth' for migration scenarios. */
35
+ basePath?: string;
36
+ }
30
37
  /**
31
38
  * Create Better Auth instance from IDP config.
32
39
  *
@@ -35,8 +42,9 @@ export type BetterAuthExtraPlugins = any[];
35
42
  *
36
43
  * @param idpConfig IDP client config (from getIDPClientConfig)
37
44
  * @param extraPlugins Optional plugins to add (e.g., credentials plugin from host app)
45
+ * @param options Optional overrides (e.g., basePath for migration scenarios)
38
46
  */
39
- export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, extraPlugins?: BetterAuthExtraPlugins): import("better-auth").Auth<{
47
+ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, extraPlugins?: BetterAuthExtraPlugins, options?: BetterAuthInstanceOptions): import("better-auth").Auth<{
40
48
  baseURL: string;
41
49
  secret: string;
42
50
  socialProviders: Record<string, BetterAuthSocialProvider>;
@@ -83,15 +83,17 @@ function buildBetterAuthProviders(config) {
83
83
  *
84
84
  * @param idpConfig IDP client config (from getIDPClientConfig)
85
85
  * @param extraPlugins Optional plugins to add (e.g., credentials plugin from host app)
86
+ * @param options Optional overrides (e.g., basePath for migration scenarios)
86
87
  */
87
- function createBetterAuthInstance(idpConfig, extraPlugins = []) {
88
+ function createBetterAuthInstance(idpConfig, extraPlugins = [], options = {}) {
88
89
  const appSlug = idpConfig.clientSlug || (0, app_slug_1.getAppSlug)();
90
+ const basePath = options.basePath || '/api/auth';
89
91
  // Resolve base URL: BETTER_AUTH_URL env > IDP config > localhost fallback
90
- // Must include /api/auth since that's where the catch-all route is mounted
92
+ // basePath defaults to /api/auth but can be overridden via options for migration scenarios
91
93
  const rawBaseURL = process.env.BETTER_AUTH_URL
92
94
  || idpConfig.baseClientUrl
93
95
  || `http://localhost:${process.env.PORT || '3000'}`;
94
- const baseURL = rawBaseURL.replace(/\/+$/, '') + '/api/auth';
96
+ const baseURL = rawBaseURL.replace(/\/+$/, '') + basePath;
95
97
  return (0, better_auth_1.betterAuth)({
96
98
  baseURL,
97
99
  secret: idpConfig.nextAuthSecret,
@@ -183,12 +183,18 @@ class ApiHandler {
183
183
  }
184
184
  }
185
185
  catch { /* ignore */ }
186
- // Check if token needs refresh
187
- // Skip entirely if there's no refresh token nothing to refresh with
186
+ // Check if token needs refresh.
187
+ // Skip the optimization (hasRefreshToken check) when access token is missing
188
+ // some session shapes store the refresh token under different field names,
189
+ // and we still want a refresh attempt to populate the access token.
188
190
  const thresholdMs = 5 * 60 * 1000;
189
191
  const expires = sessionData.idpAccessTokenExpires || 0;
190
- const hasRefreshToken = !!sessionData.idpRefreshToken;
191
- const needsRefresh = hasRefreshToken && (!accessToken || (expires - Date.now()) <= thresholdMs);
192
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
+ const hasRefreshToken = !!(sessionData.idpRefreshToken || sessionData.refreshToken);
194
+ const accessTokenStale = !accessToken || (expires - Date.now()) <= thresholdMs;
195
+ // If we already have a fresh access token, skip refresh entirely (no lock).
196
+ // If we don't, only attempt refresh when we have a refresh token to use.
197
+ const needsRefresh = accessTokenStale && hasRefreshToken;
192
198
  if (needsRefresh) {
193
199
  const refreshResult = await this.handleCoordinatedRefresh(req, token, sessionData, ctx);
194
200
  if (refreshResult.blocked) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payez/next-mvp",
3
- "version": "4.0.42",
3
+ "version": "4.0.44",
4
4
  "sideEffects": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -55,6 +55,14 @@ export function buildBetterAuthProviders(
55
55
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
56
  export type BetterAuthExtraPlugins = any[];
57
57
 
58
+ /**
59
+ * Optional overrides for createBetterAuthInstance.
60
+ */
61
+ export interface BetterAuthInstanceOptions {
62
+ /** Path suffix for the BA mount (default: '/api/auth'). Use '/api/ba-auth' for migration scenarios. */
63
+ basePath?: string;
64
+ }
65
+
58
66
  /**
59
67
  * Create Better Auth instance from IDP config.
60
68
  *
@@ -63,16 +71,22 @@ export type BetterAuthExtraPlugins = any[];
63
71
  *
64
72
  * @param idpConfig IDP client config (from getIDPClientConfig)
65
73
  * @param extraPlugins Optional plugins to add (e.g., credentials plugin from host app)
74
+ * @param options Optional overrides (e.g., basePath for migration scenarios)
66
75
  */
67
- export function createBetterAuthInstance(idpConfig: IDPClientConfig, extraPlugins: BetterAuthExtraPlugins = []) {
76
+ export function createBetterAuthInstance(
77
+ idpConfig: IDPClientConfig,
78
+ extraPlugins: BetterAuthExtraPlugins = [],
79
+ options: BetterAuthInstanceOptions = {}
80
+ ) {
68
81
  const appSlug = idpConfig.clientSlug || getAppSlug();
82
+ const basePath = options.basePath || '/api/auth';
69
83
 
70
84
  // Resolve base URL: BETTER_AUTH_URL env > IDP config > localhost fallback
71
- // Must include /api/auth since that's where the catch-all route is mounted
85
+ // basePath defaults to /api/auth but can be overridden via options for migration scenarios
72
86
  const rawBaseURL = process.env.BETTER_AUTH_URL
73
87
  || idpConfig.baseClientUrl
74
88
  || `http://localhost:${process.env.PORT || '3000'}`;
75
- const baseURL = rawBaseURL.replace(/\/+$/, '') + '/api/auth';
89
+ const baseURL = rawBaseURL.replace(/\/+$/, '') + basePath;
76
90
 
77
91
  return betterAuth({
78
92
  baseURL,
@@ -278,12 +278,18 @@ export class ApiHandler {
278
278
  }
279
279
  } catch { /* ignore */ }
280
280
 
281
- // Check if token needs refresh
282
- // Skip entirely if there's no refresh token nothing to refresh with
281
+ // Check if token needs refresh.
282
+ // Skip the optimization (hasRefreshToken check) when access token is missing
283
+ // some session shapes store the refresh token under different field names,
284
+ // and we still want a refresh attempt to populate the access token.
283
285
  const thresholdMs = 5 * 60 * 1000;
284
286
  const expires = sessionData.idpAccessTokenExpires || 0;
285
- const hasRefreshToken = !!sessionData.idpRefreshToken;
286
- const needsRefresh = hasRefreshToken && (!accessToken || (expires - Date.now()) <= thresholdMs);
287
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
288
+ const hasRefreshToken = !!(sessionData.idpRefreshToken || (sessionData as any).refreshToken);
289
+ const accessTokenStale = !accessToken || (expires - Date.now()) <= thresholdMs;
290
+ // If we already have a fresh access token, skip refresh entirely (no lock).
291
+ // If we don't, only attempt refresh when we have a refresh token to use.
292
+ const needsRefresh = accessTokenStale && hasRefreshToken;
287
293
 
288
294
  if (needsRefresh) {
289
295
  const refreshResult = await this.handleCoordinatedRefresh(req, token, sessionData, ctx);