@kadoa/mcp 0.3.2 → 0.3.3-rc.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.
- package/dist/index.js +455 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49167,6 +49167,20 @@ function errorResult(message) {
|
|
|
49167
49167
|
isError: true
|
|
49168
49168
|
};
|
|
49169
49169
|
}
|
|
49170
|
+
function extractApiMessage(responseBody) {
|
|
49171
|
+
const body = responseBody;
|
|
49172
|
+
if (!body || typeof body !== "object")
|
|
49173
|
+
return;
|
|
49174
|
+
const msg = typeof body.error === "string" && body.error || typeof body.message === "string" && body.message || undefined;
|
|
49175
|
+
if (!msg)
|
|
49176
|
+
return;
|
|
49177
|
+
if (body.validationErrors && typeof body.validationErrors === "object" && body.validationErrors !== null) {
|
|
49178
|
+
const details = Object.entries(body.validationErrors).map(([field, err]) => `${field}: "${err}"`).join(", ");
|
|
49179
|
+
if (details)
|
|
49180
|
+
return `${msg} — Details: ${details}`;
|
|
49181
|
+
}
|
|
49182
|
+
return msg;
|
|
49183
|
+
}
|
|
49170
49184
|
function classifyError(error48) {
|
|
49171
49185
|
if (KadoaSdkException.isInstance(error48)) {
|
|
49172
49186
|
const code = error48.code;
|
|
@@ -49186,9 +49200,9 @@ function classifyError(error48) {
|
|
|
49186
49200
|
return `Request timed out${status}. Please try again.`;
|
|
49187
49201
|
case "VALIDATION_ERROR":
|
|
49188
49202
|
case "BAD_REQUEST":
|
|
49189
|
-
return httpError.message;
|
|
49203
|
+
return extractApiMessage(httpError.responseBody) || httpError.message;
|
|
49190
49204
|
default:
|
|
49191
|
-
return `Kadoa API error${status}. Please try again later.`;
|
|
49205
|
+
return extractApiMessage(httpError.responseBody) || `Kadoa API error${status}. Please try again later.`;
|
|
49192
49206
|
}
|
|
49193
49207
|
}
|
|
49194
49208
|
switch (code) {
|
|
@@ -53676,14 +53690,139 @@ class KadoaOAuthProvider {
|
|
|
53676
53690
|
params,
|
|
53677
53691
|
supabaseCodeVerifier: verifier
|
|
53678
53692
|
});
|
|
53693
|
+
res.type("html").send(renderLoginPage(state));
|
|
53694
|
+
}
|
|
53695
|
+
async handleGoogleLogin(req, res) {
|
|
53696
|
+
const { state } = req.body;
|
|
53697
|
+
const pending = pendingAuths.get(state);
|
|
53698
|
+
if (!pending) {
|
|
53699
|
+
res.status(400).send("Unknown or expired state parameter");
|
|
53700
|
+
return;
|
|
53701
|
+
}
|
|
53702
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
53703
|
+
const serverUrl = process.env.MCP_SERVER_URL;
|
|
53704
|
+
if (!supabaseUrl || !serverUrl) {
|
|
53705
|
+
res.status(500).send("Server misconfigured");
|
|
53706
|
+
return;
|
|
53707
|
+
}
|
|
53679
53708
|
const redirectTo = `${serverUrl}/auth/callback?mcp_state=${state}`;
|
|
53680
53709
|
const authUrl = new URL(`${supabaseUrl}/auth/v1/authorize`);
|
|
53681
53710
|
authUrl.searchParams.set("provider", "google");
|
|
53682
53711
|
authUrl.searchParams.set("redirect_to", redirectTo);
|
|
53683
|
-
authUrl.searchParams.set("code_challenge",
|
|
53712
|
+
authUrl.searchParams.set("code_challenge", pending.supabaseCodeVerifier ? createHash2("sha256").update(pending.supabaseCodeVerifier).digest("base64url") : "");
|
|
53684
53713
|
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
53685
53714
|
res.redirect(authUrl.toString());
|
|
53686
53715
|
}
|
|
53716
|
+
async handleEmailPasswordLogin(req, res) {
|
|
53717
|
+
const { state, email: email3, password } = req.body;
|
|
53718
|
+
if (!state || !email3 || !password) {
|
|
53719
|
+
res.status(400).send("Missing required fields");
|
|
53720
|
+
return;
|
|
53721
|
+
}
|
|
53722
|
+
const pending = pendingAuths.get(state);
|
|
53723
|
+
if (!pending) {
|
|
53724
|
+
res.status(400).type("html").send(renderLoginPage(state, "Session expired — please try again"));
|
|
53725
|
+
return;
|
|
53726
|
+
}
|
|
53727
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
53728
|
+
if (!supabaseUrl) {
|
|
53729
|
+
res.status(500).send("Server misconfigured");
|
|
53730
|
+
return;
|
|
53731
|
+
}
|
|
53732
|
+
try {
|
|
53733
|
+
const tokenRes = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=password`, {
|
|
53734
|
+
method: "POST",
|
|
53735
|
+
headers: {
|
|
53736
|
+
"Content-Type": "application/json",
|
|
53737
|
+
apikey: process.env.SUPABASE_ANON_KEY
|
|
53738
|
+
},
|
|
53739
|
+
body: JSON.stringify({ email: email3, password })
|
|
53740
|
+
});
|
|
53741
|
+
if (!tokenRes.ok) {
|
|
53742
|
+
const body = await tokenRes.json().catch(() => ({ error_description: "Authentication failed" }));
|
|
53743
|
+
const message = body.error_description || body.msg || "Invalid email or password";
|
|
53744
|
+
res.type("html").send(renderLoginPage(state, message));
|
|
53745
|
+
return;
|
|
53746
|
+
}
|
|
53747
|
+
const data = await tokenRes.json();
|
|
53748
|
+
pendingAuths.delete(state);
|
|
53749
|
+
await this.completeAuthWithTokens(pending, res, data.access_token, data.refresh_token);
|
|
53750
|
+
} catch (error48) {
|
|
53751
|
+
console.error("Email/password login error:", error48);
|
|
53752
|
+
res.type("html").send(renderLoginPage(state, "An unexpected error occurred"));
|
|
53753
|
+
}
|
|
53754
|
+
}
|
|
53755
|
+
async handleSSOLogin(req, res) {
|
|
53756
|
+
const { state, email: email3 } = req.body;
|
|
53757
|
+
if (!state || !email3) {
|
|
53758
|
+
res.status(400).send("Missing required fields");
|
|
53759
|
+
return;
|
|
53760
|
+
}
|
|
53761
|
+
const pending = pendingAuths.get(state);
|
|
53762
|
+
if (!pending) {
|
|
53763
|
+
res.status(400).type("html").send(renderLoginPage(state, "Session expired — please try again"));
|
|
53764
|
+
return;
|
|
53765
|
+
}
|
|
53766
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
53767
|
+
const serverUrl = process.env.MCP_SERVER_URL;
|
|
53768
|
+
if (!supabaseUrl || !serverUrl) {
|
|
53769
|
+
res.status(500).send("Server misconfigured");
|
|
53770
|
+
return;
|
|
53771
|
+
}
|
|
53772
|
+
const domain2 = email3.includes("@") ? email3.split("@").pop() : email3;
|
|
53773
|
+
try {
|
|
53774
|
+
const ssoRes = await fetch(`${supabaseUrl}/auth/v1/sso`, {
|
|
53775
|
+
method: "POST",
|
|
53776
|
+
headers: {
|
|
53777
|
+
"Content-Type": "application/json",
|
|
53778
|
+
apikey: process.env.SUPABASE_ANON_KEY
|
|
53779
|
+
},
|
|
53780
|
+
body: JSON.stringify({
|
|
53781
|
+
domain: domain2,
|
|
53782
|
+
redirect_to: `${serverUrl}/auth/callback?mcp_state=${state}`,
|
|
53783
|
+
skip_http_redirect: true,
|
|
53784
|
+
code_challenge: createHash2("sha256").update(pending.supabaseCodeVerifier).digest("base64url"),
|
|
53785
|
+
code_challenge_method: "s256"
|
|
53786
|
+
})
|
|
53787
|
+
});
|
|
53788
|
+
if (!ssoRes.ok) {
|
|
53789
|
+
const body = await ssoRes.json().catch(() => ({}));
|
|
53790
|
+
const message = body.error_description || body.msg || body.message || "No SSO provider configured for this domain";
|
|
53791
|
+
res.type("html").send(renderLoginPage(state, message));
|
|
53792
|
+
return;
|
|
53793
|
+
}
|
|
53794
|
+
const data = await ssoRes.json();
|
|
53795
|
+
if (!data.url) {
|
|
53796
|
+
res.type("html").send(renderLoginPage(state, "No SSO provider configured for this domain"));
|
|
53797
|
+
return;
|
|
53798
|
+
}
|
|
53799
|
+
res.redirect(data.url);
|
|
53800
|
+
} catch (error48) {
|
|
53801
|
+
console.error("SSO login error:", error48);
|
|
53802
|
+
res.type("html").send(renderLoginPage(state, "An unexpected error occurred"));
|
|
53803
|
+
}
|
|
53804
|
+
}
|
|
53805
|
+
async completeAuthWithTokens(pending, res, supabaseJwt, supabaseRefreshToken) {
|
|
53806
|
+
const teams = await fetchUserTeams(supabaseJwt);
|
|
53807
|
+
if (teams.length === 1) {
|
|
53808
|
+
const refreshed = await setActiveTeamAndRefresh(supabaseJwt, supabaseRefreshToken, teams[0].id);
|
|
53809
|
+
this.completeAuthFlow(pending, res, {
|
|
53810
|
+
jwt: refreshed.jwt,
|
|
53811
|
+
refreshToken: refreshed.refreshToken,
|
|
53812
|
+
teamId: teams[0].id
|
|
53813
|
+
});
|
|
53814
|
+
return;
|
|
53815
|
+
}
|
|
53816
|
+
const selectionToken = randomToken();
|
|
53817
|
+
pendingTeamSelections.set(selectionToken, {
|
|
53818
|
+
supabaseJwt,
|
|
53819
|
+
supabaseRefreshToken,
|
|
53820
|
+
teams,
|
|
53821
|
+
pending,
|
|
53822
|
+
expiresAt: Date.now() + TEAM_SELECTION_TTL
|
|
53823
|
+
});
|
|
53824
|
+
res.type("html").send(renderTeamSelectionPage(teams, selectionToken));
|
|
53825
|
+
}
|
|
53687
53826
|
async challengeForAuthorizationCode(_client, authorizationCode) {
|
|
53688
53827
|
const entry = authCodes.get(authorizationCode);
|
|
53689
53828
|
if (!entry)
|
|
@@ -53815,26 +53954,7 @@ class KadoaOAuthProvider {
|
|
|
53815
53954
|
pendingAuths.delete(state);
|
|
53816
53955
|
try {
|
|
53817
53956
|
const supabaseTokens = await exchangeSupabaseCode(code, pending.supabaseCodeVerifier);
|
|
53818
|
-
|
|
53819
|
-
const teams = await fetchUserTeams(supabaseJwt);
|
|
53820
|
-
if (teams.length === 1) {
|
|
53821
|
-
const refreshed = await setActiveTeamAndRefresh(supabaseJwt, supabaseTokens.refreshToken, teams[0].id);
|
|
53822
|
-
this.completeAuthFlow(pending, res, {
|
|
53823
|
-
jwt: refreshed.jwt,
|
|
53824
|
-
refreshToken: refreshed.refreshToken,
|
|
53825
|
-
teamId: teams[0].id
|
|
53826
|
-
});
|
|
53827
|
-
return;
|
|
53828
|
-
}
|
|
53829
|
-
const selectionToken = randomToken();
|
|
53830
|
-
pendingTeamSelections.set(selectionToken, {
|
|
53831
|
-
supabaseJwt,
|
|
53832
|
-
supabaseRefreshToken: supabaseTokens.refreshToken,
|
|
53833
|
-
teams,
|
|
53834
|
-
pending,
|
|
53835
|
-
expiresAt: Date.now() + TEAM_SELECTION_TTL
|
|
53836
|
-
});
|
|
53837
|
-
res.type("html").send(renderTeamSelectionPage(teams, selectionToken));
|
|
53957
|
+
await this.completeAuthWithTokens(pending, res, supabaseTokens.accessToken, supabaseTokens.refreshToken);
|
|
53838
53958
|
} catch (error48) {
|
|
53839
53959
|
console.error("Auth callback error:", error48);
|
|
53840
53960
|
const redirectUrl = new URL(pending.params.redirectUri);
|
|
@@ -54017,6 +54137,309 @@ function renderTeamSelectionPage(teams, selectionToken) {
|
|
|
54017
54137
|
</body>
|
|
54018
54138
|
</html>`;
|
|
54019
54139
|
}
|
|
54140
|
+
function renderLoginPage(state, error48) {
|
|
54141
|
+
const errorHtml = error48 ? `<div class="error">${escapeHtml(error48)}</div>` : "";
|
|
54142
|
+
return `<!DOCTYPE html>
|
|
54143
|
+
<html lang="en">
|
|
54144
|
+
<head>
|
|
54145
|
+
<meta charset="utf-8" />
|
|
54146
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
54147
|
+
<title>Sign In - Kadoa</title>
|
|
54148
|
+
<style>
|
|
54149
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
54150
|
+
|
|
54151
|
+
body {
|
|
54152
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
54153
|
+
background: hsl(0 0% 98%);
|
|
54154
|
+
color: #18181b;
|
|
54155
|
+
min-height: 100dvh;
|
|
54156
|
+
display: flex;
|
|
54157
|
+
align-items: center;
|
|
54158
|
+
justify-content: center;
|
|
54159
|
+
background-image: radial-gradient(circle, #d4d4d8 1px, transparent 1px);
|
|
54160
|
+
background-size: 24px 24px;
|
|
54161
|
+
background-position: center top;
|
|
54162
|
+
}
|
|
54163
|
+
|
|
54164
|
+
.card {
|
|
54165
|
+
width: 100%;
|
|
54166
|
+
max-width: 460px;
|
|
54167
|
+
background: #fff;
|
|
54168
|
+
padding: 1rem;
|
|
54169
|
+
display: flex;
|
|
54170
|
+
flex-direction: column;
|
|
54171
|
+
gap: 1rem;
|
|
54172
|
+
min-height: 100dvh;
|
|
54173
|
+
}
|
|
54174
|
+
|
|
54175
|
+
@media (min-width: 768px) {
|
|
54176
|
+
.card { padding: 3rem; min-height: auto; border-left: 1px solid #e0e1e5; border-right: 1px solid #e0e1e5; }
|
|
54177
|
+
}
|
|
54178
|
+
|
|
54179
|
+
.logo { display: grid; place-content: center; }
|
|
54180
|
+
|
|
54181
|
+
h1 {
|
|
54182
|
+
font-size: 20px;
|
|
54183
|
+
font-weight: 600;
|
|
54184
|
+
text-align: center;
|
|
54185
|
+
color: #18181b;
|
|
54186
|
+
}
|
|
54187
|
+
|
|
54188
|
+
.spacer { height: 0; }
|
|
54189
|
+
@media (min-width: 768px) { .spacer { height: 3rem; } }
|
|
54190
|
+
|
|
54191
|
+
.error {
|
|
54192
|
+
background: #fef2f2;
|
|
54193
|
+
color: #991b1b;
|
|
54194
|
+
border: 1px solid #fecaca;
|
|
54195
|
+
border-radius: 4px;
|
|
54196
|
+
padding: 0.6rem 0.875rem;
|
|
54197
|
+
font-size: 15px;
|
|
54198
|
+
}
|
|
54199
|
+
|
|
54200
|
+
/* Buttons — matching KUI default + primary looks */
|
|
54201
|
+
.btn {
|
|
54202
|
+
width: 100%;
|
|
54203
|
+
padding: 0.6em 1em;
|
|
54204
|
+
border-radius: 4px;
|
|
54205
|
+
font-size: 16px;
|
|
54206
|
+
font-weight: 500;
|
|
54207
|
+
cursor: pointer;
|
|
54208
|
+
display: flex;
|
|
54209
|
+
align-items: center;
|
|
54210
|
+
justify-content: center;
|
|
54211
|
+
gap: 0.5rem;
|
|
54212
|
+
transition: background 0.15s, border-color 0.15s;
|
|
54213
|
+
text-decoration: none;
|
|
54214
|
+
}
|
|
54215
|
+
|
|
54216
|
+
.btn-default {
|
|
54217
|
+
background: #fff;
|
|
54218
|
+
color: #18181b;
|
|
54219
|
+
border: 1px solid #d4d4d8;
|
|
54220
|
+
box-shadow: inset 0 -3px 0 0 rgba(0,0,0,0.03), 0 1px 0px 1px rgba(255,255,255,0.5), 0 -1px 0px 1px rgba(0,0,0,0.02);
|
|
54221
|
+
}
|
|
54222
|
+
|
|
54223
|
+
.btn-default:hover {
|
|
54224
|
+
background: rgba(113,113,122,0.1);
|
|
54225
|
+
}
|
|
54226
|
+
|
|
54227
|
+
.btn-primary {
|
|
54228
|
+
background: hsl(212 70% 27%);
|
|
54229
|
+
color: #fff;
|
|
54230
|
+
border: 1px solid hsl(214 70% 23%);
|
|
54231
|
+
box-shadow: inset 0 2px 0 0 rgba(56,189,248,0.2), 0 -1px 0px 1px rgba(0,0,0,0.02);
|
|
54232
|
+
}
|
|
54233
|
+
|
|
54234
|
+
.btn-primary:hover {
|
|
54235
|
+
background: hsl(212 70% 33%);
|
|
54236
|
+
}
|
|
54237
|
+
|
|
54238
|
+
/* OR divider */
|
|
54239
|
+
.line-or {
|
|
54240
|
+
display: flex;
|
|
54241
|
+
align-items: center;
|
|
54242
|
+
gap: 0.5rem;
|
|
54243
|
+
font-weight: 500;
|
|
54244
|
+
color: rgba(24,24,27,0.6);
|
|
54245
|
+
font-size: 14px;
|
|
54246
|
+
margin: 0.5rem 0;
|
|
54247
|
+
}
|
|
54248
|
+
|
|
54249
|
+
.line-or hr {
|
|
54250
|
+
flex: 1;
|
|
54251
|
+
border: none;
|
|
54252
|
+
border-top: 1px solid rgba(113,113,122,0.15);
|
|
54253
|
+
}
|
|
54254
|
+
|
|
54255
|
+
/* Form inputs — matching KUI input style */
|
|
54256
|
+
label {
|
|
54257
|
+
display: block;
|
|
54258
|
+
font-size: 16px;
|
|
54259
|
+
font-weight: 500;
|
|
54260
|
+
margin-bottom: 0.25rem;
|
|
54261
|
+
color: #18181b;
|
|
54262
|
+
}
|
|
54263
|
+
|
|
54264
|
+
input[type="email"], input[type="password"] {
|
|
54265
|
+
width: 100%;
|
|
54266
|
+
padding: 0.35em 0.5em;
|
|
54267
|
+
border: 1px solid #d4d4d8;
|
|
54268
|
+
border-radius: 4px;
|
|
54269
|
+
font-size: 18px;
|
|
54270
|
+
font-family: inherit;
|
|
54271
|
+
color: #18181b;
|
|
54272
|
+
background: #fff;
|
|
54273
|
+
outline: none;
|
|
54274
|
+
box-shadow: inset 0 3px 0 0 rgba(0,0,0,0.025);
|
|
54275
|
+
transition: border-color 0.15s;
|
|
54276
|
+
caret-color: hsl(25 98% 53%);
|
|
54277
|
+
}
|
|
54278
|
+
|
|
54279
|
+
input[type="email"]:hover, input[type="password"]:hover {
|
|
54280
|
+
border-color: hsl(31 99% 72%);
|
|
54281
|
+
}
|
|
54282
|
+
|
|
54283
|
+
input[type="email"]:focus, input[type="password"]:focus {
|
|
54284
|
+
border-color: hsl(25 98% 53%);
|
|
54285
|
+
box-shadow: inset 0 3px 0 0 rgba(0,0,0,0.025), 0 0 0 2px rgba(249,115,22,0.2);
|
|
54286
|
+
}
|
|
54287
|
+
|
|
54288
|
+
.field { margin-bottom: 0.75rem; }
|
|
54289
|
+
|
|
54290
|
+
hr.separator {
|
|
54291
|
+
border: none;
|
|
54292
|
+
border-top: 1px solid rgba(113,113,122,0.15);
|
|
54293
|
+
margin: 0.5rem 0;
|
|
54294
|
+
}
|
|
54295
|
+
|
|
54296
|
+
.google-icon { width: 18px; height: 18px; }
|
|
54297
|
+
.key-icon { width: 16px; height: 16px; }
|
|
54298
|
+
|
|
54299
|
+
/* Tabs for email/SSO — keep simple, same visual weight */
|
|
54300
|
+
.tabs {
|
|
54301
|
+
display: none;
|
|
54302
|
+
}
|
|
54303
|
+
|
|
54304
|
+
.tab-content { display: none; }
|
|
54305
|
+
.tab-content.active { display: block; }
|
|
54306
|
+
|
|
54307
|
+
.tab-switch {
|
|
54308
|
+
text-align: center;
|
|
54309
|
+
margin-top: 0.25rem;
|
|
54310
|
+
}
|
|
54311
|
+
|
|
54312
|
+
.tab-switch a {
|
|
54313
|
+
font-size: 15px;
|
|
54314
|
+
color: #18181b;
|
|
54315
|
+
text-decoration: underline;
|
|
54316
|
+
text-decoration-color: rgba(251,146,60,0.5);
|
|
54317
|
+
text-underline-offset: 2px;
|
|
54318
|
+
cursor: pointer;
|
|
54319
|
+
}
|
|
54320
|
+
|
|
54321
|
+
.tab-switch a:hover {
|
|
54322
|
+
background: rgba(251,146,60,0.1);
|
|
54323
|
+
border-radius: 2px;
|
|
54324
|
+
}
|
|
54325
|
+
</style>
|
|
54326
|
+
</head>
|
|
54327
|
+
<body>
|
|
54328
|
+
<div class="card">
|
|
54329
|
+
<!-- Logo {k} -->
|
|
54330
|
+
<div class="logo">
|
|
54331
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
54332
|
+
<path opacity="0.15" d="M25.3196 6.25H14.6804C14.6804 7.49264 13.6596 8.5 12.4005 8.5C11.3808 8.5 10.8312 8.67478 10.5466 8.82497C10.3001 8.95506 10.147 9.11941 10.0189 9.38005C9.85482 9.7141 9.74438 10.1712 9.68281 10.8152C9.62136 11.458 9.61405 12.2133 9.61405 13.125L9.61416 13.3731C9.61532 14.9118 9.61694 17.0733 8.75235 18.8332C8.55109 19.2428 8.30266 19.6357 8 20C8.30266 20.3643 8.55109 20.7572 8.75235 21.1668C9.61694 22.9267 9.61532 25.0882 9.61416 26.6269L9.61405 26.875C9.61405 27.7867 9.62136 28.542 9.68281 29.1848C9.74438 29.8288 9.85482 30.2859 10.0189 30.6199C10.147 30.8806 10.3001 31.0449 10.5466 31.175C10.8312 31.3252 11.3808 31.5 12.4005 31.5C13.6596 31.5 14.6804 32.5074 14.6804 33.75H25.3196C25.3196 32.5074 26.3404 31.5 27.5995 31.5C28.6192 31.5 29.1688 31.3252 29.4534 31.175C29.6999 31.0449 29.853 30.8806 29.9811 30.6199C30.1452 30.2859 30.2556 29.8288 30.3172 29.1848C30.3786 28.542 30.386 27.7867 30.386 26.875L30.3858 26.6269C30.3847 25.0882 30.3831 22.9267 31.2477 21.1668C31.4489 20.7572 31.6973 20.3643 32 20C31.6973 19.6357 31.4489 19.2428 31.2477 18.8332C30.3831 17.0733 30.3847 14.9118 30.3858 13.3731L30.386 13.125C30.386 12.2133 30.3786 11.458 30.3172 10.8152C30.2556 10.1712 30.1452 9.7141 29.9811 9.38005C29.853 9.11941 29.6999 8.95506 29.4534 8.82497C29.1688 8.67478 28.6192 8.5 27.5995 8.5C26.3404 8.5 25.3196 7.49264 25.3196 6.25Z" fill="#fd7412"/>
|
|
54333
|
+
<path d="M12.5 6.25C2.5 6.25 12.5 20 2.5 20C12.5 20 2.5 33.75 12.5 33.75" stroke="#fd7412" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/>
|
|
54334
|
+
<path d="M16 10V29" stroke="#18181B" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/>
|
|
54335
|
+
<path d="M27.5 6.25C37.5 6.25 27.5 20 37.5 20C27.5 20 37.5 33.75 27.5 33.75" stroke="#fd7412" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/>
|
|
54336
|
+
<path d="M16 23L25 18" stroke="#18181B" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/>
|
|
54337
|
+
<path d="M25 29L16 23" stroke="#18181B" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/>
|
|
54338
|
+
</svg>
|
|
54339
|
+
</div>
|
|
54340
|
+
|
|
54341
|
+
<!-- Heading -->
|
|
54342
|
+
<h1>Sign in to Kadoa</h1>
|
|
54343
|
+
|
|
54344
|
+
<div class="spacer"></div>
|
|
54345
|
+
|
|
54346
|
+
${errorHtml}
|
|
54347
|
+
|
|
54348
|
+
<!-- Continue with Google -->
|
|
54349
|
+
<form method="POST" action="/auth/google">
|
|
54350
|
+
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
54351
|
+
<button type="submit" class="btn btn-default">
|
|
54352
|
+
<svg class="google-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
54353
|
+
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/>
|
|
54354
|
+
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
|
|
54355
|
+
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
|
54356
|
+
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
|
54357
|
+
</svg>
|
|
54358
|
+
Continue with Google
|
|
54359
|
+
</button>
|
|
54360
|
+
</form>
|
|
54361
|
+
|
|
54362
|
+
<!-- Continue with SSO -->
|
|
54363
|
+
<div id="sso-button-wrapper">
|
|
54364
|
+
<form method="POST" action="/auth/sso" id="sso-direct-form" style="display:none">
|
|
54365
|
+
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
54366
|
+
<input type="hidden" name="email" id="sso-email-hidden" />
|
|
54367
|
+
</form>
|
|
54368
|
+
<button type="button" class="btn btn-default" id="sso-toggle-btn">
|
|
54369
|
+
<svg class="key-icon" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
54370
|
+
<path d="M10 1a5 5 0 0 0-4.546 7.066l-4.161 4.16a.5.5 0 0 0-.146.354V14.5a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5V14h1a.5.5 0 0 0 .5-.5v-1h1a.5.5 0 0 0 .354-.146l.94-.94A5 5 0 1 0 10 1zm1.5 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" fill="currentColor"/>
|
|
54371
|
+
</svg>
|
|
54372
|
+
Continue with SSO
|
|
54373
|
+
</button>
|
|
54374
|
+
</div>
|
|
54375
|
+
|
|
54376
|
+
<!-- OR divider -->
|
|
54377
|
+
<div class="line-or">
|
|
54378
|
+
<hr />
|
|
54379
|
+
OR
|
|
54380
|
+
<hr />
|
|
54381
|
+
</div>
|
|
54382
|
+
|
|
54383
|
+
<!-- Email + Password form -->
|
|
54384
|
+
<div class="tab-content active" id="tab-email">
|
|
54385
|
+
<form method="POST" action="/auth/login">
|
|
54386
|
+
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
54387
|
+
<div class="field">
|
|
54388
|
+
<label for="email">Sign in with email:</label>
|
|
54389
|
+
<input type="email" id="email" name="email" required autocomplete="email" />
|
|
54390
|
+
</div>
|
|
54391
|
+
<div class="field">
|
|
54392
|
+
<label for="password">Your password:</label>
|
|
54393
|
+
<input type="password" id="password" name="password" required autocomplete="current-password" />
|
|
54394
|
+
</div>
|
|
54395
|
+
<button type="submit" class="btn btn-primary">Continue</button>
|
|
54396
|
+
</form>
|
|
54397
|
+
</div>
|
|
54398
|
+
|
|
54399
|
+
<!-- SSO form (shown when "Continue with SSO" is clicked) -->
|
|
54400
|
+
<div class="tab-content" id="tab-sso">
|
|
54401
|
+
<form method="POST" action="/auth/sso">
|
|
54402
|
+
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
54403
|
+
<div class="field">
|
|
54404
|
+
<label for="sso-email">Work email:</label>
|
|
54405
|
+
<input type="email" id="sso-email" name="email" required autocomplete="email" />
|
|
54406
|
+
</div>
|
|
54407
|
+
<button type="submit" class="btn btn-primary">Continue with SSO</button>
|
|
54408
|
+
</form>
|
|
54409
|
+
<div class="tab-switch">
|
|
54410
|
+
<a id="back-to-email">Sign in with email instead</a>
|
|
54411
|
+
</div>
|
|
54412
|
+
</div>
|
|
54413
|
+
|
|
54414
|
+
<div style="flex:1"></div>
|
|
54415
|
+
|
|
54416
|
+
<script>
|
|
54417
|
+
var ssoBtn = document.getElementById('sso-toggle-btn');
|
|
54418
|
+
var tabEmail = document.getElementById('tab-email');
|
|
54419
|
+
var tabSso = document.getElementById('tab-sso');
|
|
54420
|
+
var ssoWrapper = document.getElementById('sso-button-wrapper');
|
|
54421
|
+
var lineOr = document.querySelector('.line-or');
|
|
54422
|
+
var backLink = document.getElementById('back-to-email');
|
|
54423
|
+
|
|
54424
|
+
ssoBtn.addEventListener('click', function() {
|
|
54425
|
+
tabEmail.classList.remove('active');
|
|
54426
|
+
tabSso.classList.add('active');
|
|
54427
|
+
ssoWrapper.style.display = 'none';
|
|
54428
|
+
lineOr.style.display = 'none';
|
|
54429
|
+
document.getElementById('sso-email').focus();
|
|
54430
|
+
});
|
|
54431
|
+
|
|
54432
|
+
backLink.addEventListener('click', function() {
|
|
54433
|
+
tabSso.classList.remove('active');
|
|
54434
|
+
tabEmail.classList.add('active');
|
|
54435
|
+
ssoWrapper.style.display = '';
|
|
54436
|
+
lineOr.style.display = '';
|
|
54437
|
+
});
|
|
54438
|
+
</script>
|
|
54439
|
+
</div>
|
|
54440
|
+
</body>
|
|
54441
|
+
</html>`;
|
|
54442
|
+
}
|
|
54020
54443
|
function escapeHtml(str) {
|
|
54021
54444
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54022
54445
|
}
|
|
@@ -54098,6 +54521,15 @@ async function startHttpServer() {
|
|
|
54098
54521
|
app.get("/auth/callback", (req, res) => {
|
|
54099
54522
|
provider.handleAuthCallback(req, res);
|
|
54100
54523
|
});
|
|
54524
|
+
app.post("/auth/google", express8.urlencoded({ extended: false }), (req, res) => {
|
|
54525
|
+
provider.handleGoogleLogin(req, res);
|
|
54526
|
+
});
|
|
54527
|
+
app.post("/auth/login", express8.urlencoded({ extended: false }), (req, res) => {
|
|
54528
|
+
provider.handleEmailPasswordLogin(req, res);
|
|
54529
|
+
});
|
|
54530
|
+
app.post("/auth/sso", express8.urlencoded({ extended: false }), (req, res) => {
|
|
54531
|
+
provider.handleSSOLogin(req, res);
|
|
54532
|
+
});
|
|
54101
54533
|
app.post("/team-select", express8.urlencoded({ extended: false }), (req, res) => {
|
|
54102
54534
|
provider.handleTeamSelection(req, res);
|
|
54103
54535
|
});
|