@scalemule/nextjs 0.0.37 → 0.0.39
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 +1 -5
- package/dist/client.d.ts +1 -5
- package/dist/{index-BIIUrnPr.d.mts → index-BoENfro3.d.mts} +1 -54
- package/dist/{index-BIIUrnPr.d.ts → index-BoENfro3.d.ts} +1 -54
- package/dist/index.d.mts +3 -7
- package/dist/index.d.ts +3 -7
- package/dist/index.js +42 -112
- package/dist/index.mjs +42 -112
- package/dist/server/auth.js +6 -266
- package/dist/server/auth.mjs +6 -266
- package/dist/server/index.d.mts +4 -68
- package/dist/server/index.d.ts +4 -68
- package/dist/server/index.js +6 -273
- package/dist/server/index.mjs +7 -267
- package/dist/server/webhook-handler.d.mts +2 -2
- package/dist/server/webhook-handler.d.ts +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{webhook-handler-BobYTuNP.d.ts → webhook-handler-CjSSCdvI.d.ts} +1 -1
- package/dist/{webhook-handler-CNKQ1k2a.d.mts → webhook-handler-Cz9jtet2.d.mts} +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3087,6 +3087,7 @@ var RealtimeService = class extends ServiceModule {
|
|
|
3087
3087
|
super(...arguments);
|
|
3088
3088
|
this.basePath = "/v1/realtime";
|
|
3089
3089
|
this.ws = null;
|
|
3090
|
+
this.usedTicketAuth = false;
|
|
3090
3091
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
3091
3092
|
this.presenceCallbacks = /* @__PURE__ */ new Map();
|
|
3092
3093
|
this.statusCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -3215,10 +3216,31 @@ var RealtimeService = class extends ServiceModule {
|
|
|
3215
3216
|
// --------------------------------------------------------------------------
|
|
3216
3217
|
connect() {
|
|
3217
3218
|
if (this._status === "connecting" || this._status === "connected") return;
|
|
3218
|
-
const baseUrl = this.client.getBaseUrl();
|
|
3219
|
-
const wsUrl = baseUrl.replace(/^http/, "ws") + "/v1/realtime/ws";
|
|
3220
3219
|
this.setStatus(this.reconnectAttempt > 0 ? "reconnecting" : "connecting");
|
|
3220
|
+
this.fetchTicketAndConnect();
|
|
3221
|
+
}
|
|
3222
|
+
async fetchTicketAndConnect() {
|
|
3223
|
+
const baseUrl = this.client.getBaseUrl();
|
|
3221
3224
|
try {
|
|
3225
|
+
const headers = { "Content-Type": "application/json" };
|
|
3226
|
+
const apiKey = this.client.getApiKey();
|
|
3227
|
+
if (apiKey) headers["x-api-key"] = apiKey;
|
|
3228
|
+
const token = this.client.getSessionToken();
|
|
3229
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
3230
|
+
const ticketRes = await fetch(`${baseUrl}/v1/realtime/ws/ticket`, {
|
|
3231
|
+
method: "POST",
|
|
3232
|
+
headers
|
|
3233
|
+
});
|
|
3234
|
+
let wsUrl;
|
|
3235
|
+
if (ticketRes.ok) {
|
|
3236
|
+
const ticketData = await ticketRes.json();
|
|
3237
|
+
const ticket = ticketData.ticket;
|
|
3238
|
+
wsUrl = baseUrl.replace(/^http/, "ws") + `/v1/realtime/ws?ticket=${encodeURIComponent(ticket)}`;
|
|
3239
|
+
this.usedTicketAuth = true;
|
|
3240
|
+
} else {
|
|
3241
|
+
wsUrl = baseUrl.replace(/^http/, "ws") + "/v1/realtime/ws";
|
|
3242
|
+
this.usedTicketAuth = false;
|
|
3243
|
+
}
|
|
3222
3244
|
this.ws = new WebSocket(wsUrl);
|
|
3223
3245
|
} catch {
|
|
3224
3246
|
this.scheduleReconnect();
|
|
@@ -3226,8 +3248,21 @@ var RealtimeService = class extends ServiceModule {
|
|
|
3226
3248
|
}
|
|
3227
3249
|
this.ws.onopen = () => {
|
|
3228
3250
|
this.reconnectAttempt = 0;
|
|
3229
|
-
this.
|
|
3251
|
+
if (!this.usedTicketAuth) {
|
|
3252
|
+
this.authenticate();
|
|
3253
|
+
}
|
|
3230
3254
|
this.startHeartbeat();
|
|
3255
|
+
if (this.usedTicketAuth) {
|
|
3256
|
+
setTimeout(() => {
|
|
3257
|
+
if (!this.authenticated) {
|
|
3258
|
+
this.authenticated = true;
|
|
3259
|
+
this.setStatus("connected");
|
|
3260
|
+
for (const channel of this.subscriptions.keys()) {
|
|
3261
|
+
this.sendWs({ type: "subscribe", channel });
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
}, 2e3);
|
|
3265
|
+
}
|
|
3231
3266
|
};
|
|
3232
3267
|
this.ws.onmessage = (event) => {
|
|
3233
3268
|
try {
|
|
@@ -6914,8 +6949,6 @@ function ScaleMuleProvider({
|
|
|
6914
6949
|
analyticsProxyUrl,
|
|
6915
6950
|
authProxyUrl,
|
|
6916
6951
|
publishableKey,
|
|
6917
|
-
enableAccountSwitcher,
|
|
6918
|
-
accountSwitcherPrivacy,
|
|
6919
6952
|
children,
|
|
6920
6953
|
onLogin,
|
|
6921
6954
|
onLogout,
|
|
@@ -7053,11 +7086,9 @@ function ScaleMuleProvider({
|
|
|
7053
7086
|
publishableKey,
|
|
7054
7087
|
gatewayUrl: gatewayUrl || (environment === "dev" ? "https://api-dev.scalemule.com" : "https://api.scalemule.com"),
|
|
7055
7088
|
environment: environment || void 0,
|
|
7056
|
-
enableAccountSwitcher,
|
|
7057
|
-
accountSwitcherPrivacy,
|
|
7058
7089
|
bootstrapFlags
|
|
7059
7090
|
}),
|
|
7060
|
-
[client, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, gatewayUrl, environment,
|
|
7091
|
+
[client, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, gatewayUrl, environment, bootstrapFlags]
|
|
7061
7092
|
);
|
|
7062
7093
|
return /* @__PURE__ */ jsx(ScaleMuleContext.Provider, { value, children });
|
|
7063
7094
|
}
|
|
@@ -7074,41 +7105,6 @@ function useScaleMuleClient() {
|
|
|
7074
7105
|
const { client } = useScaleMule();
|
|
7075
7106
|
return client;
|
|
7076
7107
|
}
|
|
7077
|
-
function maskEmail2(email) {
|
|
7078
|
-
const [local, domain] = email.split("@");
|
|
7079
|
-
if (!domain) return "***@***.***";
|
|
7080
|
-
const tldDot = domain.lastIndexOf(".");
|
|
7081
|
-
const tld = tldDot > 0 ? domain.slice(tldDot) : "";
|
|
7082
|
-
const domainBase = tldDot > 0 ? domain.slice(0, tldDot) : domain;
|
|
7083
|
-
return `${local[0] || "*"}***@${domainBase[0] || "*"}***${tld}`;
|
|
7084
|
-
}
|
|
7085
|
-
function stableColorIndex2(userId) {
|
|
7086
|
-
let hash = 0;
|
|
7087
|
-
for (let i = 0; i < userId.length; i++) {
|
|
7088
|
-
hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
|
|
7089
|
-
}
|
|
7090
|
-
return Math.abs(hash) % 8;
|
|
7091
|
-
}
|
|
7092
|
-
function applyClientPrivacy(entry, privacy) {
|
|
7093
|
-
if (!privacy || privacy === "full") return entry;
|
|
7094
|
-
if (privacy === "masked") {
|
|
7095
|
-
return {
|
|
7096
|
-
userId: entry.userId,
|
|
7097
|
-
email: entry.email ? maskEmail2(entry.email) : void 0,
|
|
7098
|
-
fullName: entry.fullName ? `${entry.fullName[0].toUpperCase()}.` : void 0,
|
|
7099
|
-
provider: entry.provider,
|
|
7100
|
-
lastActiveAt: entry.lastActiveAt,
|
|
7101
|
-
colorIndex: stableColorIndex2(entry.userId)
|
|
7102
|
-
};
|
|
7103
|
-
}
|
|
7104
|
-
return {
|
|
7105
|
-
userId: entry.userId,
|
|
7106
|
-
provider: entry.provider,
|
|
7107
|
-
lastActiveAt: entry.lastActiveAt,
|
|
7108
|
-
displayLabel: "Account",
|
|
7109
|
-
colorIndex: stableColorIndex2(entry.userId)
|
|
7110
|
-
};
|
|
7111
|
-
}
|
|
7112
7108
|
function getCookie(name) {
|
|
7113
7109
|
if (typeof document === "undefined") return void 0;
|
|
7114
7110
|
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
|
|
@@ -7137,7 +7133,7 @@ async function proxyFetch(proxyUrl, path, options = {}) {
|
|
|
7137
7133
|
return data;
|
|
7138
7134
|
}
|
|
7139
7135
|
function useAuth() {
|
|
7140
|
-
const { client, user, setUser, initializing, error, setError, authProxyUrl
|
|
7136
|
+
const { client, user, setUser, initializing, error, setError, authProxyUrl } = useScaleMule();
|
|
7141
7137
|
const register = useCallback(
|
|
7142
7138
|
async (data) => {
|
|
7143
7139
|
setError(null);
|
|
@@ -7745,63 +7741,6 @@ function useAuth() {
|
|
|
7745
7741
|
},
|
|
7746
7742
|
[client, setUser, setError, authProxyUrl]
|
|
7747
7743
|
);
|
|
7748
|
-
const readKnownAccountsCookie = useCallback(() => {
|
|
7749
|
-
if (!enableAccountSwitcher) return [];
|
|
7750
|
-
if (typeof document === "undefined") return [];
|
|
7751
|
-
const match = document.cookie.match(/(?:^|; )sm_known_accounts=([^;]*)/);
|
|
7752
|
-
if (!match) return [];
|
|
7753
|
-
try {
|
|
7754
|
-
const decoded = decodeURIComponent(match[1]);
|
|
7755
|
-
const accounts = JSON.parse(decoded);
|
|
7756
|
-
let entries = Object.values(accounts);
|
|
7757
|
-
if (accountSwitcherPrivacy && accountSwitcherPrivacy !== "full") {
|
|
7758
|
-
entries = entries.map((e) => applyClientPrivacy(e, accountSwitcherPrivacy));
|
|
7759
|
-
}
|
|
7760
|
-
return entries;
|
|
7761
|
-
} catch {
|
|
7762
|
-
return [];
|
|
7763
|
-
}
|
|
7764
|
-
}, [enableAccountSwitcher, accountSwitcherPrivacy]);
|
|
7765
|
-
const [knownAccounts, setKnownAccounts] = useState([]);
|
|
7766
|
-
useEffect(() => {
|
|
7767
|
-
setKnownAccounts(readKnownAccountsCookie());
|
|
7768
|
-
}, [readKnownAccountsCookie, user]);
|
|
7769
|
-
const switchAccount = useCallback(
|
|
7770
|
-
async (userId) => {
|
|
7771
|
-
const target = knownAccounts.find((a) => a.userId === userId) || null;
|
|
7772
|
-
if (!target) return null;
|
|
7773
|
-
if (authProxyUrl) {
|
|
7774
|
-
await proxyFetch(authProxyUrl, "switch-account");
|
|
7775
|
-
} else {
|
|
7776
|
-
const sessionToken = client.getSessionToken();
|
|
7777
|
-
if (sessionToken) {
|
|
7778
|
-
try {
|
|
7779
|
-
await client.post("/v1/auth/logout", { session_token: sessionToken });
|
|
7780
|
-
} catch {
|
|
7781
|
-
}
|
|
7782
|
-
}
|
|
7783
|
-
await client.clearSession();
|
|
7784
|
-
}
|
|
7785
|
-
setUser(null);
|
|
7786
|
-
return target;
|
|
7787
|
-
},
|
|
7788
|
-
[client, setUser, knownAccounts, authProxyUrl]
|
|
7789
|
-
);
|
|
7790
|
-
const removeKnownAccount = useCallback(
|
|
7791
|
-
async (userId) => {
|
|
7792
|
-
if (authProxyUrl) {
|
|
7793
|
-
await proxyFetch(authProxyUrl, "forget-account", { body: { user_id: userId } });
|
|
7794
|
-
}
|
|
7795
|
-
setKnownAccounts((prev) => prev.filter((a) => a.userId !== userId));
|
|
7796
|
-
},
|
|
7797
|
-
[authProxyUrl]
|
|
7798
|
-
);
|
|
7799
|
-
const clearKnownAccounts = useCallback(async () => {
|
|
7800
|
-
if (authProxyUrl) {
|
|
7801
|
-
await proxyFetch(authProxyUrl, "forget-all-accounts");
|
|
7802
|
-
}
|
|
7803
|
-
setKnownAccounts([]);
|
|
7804
|
-
}, [authProxyUrl]);
|
|
7805
7744
|
return useMemo(
|
|
7806
7745
|
() => ({
|
|
7807
7746
|
user,
|
|
@@ -7833,12 +7772,7 @@ function useAuth() {
|
|
|
7833
7772
|
// Phone auth
|
|
7834
7773
|
sendPhoneCode,
|
|
7835
7774
|
verifyPhone,
|
|
7836
|
-
loginWithPhone
|
|
7837
|
-
// Account switcher
|
|
7838
|
-
knownAccounts,
|
|
7839
|
-
switchAccount,
|
|
7840
|
-
removeKnownAccount,
|
|
7841
|
-
clearKnownAccounts
|
|
7775
|
+
loginWithPhone
|
|
7842
7776
|
}),
|
|
7843
7777
|
[
|
|
7844
7778
|
user,
|
|
@@ -7865,11 +7799,7 @@ function useAuth() {
|
|
|
7865
7799
|
regenerateBackupCodes,
|
|
7866
7800
|
sendPhoneCode,
|
|
7867
7801
|
verifyPhone,
|
|
7868
|
-
loginWithPhone
|
|
7869
|
-
knownAccounts,
|
|
7870
|
-
switchAccount,
|
|
7871
|
-
removeKnownAccount,
|
|
7872
|
-
clearKnownAccounts
|
|
7802
|
+
loginWithPhone
|
|
7873
7803
|
]
|
|
7874
7804
|
);
|
|
7875
7805
|
}
|
package/dist/server/auth.js
CHANGED
|
@@ -767,7 +767,6 @@ function createServerClient(config) {
|
|
|
767
767
|
}
|
|
768
768
|
var SESSION_COOKIE_NAME = "sm_session";
|
|
769
769
|
var USER_ID_COOKIE_NAME = "sm_user_id";
|
|
770
|
-
var KNOWN_ACCOUNTS_COOKIE_NAME = "sm_known_accounts";
|
|
771
770
|
({
|
|
772
771
|
secure: process.env.NODE_ENV === "production"});
|
|
773
772
|
function createCookieHeader(name, value, options = {}) {
|
|
@@ -848,163 +847,6 @@ async function getSession() {
|
|
|
848
847
|
// Note: actual expiry is managed by ScaleMule backend
|
|
849
848
|
};
|
|
850
849
|
}
|
|
851
|
-
var MAX_KNOWN_ACCOUNTS = 10;
|
|
852
|
-
function maskEmail(email) {
|
|
853
|
-
const [local, domain] = email.split("@");
|
|
854
|
-
if (!domain) return "***@***.***";
|
|
855
|
-
const tldDot = domain.lastIndexOf(".");
|
|
856
|
-
const tld = tldDot > 0 ? domain.slice(tldDot) : "";
|
|
857
|
-
const domainBase = tldDot > 0 ? domain.slice(0, tldDot) : domain;
|
|
858
|
-
return `${local[0] || "*"}***@${domainBase[0] || "*"}***${tld}`;
|
|
859
|
-
}
|
|
860
|
-
function stableColorIndex(userId) {
|
|
861
|
-
let hash = 0;
|
|
862
|
-
for (let i = 0; i < userId.length; i++) {
|
|
863
|
-
hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
|
|
864
|
-
}
|
|
865
|
-
return Math.abs(hash) % 8;
|
|
866
|
-
}
|
|
867
|
-
function applyPrivacyToEntry(entry, privacy) {
|
|
868
|
-
switch (privacy) {
|
|
869
|
-
case "full":
|
|
870
|
-
return entry;
|
|
871
|
-
case "masked":
|
|
872
|
-
return {
|
|
873
|
-
userId: entry.userId,
|
|
874
|
-
email: entry.email ? maskEmail(entry.email) : void 0,
|
|
875
|
-
fullName: entry.fullName ? `${entry.fullName[0].toUpperCase()}.` : void 0,
|
|
876
|
-
provider: entry.provider,
|
|
877
|
-
lastActiveAt: entry.lastActiveAt,
|
|
878
|
-
colorIndex: stableColorIndex(entry.userId)
|
|
879
|
-
};
|
|
880
|
-
case "minimal":
|
|
881
|
-
return {
|
|
882
|
-
userId: entry.userId,
|
|
883
|
-
provider: entry.provider,
|
|
884
|
-
lastActiveAt: entry.lastActiveAt,
|
|
885
|
-
displayLabel: "Account",
|
|
886
|
-
colorIndex: stableColorIndex(entry.userId)
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
function appendKnownAccountCookie(headers, account, existingCookie, options = {}, privacy) {
|
|
891
|
-
let accounts = {};
|
|
892
|
-
if (existingCookie) {
|
|
893
|
-
try {
|
|
894
|
-
accounts = JSON.parse(decodeURIComponent(existingCookie));
|
|
895
|
-
} catch {
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
const effectivePrivacy = privacy || "full";
|
|
899
|
-
for (const [userId, entry] of Object.entries(accounts)) {
|
|
900
|
-
accounts[userId] = applyPrivacyToEntry(entry, effectivePrivacy);
|
|
901
|
-
}
|
|
902
|
-
accounts[account.userId] = applyPrivacyToEntry(account, effectivePrivacy);
|
|
903
|
-
const entries = Object.entries(accounts);
|
|
904
|
-
if (entries.length > MAX_KNOWN_ACCOUNTS) {
|
|
905
|
-
entries.sort((a, b) => new Date(b[1].lastActiveAt).getTime() - new Date(a[1].lastActiveAt).getTime());
|
|
906
|
-
accounts = Object.fromEntries(entries.slice(0, MAX_KNOWN_ACCOUNTS));
|
|
907
|
-
}
|
|
908
|
-
const maxAge = 365 * 24 * 60 * 60;
|
|
909
|
-
const secure = options.secure ?? process.env.NODE_ENV === "production";
|
|
910
|
-
const sameSite = options.sameSite ?? "lax";
|
|
911
|
-
const path = options.path ?? "/";
|
|
912
|
-
let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
|
|
913
|
-
if (secure) {
|
|
914
|
-
cookie += "; Secure";
|
|
915
|
-
}
|
|
916
|
-
if (options.domain) {
|
|
917
|
-
cookie += `; Domain=${options.domain}`;
|
|
918
|
-
}
|
|
919
|
-
headers.append("Set-Cookie", cookie);
|
|
920
|
-
}
|
|
921
|
-
function removeKnownAccountFromCookie(headers, userId, existingCookie, options = {}) {
|
|
922
|
-
let accounts = {};
|
|
923
|
-
if (existingCookie) {
|
|
924
|
-
try {
|
|
925
|
-
accounts = JSON.parse(decodeURIComponent(existingCookie));
|
|
926
|
-
} catch {
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
delete accounts[userId];
|
|
930
|
-
const maxAge = 365 * 24 * 60 * 60;
|
|
931
|
-
const secure = options.secure ?? process.env.NODE_ENV === "production";
|
|
932
|
-
const sameSite = options.sameSite ?? "lax";
|
|
933
|
-
const path = options.path ?? "/";
|
|
934
|
-
let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
|
|
935
|
-
if (secure) {
|
|
936
|
-
cookie += "; Secure";
|
|
937
|
-
}
|
|
938
|
-
if (options.domain) {
|
|
939
|
-
cookie += `; Domain=${options.domain}`;
|
|
940
|
-
}
|
|
941
|
-
headers.append("Set-Cookie", cookie);
|
|
942
|
-
}
|
|
943
|
-
function clearKnownAccountsCookie(headers, options = {}) {
|
|
944
|
-
const path = options.path ?? "/";
|
|
945
|
-
let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=; Path=${path}; Max-Age=0`;
|
|
946
|
-
if (options.domain) {
|
|
947
|
-
cookie += `; Domain=${options.domain}`;
|
|
948
|
-
}
|
|
949
|
-
headers.append("Set-Cookie", cookie);
|
|
950
|
-
}
|
|
951
|
-
function getKnownAccountsFromRequest(request) {
|
|
952
|
-
const cookieHeader = request.headers.get("cookie");
|
|
953
|
-
if (!cookieHeader) return [];
|
|
954
|
-
const cookies3 = Object.fromEntries(
|
|
955
|
-
cookieHeader.split(";").map((c) => {
|
|
956
|
-
const [key, ...rest] = c.trim().split("=");
|
|
957
|
-
return [key, decodeURIComponent(rest.join("="))];
|
|
958
|
-
})
|
|
959
|
-
);
|
|
960
|
-
const raw = cookies3[KNOWN_ACCOUNTS_COOKIE_NAME];
|
|
961
|
-
if (!raw) return [];
|
|
962
|
-
try {
|
|
963
|
-
const accounts = JSON.parse(raw);
|
|
964
|
-
return Object.values(accounts);
|
|
965
|
-
} catch {
|
|
966
|
-
return [];
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
function getKnownAccountsCookieRaw(request) {
|
|
970
|
-
const cookieHeader = request.headers.get("cookie");
|
|
971
|
-
if (!cookieHeader) return null;
|
|
972
|
-
const cookies3 = Object.fromEntries(
|
|
973
|
-
cookieHeader.split(";").map((c) => {
|
|
974
|
-
const [key, ...rest] = c.trim().split("=");
|
|
975
|
-
return [key, decodeURIComponent(rest.join("="))];
|
|
976
|
-
})
|
|
977
|
-
);
|
|
978
|
-
return cookies3[KNOWN_ACCOUNTS_COOKIE_NAME] || null;
|
|
979
|
-
}
|
|
980
|
-
function normalizeKnownAccountsCookie(request, privacy, options = {}) {
|
|
981
|
-
if (!privacy || privacy === "full") return null;
|
|
982
|
-
const raw = getKnownAccountsCookieRaw(request);
|
|
983
|
-
if (!raw) return null;
|
|
984
|
-
let accounts = {};
|
|
985
|
-
try {
|
|
986
|
-
accounts = JSON.parse(raw);
|
|
987
|
-
} catch {
|
|
988
|
-
return null;
|
|
989
|
-
}
|
|
990
|
-
let changed = false;
|
|
991
|
-
for (const [userId, entry] of Object.entries(accounts)) {
|
|
992
|
-
const normalized = applyPrivacyToEntry(entry, privacy);
|
|
993
|
-
if (JSON.stringify(normalized) !== JSON.stringify(entry)) {
|
|
994
|
-
accounts[userId] = normalized;
|
|
995
|
-
changed = true;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
if (!changed) return null;
|
|
999
|
-
const maxAge = 365 * 24 * 60 * 60;
|
|
1000
|
-
const secure = options.secure ?? process.env.NODE_ENV === "production";
|
|
1001
|
-
const sameSite = options.sameSite ?? "lax";
|
|
1002
|
-
const path = options.path ?? "/";
|
|
1003
|
-
let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
|
|
1004
|
-
if (secure) cookie += "; Secure";
|
|
1005
|
-
if (options.domain) cookie += `; Domain=${options.domain}`;
|
|
1006
|
-
return cookie;
|
|
1007
|
-
}
|
|
1008
850
|
|
|
1009
851
|
// src/server/timing.ts
|
|
1010
852
|
function constantTimeEqual(a, b) {
|
|
@@ -1091,25 +933,7 @@ function createAuthRoutes(config = {}) {
|
|
|
1091
933
|
} catch {
|
|
1092
934
|
return successResponse({ user: registeredUser, message: "Registration successful" }, 201);
|
|
1093
935
|
}
|
|
1094
|
-
|
|
1095
|
-
if (config.enableAccountSwitcher) {
|
|
1096
|
-
const existingKnown = getKnownAccountsCookieRaw(request);
|
|
1097
|
-
appendKnownAccountCookie(
|
|
1098
|
-
registerResponse.headers,
|
|
1099
|
-
{
|
|
1100
|
-
userId: registeredUser.id,
|
|
1101
|
-
email: registeredUser.email,
|
|
1102
|
-
fullName: registeredUser.full_name ?? void 0,
|
|
1103
|
-
avatarUrl: registeredUser.avatar_url ?? void 0,
|
|
1104
|
-
provider: "email",
|
|
1105
|
-
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1106
|
-
},
|
|
1107
|
-
existingKnown,
|
|
1108
|
-
cookieOptions,
|
|
1109
|
-
config.accountSwitcherPrivacy
|
|
1110
|
-
);
|
|
1111
|
-
}
|
|
1112
|
-
return registerResponse;
|
|
936
|
+
return withSession(loginData, { user: registeredUser, sessionToken: loginData.session_token, userId: registeredUser.id }, cookieOptions);
|
|
1113
937
|
}
|
|
1114
938
|
// ==================== Login ====================
|
|
1115
939
|
case "login": {
|
|
@@ -1140,25 +964,7 @@ function createAuthRoutes(config = {}) {
|
|
|
1140
964
|
email: loginData.user.email
|
|
1141
965
|
});
|
|
1142
966
|
}
|
|
1143
|
-
|
|
1144
|
-
if (config.enableAccountSwitcher) {
|
|
1145
|
-
const existingKnown = getKnownAccountsCookieRaw(request);
|
|
1146
|
-
appendKnownAccountCookie(
|
|
1147
|
-
loginResponse.headers,
|
|
1148
|
-
{
|
|
1149
|
-
userId: loginData.user.id,
|
|
1150
|
-
email: loginData.user.email,
|
|
1151
|
-
fullName: loginData.user.full_name ?? void 0,
|
|
1152
|
-
avatarUrl: loginData.user.avatar_url ?? void 0,
|
|
1153
|
-
provider: "email",
|
|
1154
|
-
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1155
|
-
},
|
|
1156
|
-
existingKnown,
|
|
1157
|
-
cookieOptions,
|
|
1158
|
-
config.accountSwitcherPrivacy
|
|
1159
|
-
);
|
|
1160
|
-
}
|
|
1161
|
-
return loginResponse;
|
|
967
|
+
return withSession(loginData, { user: loginData.user, sessionToken: loginData.session_token, userId: loginData.user.id }, cookieOptions);
|
|
1162
968
|
}
|
|
1163
969
|
// ==================== Logout ====================
|
|
1164
970
|
case "logout": {
|
|
@@ -1305,59 +1111,6 @@ function createAuthRoutes(config = {}) {
|
|
|
1305
1111
|
}
|
|
1306
1112
|
return successResponse({ message: "Password changed successfully" });
|
|
1307
1113
|
}
|
|
1308
|
-
// ==================== Switch Account ====================
|
|
1309
|
-
// Clears the active session so the user can re-authenticate as a different account.
|
|
1310
|
-
// The known accounts cookie is preserved — only the session cookie is cleared.
|
|
1311
|
-
case "switch-account": {
|
|
1312
|
-
if (!config.enableAccountSwitcher) {
|
|
1313
|
-
return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
|
|
1314
|
-
}
|
|
1315
|
-
const session = await getSession();
|
|
1316
|
-
if (session) {
|
|
1317
|
-
try {
|
|
1318
|
-
await sm.auth.logout(session.sessionToken);
|
|
1319
|
-
} catch {
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
const switchResponse = clearSession({ message: "Session cleared for account switch" }, cookieOptions);
|
|
1323
|
-
const knownAccounts = getKnownAccountsFromRequest(request);
|
|
1324
|
-
return new Response(
|
|
1325
|
-
JSON.stringify({ success: true, data: { message: "Session cleared for account switch", knownAccounts } }),
|
|
1326
|
-
{ status: 200, headers: switchResponse.headers }
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
// ==================== Forget Account ====================
|
|
1330
|
-
// Remove a specific account from the known accounts cookie.
|
|
1331
|
-
case "forget-account": {
|
|
1332
|
-
if (!config.enableAccountSwitcher) {
|
|
1333
|
-
return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
|
|
1334
|
-
}
|
|
1335
|
-
const { user_id } = body;
|
|
1336
|
-
if (!user_id) {
|
|
1337
|
-
return errorResponse("VALIDATION_ERROR", "user_id required", 400);
|
|
1338
|
-
}
|
|
1339
|
-
const headers = new Headers();
|
|
1340
|
-
headers.set("Content-Type", "application/json");
|
|
1341
|
-
const existingKnown = getKnownAccountsCookieRaw(request);
|
|
1342
|
-
removeKnownAccountFromCookie(headers, user_id, existingKnown, cookieOptions);
|
|
1343
|
-
return new Response(
|
|
1344
|
-
JSON.stringify({ success: true, data: { message: "Account forgotten" } }),
|
|
1345
|
-
{ status: 200, headers }
|
|
1346
|
-
);
|
|
1347
|
-
}
|
|
1348
|
-
// ==================== Forget All Accounts ====================
|
|
1349
|
-
case "forget-all-accounts": {
|
|
1350
|
-
if (!config.enableAccountSwitcher) {
|
|
1351
|
-
return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
|
|
1352
|
-
}
|
|
1353
|
-
const headers = new Headers();
|
|
1354
|
-
headers.set("Content-Type", "application/json");
|
|
1355
|
-
clearKnownAccountsCookie(headers, cookieOptions);
|
|
1356
|
-
return new Response(
|
|
1357
|
-
JSON.stringify({ success: true, data: { message: "All accounts forgotten" } }),
|
|
1358
|
-
{ status: 200, headers }
|
|
1359
|
-
);
|
|
1360
|
-
}
|
|
1361
1114
|
default:
|
|
1362
1115
|
return errorResponse("NOT_FOUND", `Unknown endpoint: ${path}`, 404);
|
|
1363
1116
|
}
|
|
@@ -1373,25 +1126,20 @@ function createAuthRoutes(config = {}) {
|
|
|
1373
1126
|
switch (path) {
|
|
1374
1127
|
// ==================== Get Current User ====================
|
|
1375
1128
|
case "me": {
|
|
1376
|
-
const normCookie = config.enableAccountSwitcher ? normalizeKnownAccountsCookie(request, config.accountSwitcherPrivacy, cookieOptions) : null;
|
|
1377
|
-
const withNorm = (resp) => {
|
|
1378
|
-
if (normCookie) resp.headers.append("Set-Cookie", normCookie);
|
|
1379
|
-
return resp;
|
|
1380
|
-
};
|
|
1381
1129
|
const session = await getSession();
|
|
1382
1130
|
if (!session) {
|
|
1383
|
-
return
|
|
1131
|
+
return errorResponse("UNAUTHORIZED", "Authentication required", 401);
|
|
1384
1132
|
}
|
|
1385
1133
|
let userData;
|
|
1386
1134
|
try {
|
|
1387
1135
|
userData = await sm.auth.me(session.sessionToken);
|
|
1388
1136
|
} catch {
|
|
1389
|
-
return
|
|
1137
|
+
return clearSession(
|
|
1390
1138
|
{ error: { code: "SESSION_EXPIRED", message: "Session expired" } },
|
|
1391
1139
|
cookieOptions
|
|
1392
|
-
)
|
|
1140
|
+
);
|
|
1393
1141
|
}
|
|
1394
|
-
return
|
|
1142
|
+
return successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId });
|
|
1395
1143
|
}
|
|
1396
1144
|
// ==================== Get Session Status ====================
|
|
1397
1145
|
case "session": {
|
|
@@ -1401,14 +1149,6 @@ function createAuthRoutes(config = {}) {
|
|
|
1401
1149
|
userId: session?.userId || null
|
|
1402
1150
|
});
|
|
1403
1151
|
}
|
|
1404
|
-
// ==================== Get Known Accounts ====================
|
|
1405
|
-
case "known-accounts": {
|
|
1406
|
-
if (!config.enableAccountSwitcher) {
|
|
1407
|
-
return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
|
|
1408
|
-
}
|
|
1409
|
-
const knownAccounts = getKnownAccountsFromRequest(request);
|
|
1410
|
-
return successResponse({ knownAccounts });
|
|
1411
|
-
}
|
|
1412
1152
|
default:
|
|
1413
1153
|
return errorResponse("NOT_FOUND", `Unknown endpoint: ${path}`, 404);
|
|
1414
1154
|
}
|