ugly-app 0.1.616 → 0.1.617
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/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/bootstrapApp.d.ts.map +1 -1
- package/dist/client/bootstrapApp.js +107 -26
- package/dist/client/bootstrapApp.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/client/bootstrapApp.tsx +104 -26
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.617";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapApp.d.ts","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUzE,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAInF,UAAU,mBAAmB;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,eAAe,CAAC;IAE1B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,uDAAuD;IACvD,cAAc,EAAE,aAAa,CAAC;QAC5B,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;KACjC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,EAAE,MAAM,YAAY,CAAC;IAE3B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,KAAK,CAAC;IAEjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"bootstrapApp.d.ts","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUzE,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAInF,UAAU,mBAAmB;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,eAAe,CAAC;IAE1B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,uDAAuD;IACvD,cAAc,EAAE,aAAa,CAAC;QAC5B,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;KACjC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,EAAE,MAAM,YAAY,CAAC;IAE3B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,KAAK,CAAC;IAEjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAyHD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA2L/D"}
|
|
@@ -15,21 +15,89 @@ import { createUglyBotSocket } from './uglyBotSocket.js';
|
|
|
15
15
|
const w = typeof window !== 'undefined' ? window : {};
|
|
16
16
|
const RECONCILE_TRIED_KEY = 'ugly_session_reconciled';
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
23
|
-
* mismatch, drop the stale session and re-adopt the live account.
|
|
24
|
-
*
|
|
25
|
-
* Detection works only where the ugly.bot cookie is actually sent — same-site
|
|
26
|
-
* `*.ugly.bot` subdomains. Cross-site apex apps get `{ userId: null }` (SameSite
|
|
27
|
-
* =Lax strips the cookie), which is a safe no-op. Guarded once-per-tab so the
|
|
28
|
-
* re-adopt bounce can't loop.
|
|
18
|
+
* True when this app shares ugly.bot's session cookie — i.e. it's served from
|
|
19
|
+
* `ugly.bot` or a `*.ugly.bot` subdomain. Same-site means SameSite=Lax sends the
|
|
20
|
+
* cookie even on a hidden iframe subresource, so the silent-auth iframe below can
|
|
21
|
+
* read the live session. Cross-site apex apps (ugly.chat, andalib.net) don't get
|
|
22
|
+
* the cookie in an iframe and must use the top-level redirect SSO instead.
|
|
29
23
|
*/
|
|
30
|
-
function
|
|
24
|
+
function sharesUglyBotCookie(uglyBotUrl) {
|
|
25
|
+
if (typeof window === 'undefined')
|
|
26
|
+
return false;
|
|
27
|
+
try {
|
|
28
|
+
const botHost = new URL(uglyBotUrl).hostname;
|
|
29
|
+
const here = window.location.hostname;
|
|
30
|
+
return here === botHost || here.endsWith(`.${botHost}`);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load ugly.bot's silent-auth endpoint in a hidden iframe and resolve with the
|
|
38
|
+
* one-time code it postMessages back (or null if there's no ugly.bot session, or
|
|
39
|
+
* on timeout). This is the SAME mechanism as auto-login — a same-site iframe that
|
|
40
|
+
* reads the live ugly.bot cookie — repurposed here so it ALSO serves as the
|
|
41
|
+
* keep-the-token-fresh / account-sync check on every boot.
|
|
42
|
+
*/
|
|
43
|
+
function silentAuthViaIframe(uglyBotUrl) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
if (typeof document === 'undefined') {
|
|
46
|
+
resolve(null);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const origin = window.location.origin;
|
|
50
|
+
let botOrigin;
|
|
51
|
+
try {
|
|
52
|
+
botOrigin = new URL(uglyBotUrl).origin;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
resolve(null);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const src = `${botOrigin}/oauth/silent?origin=${encodeURIComponent(origin)}&mode=iframe`;
|
|
59
|
+
const iframe = document.createElement('iframe');
|
|
60
|
+
iframe.style.display = 'none';
|
|
61
|
+
iframe.setAttribute('aria-hidden', 'true');
|
|
62
|
+
iframe.setAttribute('tabindex', '-1');
|
|
63
|
+
let settled = false;
|
|
64
|
+
const finish = (code) => {
|
|
65
|
+
if (settled)
|
|
66
|
+
return;
|
|
67
|
+
settled = true;
|
|
68
|
+
window.removeEventListener('message', onMessage);
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
iframe.remove();
|
|
71
|
+
resolve(code);
|
|
72
|
+
};
|
|
73
|
+
const onMessage = (e) => {
|
|
74
|
+
if (e.origin !== botOrigin)
|
|
75
|
+
return;
|
|
76
|
+
const data = e.data;
|
|
77
|
+
if (data && data.type === 'ugly-bot-silent-auth') {
|
|
78
|
+
finish(typeof data.code === 'string' ? data.code : null);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
window.addEventListener('message', onMessage);
|
|
82
|
+
const timer = setTimeout(() => finish(null), 6000);
|
|
83
|
+
iframe.src = src;
|
|
84
|
+
document.body.appendChild(iframe);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Boot-time silent auth + account-sync via the iframe above. One mechanism, three
|
|
89
|
+
* jobs: (1) logged-out → auto-login if a ugly.bot session exists; (2) logged-in,
|
|
90
|
+
* same account → redeem a fresh code so the session cookie/token stays valid;
|
|
91
|
+
* (3) logged-in, DIFFERENT ugly.bot account → reload onto the live account (the
|
|
92
|
+
* child's old JWT never auto-revokes, so without this it would keep acting as the
|
|
93
|
+
* stale account). `sessionUserId` is the current session's user (null if logged
|
|
94
|
+
* out). Guarded once-per-tab so the reload can't loop.
|
|
95
|
+
*/
|
|
96
|
+
function silentAuthSync(uglyBotUrl, sessionUserId) {
|
|
31
97
|
if (typeof window === 'undefined')
|
|
32
98
|
return;
|
|
99
|
+
if (!sharesUglyBotCookie(uglyBotUrl))
|
|
100
|
+
return; // apex apps use redirect SSO
|
|
33
101
|
try {
|
|
34
102
|
if (sessionStorage.getItem(RECONCILE_TRIED_KEY))
|
|
35
103
|
return;
|
|
@@ -39,22 +107,31 @@ function reconcileUglyBotSession(uglyBotUrl, sessionUserId) {
|
|
|
39
107
|
return;
|
|
40
108
|
}
|
|
41
109
|
void (async () => {
|
|
110
|
+
const code = await silentAuthViaIframe(uglyBotUrl).catch(() => null);
|
|
111
|
+
// No code → no reachable ugly.bot session (logged out there too). Leave the
|
|
112
|
+
// current state as-is.
|
|
113
|
+
if (!code)
|
|
114
|
+
return;
|
|
42
115
|
try {
|
|
43
|
-
const res = await fetch(
|
|
116
|
+
const res = await fetch('/auth/verify', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: { 'Content-Type': 'application/json' },
|
|
119
|
+
body: JSON.stringify({ code }),
|
|
120
|
+
});
|
|
44
121
|
if (!res.ok)
|
|
45
122
|
return;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
// null → cookie not sent (cross-site apex, or logged out at ugly.bot):
|
|
49
|
-
// nothing to reconcile, leave the session untouched.
|
|
50
|
-
if (!liveId || liveId === sessionUserId)
|
|
123
|
+
const { token } = (await res.json());
|
|
124
|
+
if (!token)
|
|
51
125
|
return;
|
|
52
|
-
|
|
53
|
-
// the
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
126
|
+
const liveUserId = JSON.parse(atob(token.split('.')[1])).sub ?? null;
|
|
127
|
+
// Same account → the cookie was just refreshed; nothing visible to do.
|
|
128
|
+
// Different account (or we were logged out) → reload to adopt the live
|
|
129
|
+
// session the server now holds. Clear the once-per-tab silent-SSO guard so
|
|
130
|
+
// the reloaded page doesn't suppress its own SSO.
|
|
131
|
+
if (liveUserId && liveUserId !== sessionUserId) {
|
|
132
|
+
clearSilentSsoTried();
|
|
57
133
|
window.location.reload();
|
|
134
|
+
}
|
|
58
135
|
}
|
|
59
136
|
catch {
|
|
60
137
|
/* network hiccup — leave the session as-is */
|
|
@@ -144,6 +221,10 @@ export function bootstrapApp(options) {
|
|
|
144
221
|
return;
|
|
145
222
|
}
|
|
146
223
|
renderWithRouter(false, renderApp());
|
|
224
|
+
// Subdomain apps (share the cookie): silently adopt an existing ugly.bot
|
|
225
|
+
// session via the hidden iframe — same mechanism as auto-login. Renders
|
|
226
|
+
// logged-out first, then reloads logged-in if a session is found.
|
|
227
|
+
silentAuthSync(uglyBotUrl, null);
|
|
147
228
|
return;
|
|
148
229
|
}
|
|
149
230
|
const userId = (JSON.parse(atob(token.split('.')[1]))).sub;
|
|
@@ -188,9 +269,9 @@ export function bootstrapApp(options) {
|
|
|
188
269
|
? createUglyBotSocket(effectiveAppToken, uglyBotUrl)
|
|
189
270
|
: null;
|
|
190
271
|
renderWithRouter(true, _jsx(AppProvider, { socket: socket, uglyBotSocket: uglyBotSocket, userId: userId, user: user, children: renderApp() }));
|
|
191
|
-
// Background: make sure
|
|
192
|
-
// account; re-adopt the correct one if the user switched
|
|
193
|
-
|
|
272
|
+
// Background: refresh the session token and make sure it still matches the
|
|
273
|
+
// live ugly.bot account; re-adopt the correct one if the user switched.
|
|
274
|
+
silentAuthSync(uglyBotUrl, userId);
|
|
194
275
|
})
|
|
195
276
|
.catch((err) => {
|
|
196
277
|
console.error('[bootstrapApp] socket.connect failed, clearing auth cookie:', err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapApp.js","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,eAAe,EAA8B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AA4CzD,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAuD,CAAC,CAAC,CAAC,EAAwC,CAAC;AAE7I,MAAM,mBAAmB,GAAG,yBAAyB,CAAC;AAEtD
|
|
1
|
+
{"version":3,"file":"bootstrapApp.js","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.617",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
6
6
|
"files": [
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.617";
|
|
@@ -61,20 +61,85 @@ const w = typeof window !== 'undefined' ? window as unknown as Record<string, st
|
|
|
61
61
|
const RECONCILE_TRIED_KEY = 'ugly_session_reconciled';
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* the
|
|
69
|
-
* mismatch, drop the stale session and re-adopt the live account.
|
|
70
|
-
*
|
|
71
|
-
* Detection works only where the ugly.bot cookie is actually sent — same-site
|
|
72
|
-
* `*.ugly.bot` subdomains. Cross-site apex apps get `{ userId: null }` (SameSite
|
|
73
|
-
* =Lax strips the cookie), which is a safe no-op. Guarded once-per-tab so the
|
|
74
|
-
* re-adopt bounce can't loop.
|
|
64
|
+
* True when this app shares ugly.bot's session cookie — i.e. it's served from
|
|
65
|
+
* `ugly.bot` or a `*.ugly.bot` subdomain. Same-site means SameSite=Lax sends the
|
|
66
|
+
* cookie even on a hidden iframe subresource, so the silent-auth iframe below can
|
|
67
|
+
* read the live session. Cross-site apex apps (ugly.chat, andalib.net) don't get
|
|
68
|
+
* the cookie in an iframe and must use the top-level redirect SSO instead.
|
|
75
69
|
*/
|
|
76
|
-
function
|
|
70
|
+
function sharesUglyBotCookie(uglyBotUrl: string): boolean {
|
|
71
|
+
if (typeof window === 'undefined') return false;
|
|
72
|
+
try {
|
|
73
|
+
const botHost = new URL(uglyBotUrl).hostname;
|
|
74
|
+
const here = window.location.hostname;
|
|
75
|
+
return here === botHost || here.endsWith(`.${botHost}`);
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load ugly.bot's silent-auth endpoint in a hidden iframe and resolve with the
|
|
83
|
+
* one-time code it postMessages back (or null if there's no ugly.bot session, or
|
|
84
|
+
* on timeout). This is the SAME mechanism as auto-login — a same-site iframe that
|
|
85
|
+
* reads the live ugly.bot cookie — repurposed here so it ALSO serves as the
|
|
86
|
+
* keep-the-token-fresh / account-sync check on every boot.
|
|
87
|
+
*/
|
|
88
|
+
function silentAuthViaIframe(uglyBotUrl: string): Promise<string | null> {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
if (typeof document === 'undefined') {
|
|
91
|
+
resolve(null);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const origin = window.location.origin;
|
|
95
|
+
let botOrigin: string;
|
|
96
|
+
try {
|
|
97
|
+
botOrigin = new URL(uglyBotUrl).origin;
|
|
98
|
+
} catch {
|
|
99
|
+
resolve(null);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const src = `${botOrigin}/oauth/silent?origin=${encodeURIComponent(origin)}&mode=iframe`;
|
|
103
|
+
const iframe = document.createElement('iframe');
|
|
104
|
+
iframe.style.display = 'none';
|
|
105
|
+
iframe.setAttribute('aria-hidden', 'true');
|
|
106
|
+
iframe.setAttribute('tabindex', '-1');
|
|
107
|
+
|
|
108
|
+
let settled = false;
|
|
109
|
+
const finish = (code: string | null): void => {
|
|
110
|
+
if (settled) return;
|
|
111
|
+
settled = true;
|
|
112
|
+
window.removeEventListener('message', onMessage);
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
iframe.remove();
|
|
115
|
+
resolve(code);
|
|
116
|
+
};
|
|
117
|
+
const onMessage = (e: MessageEvent): void => {
|
|
118
|
+
if (e.origin !== botOrigin) return;
|
|
119
|
+
const data = e.data as { type?: string; code?: string | null } | null;
|
|
120
|
+
if (data && data.type === 'ugly-bot-silent-auth') {
|
|
121
|
+
finish(typeof data.code === 'string' ? data.code : null);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
window.addEventListener('message', onMessage);
|
|
125
|
+
const timer = setTimeout(() => finish(null), 6000);
|
|
126
|
+
iframe.src = src;
|
|
127
|
+
document.body.appendChild(iframe);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Boot-time silent auth + account-sync via the iframe above. One mechanism, three
|
|
133
|
+
* jobs: (1) logged-out → auto-login if a ugly.bot session exists; (2) logged-in,
|
|
134
|
+
* same account → redeem a fresh code so the session cookie/token stays valid;
|
|
135
|
+
* (3) logged-in, DIFFERENT ugly.bot account → reload onto the live account (the
|
|
136
|
+
* child's old JWT never auto-revokes, so without this it would keep acting as the
|
|
137
|
+
* stale account). `sessionUserId` is the current session's user (null if logged
|
|
138
|
+
* out). Guarded once-per-tab so the reload can't loop.
|
|
139
|
+
*/
|
|
140
|
+
function silentAuthSync(uglyBotUrl: string, sessionUserId: string | null): void {
|
|
77
141
|
if (typeof window === 'undefined') return;
|
|
142
|
+
if (!sharesUglyBotCookie(uglyBotUrl)) return; // apex apps use redirect SSO
|
|
78
143
|
try {
|
|
79
144
|
if (sessionStorage.getItem(RECONCILE_TRIED_KEY)) return;
|
|
80
145
|
sessionStorage.setItem(RECONCILE_TRIED_KEY, '1');
|
|
@@ -82,19 +147,28 @@ function reconcileUglyBotSession(uglyBotUrl: string, sessionUserId: string): voi
|
|
|
82
147
|
return;
|
|
83
148
|
}
|
|
84
149
|
void (async () => {
|
|
150
|
+
const code = await silentAuthViaIframe(uglyBotUrl).catch(() => null);
|
|
151
|
+
// No code → no reachable ugly.bot session (logged out there too). Leave the
|
|
152
|
+
// current state as-is.
|
|
153
|
+
if (!code) return;
|
|
85
154
|
try {
|
|
86
|
-
const res = await fetch(
|
|
155
|
+
const res = await fetch('/auth/verify', {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
body: JSON.stringify({ code }),
|
|
159
|
+
});
|
|
87
160
|
if (!res.ok) return;
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
// the
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
161
|
+
const { token } = (await res.json()) as { token?: string };
|
|
162
|
+
if (!token) return;
|
|
163
|
+
const liveUserId = (JSON.parse(atob(token.split('.')[1])) as { sub?: string }).sub ?? null;
|
|
164
|
+
// Same account → the cookie was just refreshed; nothing visible to do.
|
|
165
|
+
// Different account (or we were logged out) → reload to adopt the live
|
|
166
|
+
// session the server now holds. Clear the once-per-tab silent-SSO guard so
|
|
167
|
+
// the reloaded page doesn't suppress its own SSO.
|
|
168
|
+
if (liveUserId && liveUserId !== sessionUserId) {
|
|
169
|
+
clearSilentSsoTried();
|
|
170
|
+
window.location.reload();
|
|
171
|
+
}
|
|
98
172
|
} catch {
|
|
99
173
|
/* network hiccup — leave the session as-is */
|
|
100
174
|
}
|
|
@@ -222,6 +296,10 @@ export function bootstrapApp(options: BootstrapAppOptions): void {
|
|
|
222
296
|
return;
|
|
223
297
|
}
|
|
224
298
|
renderWithRouter(false, renderApp());
|
|
299
|
+
// Subdomain apps (share the cookie): silently adopt an existing ugly.bot
|
|
300
|
+
// session via the hidden iframe — same mechanism as auto-login. Renders
|
|
301
|
+
// logged-out first, then reloads logged-in if a session is found.
|
|
302
|
+
silentAuthSync(uglyBotUrl, null);
|
|
225
303
|
return;
|
|
226
304
|
}
|
|
227
305
|
|
|
@@ -275,9 +353,9 @@ export function bootstrapApp(options: BootstrapAppOptions): void {
|
|
|
275
353
|
{renderApp()}
|
|
276
354
|
</AppProvider>,
|
|
277
355
|
);
|
|
278
|
-
// Background: make sure
|
|
279
|
-
// account; re-adopt the correct one if the user switched
|
|
280
|
-
|
|
356
|
+
// Background: refresh the session token and make sure it still matches the
|
|
357
|
+
// live ugly.bot account; re-adopt the correct one if the user switched.
|
|
358
|
+
silentAuthSync(uglyBotUrl, userId);
|
|
281
359
|
})
|
|
282
360
|
.catch((err: unknown) => {
|
|
283
361
|
console.error('[bootstrapApp] socket.connect failed, clearing auth cookie:', err);
|