@imtbl/auth-next-server 2.17.0 → 2.17.1-alpha.1

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.
@@ -124,7 +124,8 @@ function extractZkEvmFromIdToken(idToken) {
124
124
  }
125
125
  return void 0;
126
126
  }
127
- async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_AUTH_DOMAIN) {
127
+ var refreshPromises = /* @__PURE__ */ new Map();
128
+ async function doRefreshAccessToken(refreshToken, clientId, authDomain) {
128
129
  const tokenUrl = `${authDomain}/oauth/token`;
129
130
  const response = await fetch(tokenUrl, {
130
131
  method: "POST",
@@ -163,6 +164,16 @@ async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_A
163
164
  accessTokenExpires: decodeJwtExpiry(tokenData.access_token)
164
165
  };
165
166
  }
167
+ async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_AUTH_DOMAIN) {
168
+ const cacheKey = `${clientId}:${refreshToken}`;
169
+ const inflight = refreshPromises.get(cacheKey);
170
+ if (inflight) return inflight;
171
+ const promise = doRefreshAccessToken(refreshToken, clientId, authDomain).finally(() => {
172
+ refreshPromises.delete(cacheKey);
173
+ });
174
+ refreshPromises.set(cacheKey, promise);
175
+ return promise;
176
+ }
166
177
 
167
178
  // src/config.ts
168
179
  var Credentials = import_credentials.default.default || import_credentials.default;
@@ -67,7 +67,8 @@ function extractZkEvmFromIdToken(idToken) {
67
67
  }
68
68
  return void 0;
69
69
  }
70
- async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_AUTH_DOMAIN) {
70
+ var refreshPromises = /* @__PURE__ */ new Map();
71
+ async function doRefreshAccessToken(refreshToken, clientId, authDomain) {
71
72
  const tokenUrl = `${authDomain}/oauth/token`;
72
73
  const response = await fetch(tokenUrl, {
73
74
  method: "POST",
@@ -106,6 +107,16 @@ async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_A
106
107
  accessTokenExpires: decodeJwtExpiry(tokenData.access_token)
107
108
  };
108
109
  }
110
+ async function refreshAccessToken(refreshToken, clientId, authDomain = DEFAULT_AUTH_DOMAIN) {
111
+ const cacheKey = `${clientId}:${refreshToken}`;
112
+ const inflight = refreshPromises.get(cacheKey);
113
+ if (inflight) return inflight;
114
+ const promise = doRefreshAccessToken(refreshToken, clientId, authDomain).finally(() => {
115
+ refreshPromises.delete(cacheKey);
116
+ });
117
+ refreshPromises.set(cacheKey, promise);
118
+ return promise;
119
+ }
109
120
 
110
121
  // src/config.ts
111
122
  var Credentials = CredentialsImport.default || CredentialsImport;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imtbl/auth-next-server",
3
- "version": "2.17.0",
3
+ "version": "2.17.1-alpha.1",
4
4
  "description": "Immutable Auth.js v5 integration for Next.js - Server-side utilities",
5
5
  "author": "Immutable",
6
6
  "license": "Apache-2.0",
package/src/refresh.ts CHANGED
@@ -85,20 +85,16 @@ export function extractZkEvmFromIdToken(idToken?: string): ZkEvmData | undefined
85
85
  return undefined;
86
86
  }
87
87
 
88
- /**
89
- * Refresh access token using the refresh token.
90
- * This is called server-side in the JWT callback when the access token is expired.
91
- *
92
- * @param refreshToken - The refresh token to use
93
- * @param clientId - The OAuth client ID
94
- * @param authDomain - The authentication domain (default: https://auth.immutable.com)
95
- * @returns The refreshed tokens
96
- * @throws Error if refresh fails
97
- */
98
- export async function refreshAccessToken(
88
+ // Deduplicates concurrent refresh calls for the same refresh token.
89
+ // OAuth refresh tokens are single-use: if two requests arrive simultaneously
90
+ // with the same expired token, only the first call reaches the provider.
91
+ // Subsequent callers await the same promise and receive the same result.
92
+ const refreshPromises = new Map<string, Promise<RefreshedTokens>>();
93
+
94
+ async function doRefreshAccessToken(
99
95
  refreshToken: string,
100
96
  clientId: string,
101
- authDomain: string = DEFAULT_AUTH_DOMAIN,
97
+ authDomain: string,
102
98
  ): Promise<RefreshedTokens> {
103
99
  const tokenUrl = `${authDomain}/oauth/token`;
104
100
 
@@ -141,3 +137,29 @@ export async function refreshAccessToken(
141
137
  accessTokenExpires: decodeJwtExpiry(tokenData.access_token),
142
138
  };
143
139
  }
140
+
141
+ /**
142
+ * Refresh access token using the refresh token.
143
+ * This is called server-side in the JWT callback when the access token is expired.
144
+ *
145
+ * @param refreshToken - The refresh token to use
146
+ * @param clientId - The OAuth client ID
147
+ * @param authDomain - The authentication domain (default: https://auth.immutable.com)
148
+ * @returns The refreshed tokens
149
+ * @throws Error if refresh fails
150
+ */
151
+ export async function refreshAccessToken(
152
+ refreshToken: string,
153
+ clientId: string,
154
+ authDomain: string = DEFAULT_AUTH_DOMAIN,
155
+ ): Promise<RefreshedTokens> {
156
+ const cacheKey = `${clientId}:${refreshToken}`;
157
+ const inflight = refreshPromises.get(cacheKey);
158
+ if (inflight) return inflight;
159
+
160
+ const promise = doRefreshAccessToken(refreshToken, clientId, authDomain).finally(() => {
161
+ refreshPromises.delete(cacheKey);
162
+ });
163
+ refreshPromises.set(cacheKey, promise);
164
+ return promise;
165
+ }