@scalemule/nextjs 0.1.16 → 0.1.18
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.mts +13 -1
- package/dist/client.d.ts +13 -1
- package/dist/client.js +32 -0
- package/dist/client.mjs +32 -0
- package/dist/index.js +32 -0
- package/dist/index.mjs +32 -0
- package/dist/server/auth.js +130 -21
- package/dist/server/auth.mjs +130 -21
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +140 -26
- package/dist/server/index.mjs +140 -26
- package/dist/server/ledvery.js +10 -5
- package/dist/server/ledvery.mjs +10 -5
- package/dist/server/webhook-handler.d.mts +1 -1
- package/dist/server/webhook-handler.d.ts +1 -1
- package/dist/{webhook-handler-C07AK4QB.d.ts → webhook-handler-CTx4Uh8u.d.ts} +33 -7
- package/dist/{webhook-handler-CUp_A0_p.d.mts → webhook-handler-DsxJS_He.d.mts} +33 -7
- package/package.json +2 -2
package/dist/server/auth.mjs
CHANGED
|
@@ -130,23 +130,31 @@ var ScaleMuleServer = class {
|
|
|
130
130
|
/**
|
|
131
131
|
* Logout user
|
|
132
132
|
*/
|
|
133
|
-
logout: async (sessionToken) => {
|
|
133
|
+
logout: async (sessionToken, options) => {
|
|
134
134
|
return this.request("POST", "/v1/auth/logout", {
|
|
135
|
-
|
|
135
|
+
sessionToken,
|
|
136
|
+
body: { session_token: sessionToken },
|
|
137
|
+
onTokenRotated: options?.onTokenRotated
|
|
136
138
|
});
|
|
137
139
|
},
|
|
138
140
|
/**
|
|
139
141
|
* Get current user from session token
|
|
140
142
|
*/
|
|
141
|
-
me: async (sessionToken) => {
|
|
142
|
-
return this.request("GET", "/v1/auth/me", {
|
|
143
|
+
me: async (sessionToken, options) => {
|
|
144
|
+
return this.request("GET", "/v1/auth/me", {
|
|
145
|
+
sessionToken,
|
|
146
|
+
onTokenRotated: options?.onTokenRotated
|
|
147
|
+
});
|
|
143
148
|
},
|
|
144
149
|
/**
|
|
145
150
|
* Refresh session token
|
|
146
151
|
*/
|
|
147
|
-
refresh: async (sessionToken) => {
|
|
152
|
+
refresh: async (sessionToken, options) => {
|
|
148
153
|
return this.request("POST", "/v1/auth/refresh", {
|
|
149
|
-
|
|
154
|
+
sessionToken,
|
|
155
|
+
clientContext: options?.clientContext,
|
|
156
|
+
isAutoRefresh: options?.isAutoRefresh,
|
|
157
|
+
onTokenRotated: options?.onTokenRotated
|
|
150
158
|
});
|
|
151
159
|
},
|
|
152
160
|
/**
|
|
@@ -178,7 +186,8 @@ var ScaleMuleServer = class {
|
|
|
178
186
|
if (options?.email) {
|
|
179
187
|
return this.request("POST", "/v1/auth/resend-verification", {
|
|
180
188
|
sessionToken: sessionTokenOrEmail,
|
|
181
|
-
body: { email: options.email }
|
|
189
|
+
body: { email: options.email },
|
|
190
|
+
onTokenRotated: options?.onTokenRotated
|
|
182
191
|
});
|
|
183
192
|
}
|
|
184
193
|
if (sessionTokenOrEmail.includes("@")) {
|
|
@@ -187,7 +196,8 @@ var ScaleMuleServer = class {
|
|
|
187
196
|
});
|
|
188
197
|
}
|
|
189
198
|
return this.request("POST", "/v1/auth/resend-verification", {
|
|
190
|
-
sessionToken: sessionTokenOrEmail
|
|
199
|
+
sessionToken: sessionTokenOrEmail,
|
|
200
|
+
onTokenRotated: options?.onTokenRotated
|
|
191
201
|
});
|
|
192
202
|
}
|
|
193
203
|
};
|
|
@@ -198,37 +208,41 @@ var ScaleMuleServer = class {
|
|
|
198
208
|
/**
|
|
199
209
|
* Update user profile
|
|
200
210
|
*/
|
|
201
|
-
update: async (sessionToken, data) => {
|
|
211
|
+
update: async (sessionToken, data, options) => {
|
|
202
212
|
return this.request("PATCH", "/v1/auth/profile", {
|
|
203
213
|
sessionToken,
|
|
204
|
-
body: data
|
|
214
|
+
body: data,
|
|
215
|
+
onTokenRotated: options?.onTokenRotated
|
|
205
216
|
});
|
|
206
217
|
},
|
|
207
218
|
/**
|
|
208
219
|
* Change password
|
|
209
220
|
*/
|
|
210
|
-
changePassword: async (sessionToken, currentPassword, newPassword) => {
|
|
221
|
+
changePassword: async (sessionToken, currentPassword, newPassword, options) => {
|
|
211
222
|
return this.request("POST", "/v1/auth/change-password", {
|
|
212
223
|
sessionToken,
|
|
213
|
-
body: { current_password: currentPassword, new_password: newPassword }
|
|
224
|
+
body: { current_password: currentPassword, new_password: newPassword },
|
|
225
|
+
onTokenRotated: options?.onTokenRotated
|
|
214
226
|
});
|
|
215
227
|
},
|
|
216
228
|
/**
|
|
217
229
|
* Change email
|
|
218
230
|
*/
|
|
219
|
-
changeEmail: async (sessionToken, newEmail, password) => {
|
|
231
|
+
changeEmail: async (sessionToken, newEmail, password, options) => {
|
|
220
232
|
return this.request("POST", "/v1/auth/change-email", {
|
|
221
233
|
sessionToken,
|
|
222
|
-
body: { new_email: newEmail, password }
|
|
234
|
+
body: { new_email: newEmail, password },
|
|
235
|
+
onTokenRotated: options?.onTokenRotated
|
|
223
236
|
});
|
|
224
237
|
},
|
|
225
238
|
/**
|
|
226
239
|
* Delete account
|
|
227
240
|
*/
|
|
228
|
-
deleteAccount: async (sessionToken, password) => {
|
|
241
|
+
deleteAccount: async (sessionToken, password, options) => {
|
|
229
242
|
return this.request("DELETE", "/v1/auth/me", {
|
|
230
243
|
sessionToken,
|
|
231
|
-
body: { password }
|
|
244
|
+
body: { password },
|
|
245
|
+
onTokenRotated: options?.onTokenRotated
|
|
232
246
|
});
|
|
233
247
|
}
|
|
234
248
|
};
|
|
@@ -689,6 +703,9 @@ var ScaleMuleServer = class {
|
|
|
689
703
|
this.apiKey = config.apiKey;
|
|
690
704
|
this.gatewayUrl = resolveGatewayUrl(config);
|
|
691
705
|
this.debug = config.debug || false;
|
|
706
|
+
this.onRefreshStart = config.onRefreshStart;
|
|
707
|
+
this.onRefreshEnd = config.onRefreshEnd;
|
|
708
|
+
this.onAutoRefreshFailed = config.onAutoRefreshFailed;
|
|
692
709
|
this.money = createMoneyClient({
|
|
693
710
|
apiKey: this.apiKey,
|
|
694
711
|
gatewayUrl: this.gatewayUrl,
|
|
@@ -732,6 +749,10 @@ var ScaleMuleServer = class {
|
|
|
732
749
|
headers,
|
|
733
750
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
734
751
|
});
|
|
752
|
+
const rotated = response.headers.get("x-rotated-session-token");
|
|
753
|
+
if (rotated && options.onTokenRotated) {
|
|
754
|
+
options.onTokenRotated(rotated);
|
|
755
|
+
}
|
|
735
756
|
const text = await response.text();
|
|
736
757
|
let responseData = null;
|
|
737
758
|
try {
|
|
@@ -743,6 +764,33 @@ var ScaleMuleServer = class {
|
|
|
743
764
|
code: `HTTP_${response.status}`,
|
|
744
765
|
message: responseData?.message || text || response.statusText
|
|
745
766
|
};
|
|
767
|
+
if (response.status === 401 && options.sessionToken && !options.isAutoRefresh) {
|
|
768
|
+
if (this.debug) console.log("[ScaleMule Server] 401 received, attempting auto-refresh...");
|
|
769
|
+
try {
|
|
770
|
+
this.onRefreshStart?.();
|
|
771
|
+
const refreshData = await this.auth.refresh(options.sessionToken, {
|
|
772
|
+
clientContext: options.clientContext,
|
|
773
|
+
isAutoRefresh: true
|
|
774
|
+
// Prevent infinite loops
|
|
775
|
+
});
|
|
776
|
+
const newToken = refreshData.session_token;
|
|
777
|
+
if (this.debug) console.log("[ScaleMule Server] Auto-refresh succeeded, retrying original request...");
|
|
778
|
+
options.onTokenRotated?.(newToken);
|
|
779
|
+
return this.request(method, path, {
|
|
780
|
+
...options,
|
|
781
|
+
sessionToken: newToken,
|
|
782
|
+
isAutoRefresh: true
|
|
783
|
+
// Don't refresh again on this retry
|
|
784
|
+
});
|
|
785
|
+
} catch (refreshErr) {
|
|
786
|
+
if (this.debug) console.error("[ScaleMule Server] Auto-refresh failed:", refreshErr);
|
|
787
|
+
const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
|
|
788
|
+
this.onAutoRefreshFailed?.(refreshApiError);
|
|
789
|
+
throw new ScaleMuleApiError(error);
|
|
790
|
+
} finally {
|
|
791
|
+
this.onRefreshEnd?.();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
746
794
|
throw new ScaleMuleApiError(error);
|
|
747
795
|
}
|
|
748
796
|
const data = responseData?.data !== void 0 ? responseData.data : responseData;
|
|
@@ -1171,8 +1219,16 @@ function createAuthRoutes(config = {}) {
|
|
|
1171
1219
|
// ==================== Logout ====================
|
|
1172
1220
|
case "logout": {
|
|
1173
1221
|
const session = await getSession();
|
|
1222
|
+
let rotated = null;
|
|
1174
1223
|
if (session) {
|
|
1175
|
-
|
|
1224
|
+
try {
|
|
1225
|
+
await sm.auth.logout(session.sessionToken, {
|
|
1226
|
+
onTokenRotated: (newToken) => {
|
|
1227
|
+
rotated = newToken;
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
} catch {
|
|
1231
|
+
}
|
|
1176
1232
|
}
|
|
1177
1233
|
if (config.onLogout) {
|
|
1178
1234
|
await config.onLogout();
|
|
@@ -1237,6 +1293,7 @@ function createAuthRoutes(config = {}) {
|
|
|
1237
1293
|
case "resend-verification": {
|
|
1238
1294
|
const { email } = body;
|
|
1239
1295
|
const session = await getSession();
|
|
1296
|
+
let rotated = null;
|
|
1240
1297
|
if (email) {
|
|
1241
1298
|
try {
|
|
1242
1299
|
await sm.auth.resendVerification(email);
|
|
@@ -1254,7 +1311,11 @@ function createAuthRoutes(config = {}) {
|
|
|
1254
1311
|
return errorResponse("UNAUTHORIZED", "Email or session required", 401);
|
|
1255
1312
|
}
|
|
1256
1313
|
try {
|
|
1257
|
-
await sm.auth.resendVerification(session.sessionToken
|
|
1314
|
+
await sm.auth.resendVerification(session.sessionToken, {
|
|
1315
|
+
onTokenRotated: (newToken) => {
|
|
1316
|
+
rotated = newToken;
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1258
1319
|
} catch (err) {
|
|
1259
1320
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
1260
1321
|
return errorResponse(
|
|
@@ -1263,6 +1324,14 @@ function createAuthRoutes(config = {}) {
|
|
|
1263
1324
|
400
|
|
1264
1325
|
);
|
|
1265
1326
|
}
|
|
1327
|
+
if (rotated) {
|
|
1328
|
+
return withRefreshedSession(
|
|
1329
|
+
rotated,
|
|
1330
|
+
session.userId,
|
|
1331
|
+
{ success: true, data: { message: "Verification email sent" } },
|
|
1332
|
+
cookieOptions
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1266
1335
|
return successResponse({ message: "Verification email sent" });
|
|
1267
1336
|
}
|
|
1268
1337
|
// ==================== Refresh Session ====================
|
|
@@ -1297,11 +1366,17 @@ function createAuthRoutes(config = {}) {
|
|
|
1297
1366
|
if (!current_password || !new_password) {
|
|
1298
1367
|
return errorResponse("VALIDATION_ERROR", "Current and new password required", 400);
|
|
1299
1368
|
}
|
|
1369
|
+
let rotated = null;
|
|
1300
1370
|
try {
|
|
1301
1371
|
await sm.user.changePassword(
|
|
1302
1372
|
session.sessionToken,
|
|
1303
1373
|
current_password,
|
|
1304
|
-
new_password
|
|
1374
|
+
new_password,
|
|
1375
|
+
{
|
|
1376
|
+
onTokenRotated: (newToken) => {
|
|
1377
|
+
rotated = newToken;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1305
1380
|
);
|
|
1306
1381
|
} catch (err) {
|
|
1307
1382
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
@@ -1311,6 +1386,14 @@ function createAuthRoutes(config = {}) {
|
|
|
1311
1386
|
400
|
|
1312
1387
|
);
|
|
1313
1388
|
}
|
|
1389
|
+
if (rotated) {
|
|
1390
|
+
return withRefreshedSession(
|
|
1391
|
+
rotated,
|
|
1392
|
+
session.userId,
|
|
1393
|
+
{ success: true, data: { message: "Password changed successfully" } },
|
|
1394
|
+
cookieOptions
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1314
1397
|
return successResponse({ message: "Password changed successfully" });
|
|
1315
1398
|
}
|
|
1316
1399
|
// ==================== Switch Account ====================
|
|
@@ -1391,14 +1474,27 @@ function createAuthRoutes(config = {}) {
|
|
|
1391
1474
|
return withNorm(errorResponse("UNAUTHORIZED", "Authentication required", 401));
|
|
1392
1475
|
}
|
|
1393
1476
|
let userData;
|
|
1477
|
+
let rotated = null;
|
|
1394
1478
|
try {
|
|
1395
|
-
userData = await sm.auth.me(session.sessionToken
|
|
1479
|
+
userData = await sm.auth.me(session.sessionToken, {
|
|
1480
|
+
onTokenRotated: (newToken) => {
|
|
1481
|
+
rotated = newToken;
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1396
1484
|
} catch {
|
|
1397
1485
|
return withNorm(clearSession(
|
|
1398
1486
|
{ error: { code: "SESSION_EXPIRED", message: "Session expired" } },
|
|
1399
1487
|
cookieOptions
|
|
1400
1488
|
));
|
|
1401
1489
|
}
|
|
1490
|
+
if (rotated) {
|
|
1491
|
+
return withNorm(withRefreshedSession(
|
|
1492
|
+
rotated,
|
|
1493
|
+
session.userId,
|
|
1494
|
+
{ user: userData, sessionToken: rotated, userId: session.userId },
|
|
1495
|
+
cookieOptions
|
|
1496
|
+
));
|
|
1497
|
+
}
|
|
1402
1498
|
return withNorm(successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId }));
|
|
1403
1499
|
}
|
|
1404
1500
|
// ==================== Get Session Status ====================
|
|
@@ -1477,8 +1573,13 @@ function createAuthRoutes(config = {}) {
|
|
|
1477
1573
|
const body = await request.json().catch(() => ({}));
|
|
1478
1574
|
const { full_name, avatar_url } = body;
|
|
1479
1575
|
let updatedUser;
|
|
1576
|
+
let rotated = null;
|
|
1480
1577
|
try {
|
|
1481
|
-
updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }
|
|
1578
|
+
updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }, {
|
|
1579
|
+
onTokenRotated: (newToken) => {
|
|
1580
|
+
rotated = newToken;
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1482
1583
|
} catch (err) {
|
|
1483
1584
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
1484
1585
|
return errorResponse(
|
|
@@ -1487,6 +1588,14 @@ function createAuthRoutes(config = {}) {
|
|
|
1487
1588
|
400
|
|
1488
1589
|
);
|
|
1489
1590
|
}
|
|
1591
|
+
if (rotated) {
|
|
1592
|
+
return withRefreshedSession(
|
|
1593
|
+
rotated,
|
|
1594
|
+
session.userId,
|
|
1595
|
+
{ success: true, data: { user: updatedUser } },
|
|
1596
|
+
cookieOptions
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1490
1599
|
return successResponse({ user: updatedUser });
|
|
1491
1600
|
}
|
|
1492
1601
|
default:
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-DsxJS_He.mjs';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-DsxJS_He.mjs';
|
|
3
3
|
import { p as ClientContext, A as ApiError } from '../index-DTWyUcyd.mjs';
|
|
4
4
|
export { L as LedveryRoutesConfig, a as LedverySessionData, S as SM_LEDVERY_ACCESS_TOKEN_COOKIE, b as SM_LEDVERY_ID_TOKEN_COOKIE, c as SM_LEDVERY_NONCE_COOKIE, d as SM_LEDVERY_PKCE_VERIFIER_COOKIE, e as SM_LEDVERY_STATE_COOKIE, f as createLedveryRoutes, g as getLedverySession } from '../ledvery-CxPzZpxP.mjs';
|
|
5
5
|
import { NextRequest, NextResponse } from 'next/server';
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-CTx4Uh8u.js';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-CTx4Uh8u.js';
|
|
3
3
|
import { p as ClientContext, A as ApiError } from '../index-DTWyUcyd.js';
|
|
4
4
|
export { L as LedveryRoutesConfig, a as LedverySessionData, S as SM_LEDVERY_ACCESS_TOKEN_COOKIE, b as SM_LEDVERY_ID_TOKEN_COOKIE, c as SM_LEDVERY_NONCE_COOKIE, d as SM_LEDVERY_PKCE_VERIFIER_COOKIE, e as SM_LEDVERY_STATE_COOKIE, f as createLedveryRoutes, g as getLedverySession } from '../ledvery-CxPzZpxP.js';
|
|
5
5
|
import { NextRequest, NextResponse } from 'next/server';
|
package/dist/server/index.js
CHANGED
|
@@ -2111,23 +2111,31 @@ var ScaleMuleServer = class {
|
|
|
2111
2111
|
/**
|
|
2112
2112
|
* Logout user
|
|
2113
2113
|
*/
|
|
2114
|
-
logout: async (sessionToken) => {
|
|
2114
|
+
logout: async (sessionToken, options) => {
|
|
2115
2115
|
return this.request("POST", "/v1/auth/logout", {
|
|
2116
|
-
|
|
2116
|
+
sessionToken,
|
|
2117
|
+
body: { session_token: sessionToken },
|
|
2118
|
+
onTokenRotated: options?.onTokenRotated
|
|
2117
2119
|
});
|
|
2118
2120
|
},
|
|
2119
2121
|
/**
|
|
2120
2122
|
* Get current user from session token
|
|
2121
2123
|
*/
|
|
2122
|
-
me: async (sessionToken) => {
|
|
2123
|
-
return this.request("GET", "/v1/auth/me", {
|
|
2124
|
+
me: async (sessionToken, options) => {
|
|
2125
|
+
return this.request("GET", "/v1/auth/me", {
|
|
2126
|
+
sessionToken,
|
|
2127
|
+
onTokenRotated: options?.onTokenRotated
|
|
2128
|
+
});
|
|
2124
2129
|
},
|
|
2125
2130
|
/**
|
|
2126
2131
|
* Refresh session token
|
|
2127
2132
|
*/
|
|
2128
|
-
refresh: async (sessionToken) => {
|
|
2133
|
+
refresh: async (sessionToken, options) => {
|
|
2129
2134
|
return this.request("POST", "/v1/auth/refresh", {
|
|
2130
|
-
|
|
2135
|
+
sessionToken,
|
|
2136
|
+
clientContext: options?.clientContext,
|
|
2137
|
+
isAutoRefresh: options?.isAutoRefresh,
|
|
2138
|
+
onTokenRotated: options?.onTokenRotated
|
|
2131
2139
|
});
|
|
2132
2140
|
},
|
|
2133
2141
|
/**
|
|
@@ -2159,7 +2167,8 @@ var ScaleMuleServer = class {
|
|
|
2159
2167
|
if (options?.email) {
|
|
2160
2168
|
return this.request("POST", "/v1/auth/resend-verification", {
|
|
2161
2169
|
sessionToken: sessionTokenOrEmail,
|
|
2162
|
-
body: { email: options.email }
|
|
2170
|
+
body: { email: options.email },
|
|
2171
|
+
onTokenRotated: options?.onTokenRotated
|
|
2163
2172
|
});
|
|
2164
2173
|
}
|
|
2165
2174
|
if (sessionTokenOrEmail.includes("@")) {
|
|
@@ -2168,7 +2177,8 @@ var ScaleMuleServer = class {
|
|
|
2168
2177
|
});
|
|
2169
2178
|
}
|
|
2170
2179
|
return this.request("POST", "/v1/auth/resend-verification", {
|
|
2171
|
-
sessionToken: sessionTokenOrEmail
|
|
2180
|
+
sessionToken: sessionTokenOrEmail,
|
|
2181
|
+
onTokenRotated: options?.onTokenRotated
|
|
2172
2182
|
});
|
|
2173
2183
|
}
|
|
2174
2184
|
};
|
|
@@ -2179,37 +2189,41 @@ var ScaleMuleServer = class {
|
|
|
2179
2189
|
/**
|
|
2180
2190
|
* Update user profile
|
|
2181
2191
|
*/
|
|
2182
|
-
update: async (sessionToken, data) => {
|
|
2192
|
+
update: async (sessionToken, data, options) => {
|
|
2183
2193
|
return this.request("PATCH", "/v1/auth/profile", {
|
|
2184
2194
|
sessionToken,
|
|
2185
|
-
body: data
|
|
2195
|
+
body: data,
|
|
2196
|
+
onTokenRotated: options?.onTokenRotated
|
|
2186
2197
|
});
|
|
2187
2198
|
},
|
|
2188
2199
|
/**
|
|
2189
2200
|
* Change password
|
|
2190
2201
|
*/
|
|
2191
|
-
changePassword: async (sessionToken, currentPassword, newPassword) => {
|
|
2202
|
+
changePassword: async (sessionToken, currentPassword, newPassword, options) => {
|
|
2192
2203
|
return this.request("POST", "/v1/auth/change-password", {
|
|
2193
2204
|
sessionToken,
|
|
2194
|
-
body: { current_password: currentPassword, new_password: newPassword }
|
|
2205
|
+
body: { current_password: currentPassword, new_password: newPassword },
|
|
2206
|
+
onTokenRotated: options?.onTokenRotated
|
|
2195
2207
|
});
|
|
2196
2208
|
},
|
|
2197
2209
|
/**
|
|
2198
2210
|
* Change email
|
|
2199
2211
|
*/
|
|
2200
|
-
changeEmail: async (sessionToken, newEmail, password) => {
|
|
2212
|
+
changeEmail: async (sessionToken, newEmail, password, options) => {
|
|
2201
2213
|
return this.request("POST", "/v1/auth/change-email", {
|
|
2202
2214
|
sessionToken,
|
|
2203
|
-
body: { new_email: newEmail, password }
|
|
2215
|
+
body: { new_email: newEmail, password },
|
|
2216
|
+
onTokenRotated: options?.onTokenRotated
|
|
2204
2217
|
});
|
|
2205
2218
|
},
|
|
2206
2219
|
/**
|
|
2207
2220
|
* Delete account
|
|
2208
2221
|
*/
|
|
2209
|
-
deleteAccount: async (sessionToken, password) => {
|
|
2222
|
+
deleteAccount: async (sessionToken, password, options) => {
|
|
2210
2223
|
return this.request("DELETE", "/v1/auth/me", {
|
|
2211
2224
|
sessionToken,
|
|
2212
|
-
body: { password }
|
|
2225
|
+
body: { password },
|
|
2226
|
+
onTokenRotated: options?.onTokenRotated
|
|
2213
2227
|
});
|
|
2214
2228
|
}
|
|
2215
2229
|
};
|
|
@@ -2670,6 +2684,9 @@ var ScaleMuleServer = class {
|
|
|
2670
2684
|
this.apiKey = config.apiKey;
|
|
2671
2685
|
this.gatewayUrl = resolveGatewayUrl(config);
|
|
2672
2686
|
this.debug = config.debug || false;
|
|
2687
|
+
this.onRefreshStart = config.onRefreshStart;
|
|
2688
|
+
this.onRefreshEnd = config.onRefreshEnd;
|
|
2689
|
+
this.onAutoRefreshFailed = config.onAutoRefreshFailed;
|
|
2673
2690
|
this.money = money.createMoneyClient({
|
|
2674
2691
|
apiKey: this.apiKey,
|
|
2675
2692
|
gatewayUrl: this.gatewayUrl,
|
|
@@ -2713,6 +2730,10 @@ var ScaleMuleServer = class {
|
|
|
2713
2730
|
headers: headers2,
|
|
2714
2731
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2715
2732
|
});
|
|
2733
|
+
const rotated = response.headers.get("x-rotated-session-token");
|
|
2734
|
+
if (rotated && options.onTokenRotated) {
|
|
2735
|
+
options.onTokenRotated(rotated);
|
|
2736
|
+
}
|
|
2716
2737
|
const text = await response.text();
|
|
2717
2738
|
let responseData = null;
|
|
2718
2739
|
try {
|
|
@@ -2724,6 +2745,33 @@ var ScaleMuleServer = class {
|
|
|
2724
2745
|
code: `HTTP_${response.status}`,
|
|
2725
2746
|
message: responseData?.message || text || response.statusText
|
|
2726
2747
|
};
|
|
2748
|
+
if (response.status === 401 && options.sessionToken && !options.isAutoRefresh) {
|
|
2749
|
+
if (this.debug) console.log("[ScaleMule Server] 401 received, attempting auto-refresh...");
|
|
2750
|
+
try {
|
|
2751
|
+
this.onRefreshStart?.();
|
|
2752
|
+
const refreshData = await this.auth.refresh(options.sessionToken, {
|
|
2753
|
+
clientContext: options.clientContext,
|
|
2754
|
+
isAutoRefresh: true
|
|
2755
|
+
// Prevent infinite loops
|
|
2756
|
+
});
|
|
2757
|
+
const newToken = refreshData.session_token;
|
|
2758
|
+
if (this.debug) console.log("[ScaleMule Server] Auto-refresh succeeded, retrying original request...");
|
|
2759
|
+
options.onTokenRotated?.(newToken);
|
|
2760
|
+
return this.request(method, path, {
|
|
2761
|
+
...options,
|
|
2762
|
+
sessionToken: newToken,
|
|
2763
|
+
isAutoRefresh: true
|
|
2764
|
+
// Don't refresh again on this retry
|
|
2765
|
+
});
|
|
2766
|
+
} catch (refreshErr) {
|
|
2767
|
+
if (this.debug) console.error("[ScaleMule Server] Auto-refresh failed:", refreshErr);
|
|
2768
|
+
const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
|
|
2769
|
+
this.onAutoRefreshFailed?.(refreshApiError);
|
|
2770
|
+
throw new ScaleMuleApiError(error);
|
|
2771
|
+
} finally {
|
|
2772
|
+
this.onRefreshEnd?.();
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2727
2775
|
throw new ScaleMuleApiError(error);
|
|
2728
2776
|
}
|
|
2729
2777
|
const data = responseData?.data !== void 0 ? responseData.data : responseData;
|
|
@@ -3245,8 +3293,16 @@ function createAuthRoutes(config = {}) {
|
|
|
3245
3293
|
// ==================== Logout ====================
|
|
3246
3294
|
case "logout": {
|
|
3247
3295
|
const session = await getSession();
|
|
3296
|
+
let rotated = null;
|
|
3248
3297
|
if (session) {
|
|
3249
|
-
|
|
3298
|
+
try {
|
|
3299
|
+
await sm.auth.logout(session.sessionToken, {
|
|
3300
|
+
onTokenRotated: (newToken) => {
|
|
3301
|
+
rotated = newToken;
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
} catch {
|
|
3305
|
+
}
|
|
3250
3306
|
}
|
|
3251
3307
|
if (config.onLogout) {
|
|
3252
3308
|
await config.onLogout();
|
|
@@ -3311,6 +3367,7 @@ function createAuthRoutes(config = {}) {
|
|
|
3311
3367
|
case "resend-verification": {
|
|
3312
3368
|
const { email } = body;
|
|
3313
3369
|
const session = await getSession();
|
|
3370
|
+
let rotated = null;
|
|
3314
3371
|
if (email) {
|
|
3315
3372
|
try {
|
|
3316
3373
|
await sm.auth.resendVerification(email);
|
|
@@ -3328,7 +3385,11 @@ function createAuthRoutes(config = {}) {
|
|
|
3328
3385
|
return errorResponse("UNAUTHORIZED", "Email or session required", 401);
|
|
3329
3386
|
}
|
|
3330
3387
|
try {
|
|
3331
|
-
await sm.auth.resendVerification(session.sessionToken
|
|
3388
|
+
await sm.auth.resendVerification(session.sessionToken, {
|
|
3389
|
+
onTokenRotated: (newToken) => {
|
|
3390
|
+
rotated = newToken;
|
|
3391
|
+
}
|
|
3392
|
+
});
|
|
3332
3393
|
} catch (err) {
|
|
3333
3394
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
3334
3395
|
return errorResponse(
|
|
@@ -3337,6 +3398,14 @@ function createAuthRoutes(config = {}) {
|
|
|
3337
3398
|
400
|
|
3338
3399
|
);
|
|
3339
3400
|
}
|
|
3401
|
+
if (rotated) {
|
|
3402
|
+
return withRefreshedSession(
|
|
3403
|
+
rotated,
|
|
3404
|
+
session.userId,
|
|
3405
|
+
{ success: true, data: { message: "Verification email sent" } },
|
|
3406
|
+
cookieOptions
|
|
3407
|
+
);
|
|
3408
|
+
}
|
|
3340
3409
|
return successResponse({ message: "Verification email sent" });
|
|
3341
3410
|
}
|
|
3342
3411
|
// ==================== Refresh Session ====================
|
|
@@ -3371,11 +3440,17 @@ function createAuthRoutes(config = {}) {
|
|
|
3371
3440
|
if (!current_password || !new_password) {
|
|
3372
3441
|
return errorResponse("VALIDATION_ERROR", "Current and new password required", 400);
|
|
3373
3442
|
}
|
|
3443
|
+
let rotated = null;
|
|
3374
3444
|
try {
|
|
3375
3445
|
await sm.user.changePassword(
|
|
3376
3446
|
session.sessionToken,
|
|
3377
3447
|
current_password,
|
|
3378
|
-
new_password
|
|
3448
|
+
new_password,
|
|
3449
|
+
{
|
|
3450
|
+
onTokenRotated: (newToken) => {
|
|
3451
|
+
rotated = newToken;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3379
3454
|
);
|
|
3380
3455
|
} catch (err) {
|
|
3381
3456
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
@@ -3385,6 +3460,14 @@ function createAuthRoutes(config = {}) {
|
|
|
3385
3460
|
400
|
|
3386
3461
|
);
|
|
3387
3462
|
}
|
|
3463
|
+
if (rotated) {
|
|
3464
|
+
return withRefreshedSession(
|
|
3465
|
+
rotated,
|
|
3466
|
+
session.userId,
|
|
3467
|
+
{ success: true, data: { message: "Password changed successfully" } },
|
|
3468
|
+
cookieOptions
|
|
3469
|
+
);
|
|
3470
|
+
}
|
|
3388
3471
|
return successResponse({ message: "Password changed successfully" });
|
|
3389
3472
|
}
|
|
3390
3473
|
// ==================== Switch Account ====================
|
|
@@ -3465,14 +3548,27 @@ function createAuthRoutes(config = {}) {
|
|
|
3465
3548
|
return withNorm(errorResponse("UNAUTHORIZED", "Authentication required", 401));
|
|
3466
3549
|
}
|
|
3467
3550
|
let userData;
|
|
3551
|
+
let rotated = null;
|
|
3468
3552
|
try {
|
|
3469
|
-
userData = await sm.auth.me(session.sessionToken
|
|
3553
|
+
userData = await sm.auth.me(session.sessionToken, {
|
|
3554
|
+
onTokenRotated: (newToken) => {
|
|
3555
|
+
rotated = newToken;
|
|
3556
|
+
}
|
|
3557
|
+
});
|
|
3470
3558
|
} catch {
|
|
3471
3559
|
return withNorm(clearSession(
|
|
3472
3560
|
{ error: { code: "SESSION_EXPIRED", message: "Session expired" } },
|
|
3473
3561
|
cookieOptions
|
|
3474
3562
|
));
|
|
3475
3563
|
}
|
|
3564
|
+
if (rotated) {
|
|
3565
|
+
return withNorm(withRefreshedSession(
|
|
3566
|
+
rotated,
|
|
3567
|
+
session.userId,
|
|
3568
|
+
{ user: userData, sessionToken: rotated, userId: session.userId },
|
|
3569
|
+
cookieOptions
|
|
3570
|
+
));
|
|
3571
|
+
}
|
|
3476
3572
|
return withNorm(successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId }));
|
|
3477
3573
|
}
|
|
3478
3574
|
// ==================== Get Session Status ====================
|
|
@@ -3551,8 +3647,13 @@ function createAuthRoutes(config = {}) {
|
|
|
3551
3647
|
const body = await request.json().catch(() => ({}));
|
|
3552
3648
|
const { full_name, avatar_url } = body;
|
|
3553
3649
|
let updatedUser;
|
|
3650
|
+
let rotated = null;
|
|
3554
3651
|
try {
|
|
3555
|
-
updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }
|
|
3652
|
+
updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }, {
|
|
3653
|
+
onTokenRotated: (newToken) => {
|
|
3654
|
+
rotated = newToken;
|
|
3655
|
+
}
|
|
3656
|
+
});
|
|
3556
3657
|
} catch (err) {
|
|
3557
3658
|
const apiErr = err instanceof ScaleMuleApiError ? err : null;
|
|
3558
3659
|
return errorResponse(
|
|
@@ -3561,6 +3662,14 @@ function createAuthRoutes(config = {}) {
|
|
|
3561
3662
|
400
|
|
3562
3663
|
);
|
|
3563
3664
|
}
|
|
3665
|
+
if (rotated) {
|
|
3666
|
+
return withRefreshedSession(
|
|
3667
|
+
rotated,
|
|
3668
|
+
session.userId,
|
|
3669
|
+
{ success: true, data: { user: updatedUser } },
|
|
3670
|
+
cookieOptions
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3564
3673
|
return successResponse({ user: updatedUser });
|
|
3565
3674
|
}
|
|
3566
3675
|
default:
|
|
@@ -3832,6 +3941,7 @@ function setLedverySession(response, session, opts) {
|
|
|
3832
3941
|
if (opts?.cookies?.sameSite) cookieOpts.sameSite = opts.cookies.sameSite;
|
|
3833
3942
|
if (opts?.cookies?.secure !== void 0) cookieOpts.secure = opts.cookies.secure;
|
|
3834
3943
|
const sessionPayload = JSON.stringify({
|
|
3944
|
+
idToken: session.idToken,
|
|
3835
3945
|
claims: session.claims,
|
|
3836
3946
|
expiresAt: session.expiresAt
|
|
3837
3947
|
});
|
|
@@ -3846,8 +3956,7 @@ function getLedverySession(request) {
|
|
|
3846
3956
|
try {
|
|
3847
3957
|
const parsed = JSON.parse(raw);
|
|
3848
3958
|
return {
|
|
3849
|
-
idToken: "",
|
|
3850
|
-
// not stored in cookie for size
|
|
3959
|
+
idToken: parsed.idToken || "",
|
|
3851
3960
|
claims: parsed.claims,
|
|
3852
3961
|
expiresAt: parsed.expiresAt
|
|
3853
3962
|
};
|
|
@@ -4052,9 +4161,14 @@ function createLedveryRoutes(config) {
|
|
|
4052
4161
|
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
4053
4162
|
);
|
|
4054
4163
|
}
|
|
4055
|
-
function handleLogout(
|
|
4164
|
+
function handleLogout(request) {
|
|
4165
|
+
const session = getLedverySession(request);
|
|
4166
|
+
const idToken = session?.idToken;
|
|
4056
4167
|
if (config.gatewayUrl) {
|
|
4057
|
-
|
|
4168
|
+
let rpLogoutUrl = `${config.gatewayUrl}/v1/auth/oauth/ledvery/logout?post_logout_redirect_uri=${encodeURIComponent(postLogoutRedirect)}`;
|
|
4169
|
+
if (idToken) {
|
|
4170
|
+
rpLogoutUrl += `&id_token_hint=${encodeURIComponent(idToken)}`;
|
|
4171
|
+
}
|
|
4058
4172
|
const response2 = new Response(null, {
|
|
4059
4173
|
status: 302,
|
|
4060
4174
|
headers: { Location: rpLogoutUrl }
|
|
@@ -4080,7 +4194,7 @@ function createLedveryRoutes(config) {
|
|
|
4080
4194
|
case "session":
|
|
4081
4195
|
return handleSession(request);
|
|
4082
4196
|
case "logout":
|
|
4083
|
-
return handleLogout();
|
|
4197
|
+
return handleLogout(request);
|
|
4084
4198
|
default:
|
|
4085
4199
|
return new Response(
|
|
4086
4200
|
JSON.stringify({ error: "not_found", message: `Unknown Ledvery route: ${action}` }),
|