@emulators/vercel 0.4.1 → 0.6.0
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/fonts/favicon.ico +0 -0
- package/dist/index.js +210 -47
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,10 @@ function getVercelStore(store) {
|
|
|
6
6
|
teamMembers: store.collection("vercel.team_members", ["teamId", "userId"]),
|
|
7
7
|
projects: store.collection("vercel.projects", ["uid", "name", "accountId"]),
|
|
8
8
|
deployments: store.collection("vercel.deployments", ["uid", "projectId", "url"]),
|
|
9
|
-
deploymentAliases: store.collection("vercel.deployment_aliases", [
|
|
9
|
+
deploymentAliases: store.collection("vercel.deployment_aliases", [
|
|
10
|
+
"deploymentId",
|
|
11
|
+
"projectId"
|
|
12
|
+
]),
|
|
10
13
|
builds: store.collection("vercel.builds", ["deploymentId"]),
|
|
11
14
|
deploymentEvents: store.collection("vercel.deployment_events", ["deploymentId"]),
|
|
12
15
|
files: store.collection("vercel.files", ["digest"]),
|
|
@@ -51,7 +54,7 @@ function resolveTeamScope(c, vs) {
|
|
|
51
54
|
return { accountId: user.uid, team: null };
|
|
52
55
|
}
|
|
53
56
|
function lookupProject(vs, idOrName, accountId) {
|
|
54
|
-
|
|
57
|
+
const project = vs.projects.findOneBy("uid", idOrName);
|
|
55
58
|
if (project && project.accountId === accountId) return project;
|
|
56
59
|
const byName = vs.projects.findBy("name", idOrName);
|
|
57
60
|
return byName.find((p) => p.accountId === accountId);
|
|
@@ -242,8 +245,6 @@ function formatEnvVar(env, decrypt = false) {
|
|
|
242
245
|
}
|
|
243
246
|
|
|
244
247
|
// ../core/dist/index.js
|
|
245
|
-
import { Hono } from "hono";
|
|
246
|
-
import { cors } from "hono/cors";
|
|
247
248
|
import { readFileSync } from "fs";
|
|
248
249
|
import { fileURLToPath } from "url";
|
|
249
250
|
import { dirname, join } from "path";
|
|
@@ -287,6 +288,7 @@ var FONTS = {
|
|
|
287
288
|
"geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
|
|
288
289
|
"GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
289
290
|
};
|
|
291
|
+
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
|
|
290
292
|
function escapeHtml(s) {
|
|
291
293
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
292
294
|
}
|
|
@@ -438,6 +440,132 @@ body{
|
|
|
438
440
|
.app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
|
|
439
441
|
.app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
440
442
|
.empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
|
|
443
|
+
|
|
444
|
+
.inspector-layout{max-width:960px;margin:0 auto;padding:28px 20px;}
|
|
445
|
+
.inspector-tabs{display:flex;gap:4px;margin-bottom:20px;}
|
|
446
|
+
.inspector-tabs a{
|
|
447
|
+
padding:7px 16px;border-radius:6px;text-decoration:none;
|
|
448
|
+
font-size:.8125rem;color:#1a8c00;border:1px solid transparent;
|
|
449
|
+
transition:color .15s,border-color .15s;
|
|
450
|
+
}
|
|
451
|
+
.inspector-tabs a:hover{color:#33ff00;}
|
|
452
|
+
.inspector-tabs a.active{color:#33ff00;font-weight:600;border-color:#0a3300;background:#0a3300;}
|
|
453
|
+
.inspector-section{margin-bottom:24px;}
|
|
454
|
+
.inspector-section h2{
|
|
455
|
+
font-family:'Geist Pixel',monospace;
|
|
456
|
+
font-size:1rem;font-weight:600;color:#33ff00;margin-bottom:10px;
|
|
457
|
+
}
|
|
458
|
+
.inspector-section h3{
|
|
459
|
+
font-family:'Geist Pixel',monospace;
|
|
460
|
+
font-size:.875rem;font-weight:600;color:#1a8c00;margin:16px 0 8px;
|
|
461
|
+
}
|
|
462
|
+
.inspector-table{width:100%;border-collapse:collapse;margin-bottom:12px;}
|
|
463
|
+
.inspector-table th,.inspector-table td{
|
|
464
|
+
text-align:left;padding:8px 12px;border-bottom:1px solid #0a3300;
|
|
465
|
+
font-size:.8125rem;
|
|
466
|
+
}
|
|
467
|
+
.inspector-table th{color:#1a8c00;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;}
|
|
468
|
+
.inspector-table td{color:#33ff00;}
|
|
469
|
+
.inspector-table tbody tr{transition:background .1s;}
|
|
470
|
+
.inspector-table tbody tr:hover{background:#0a3300;}
|
|
471
|
+
.inspector-empty{color:#1a8c00;text-align:center;padding:20px 0;font-size:.8125rem;}
|
|
472
|
+
|
|
473
|
+
.checkout-layout{
|
|
474
|
+
display:flex;min-height:calc(100vh - 42px);
|
|
475
|
+
}
|
|
476
|
+
.checkout-summary{
|
|
477
|
+
flex:1;background:#020;padding:48px 40px 48px 10%;
|
|
478
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
479
|
+
border-right:1px solid #0a3300;
|
|
480
|
+
}
|
|
481
|
+
.checkout-form-side{
|
|
482
|
+
flex:1;background:#000;padding:48px 10% 48px 40px;
|
|
483
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
484
|
+
}
|
|
485
|
+
.checkout-merchant{
|
|
486
|
+
display:flex;align-items:center;gap:10px;margin-bottom:6px;
|
|
487
|
+
}
|
|
488
|
+
.checkout-merchant-name{
|
|
489
|
+
font-family:'Geist Pixel',monospace;
|
|
490
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
491
|
+
}
|
|
492
|
+
.checkout-test-badge{
|
|
493
|
+
font-size:.625rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
|
|
494
|
+
background:#0a3300;color:#1a8c00;padding:2px 8px;border-radius:4px;
|
|
495
|
+
}
|
|
496
|
+
.checkout-total{
|
|
497
|
+
font-family:'Geist Pixel',monospace;
|
|
498
|
+
font-size:2rem;font-weight:700;color:#33ff00;margin:8px 0 28px;
|
|
499
|
+
}
|
|
500
|
+
.checkout-line-item{
|
|
501
|
+
display:flex;align-items:center;gap:14px;padding:14px 0;
|
|
502
|
+
border-bottom:1px solid #0a3300;
|
|
503
|
+
}
|
|
504
|
+
.checkout-line-item:first-child{border-top:1px solid #0a3300;}
|
|
505
|
+
.checkout-item-icon{
|
|
506
|
+
width:42px;height:42px;border-radius:6px;background:#0a3300;
|
|
507
|
+
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
508
|
+
font-family:'Geist Pixel',monospace;font-size:.875rem;font-weight:700;color:#116600;
|
|
509
|
+
}
|
|
510
|
+
.checkout-item-details{flex:1;min-width:0;}
|
|
511
|
+
.checkout-item-name{font-size:.875rem;font-weight:600;color:#33ff00;}
|
|
512
|
+
.checkout-item-qty{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
513
|
+
.checkout-item-price{
|
|
514
|
+
font-size:.875rem;font-weight:600;color:#33ff00;text-align:right;white-space:nowrap;
|
|
515
|
+
}
|
|
516
|
+
.checkout-item-unit{font-size:.6875rem;color:#1a8c00;text-align:right;margin-top:2px;}
|
|
517
|
+
.checkout-totals{margin-top:20px;}
|
|
518
|
+
.checkout-totals-row{
|
|
519
|
+
display:flex;justify-content:space-between;padding:6px 0;
|
|
520
|
+
font-size:.8125rem;color:#1a8c00;
|
|
521
|
+
}
|
|
522
|
+
.checkout-totals-row.total{
|
|
523
|
+
border-top:1px solid #0a3300;margin-top:8px;padding-top:14px;
|
|
524
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
525
|
+
}
|
|
526
|
+
.checkout-form-section{margin-bottom:24px;}
|
|
527
|
+
.checkout-form-label{
|
|
528
|
+
font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:8px;display:block;
|
|
529
|
+
}
|
|
530
|
+
.checkout-input{
|
|
531
|
+
width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
|
|
532
|
+
background:#020;color:#33ff00;font:inherit;font-size:.875rem;
|
|
533
|
+
transition:border-color .15s;outline:none;
|
|
534
|
+
}
|
|
535
|
+
.checkout-input:focus{border-color:#33ff00;}
|
|
536
|
+
.checkout-input::placeholder{color:#116600;}
|
|
537
|
+
.checkout-card-box{
|
|
538
|
+
border:1px solid #0a3300;border-radius:6px;padding:14px;
|
|
539
|
+
background:#020;
|
|
540
|
+
}
|
|
541
|
+
.checkout-card-row{
|
|
542
|
+
display:flex;gap:12px;margin-top:10px;
|
|
543
|
+
}
|
|
544
|
+
.checkout-card-row .checkout-input{flex:1;}
|
|
545
|
+
.checkout-sim-note{
|
|
546
|
+
font-size:.6875rem;color:#1a8c00;margin-top:10px;text-align:center;
|
|
547
|
+
font-style:italic;
|
|
548
|
+
}
|
|
549
|
+
.checkout-pay-btn{
|
|
550
|
+
width:100%;padding:14px;border:none;border-radius:8px;
|
|
551
|
+
background:#33ff00;color:#000;font:inherit;font-size:.9375rem;font-weight:700;
|
|
552
|
+
cursor:pointer;transition:background .15s;
|
|
553
|
+
font-family:'Geist Pixel',monospace;
|
|
554
|
+
}
|
|
555
|
+
.checkout-pay-btn:hover{background:#44ff22;}
|
|
556
|
+
.checkout-cancel{
|
|
557
|
+
text-align:center;margin-top:14px;
|
|
558
|
+
}
|
|
559
|
+
.checkout-cancel a{
|
|
560
|
+
color:#1a8c00;text-decoration:none;font-size:.8125rem;
|
|
561
|
+
transition:color .15s;
|
|
562
|
+
}
|
|
563
|
+
.checkout-cancel a:hover{color:#33ff00;}
|
|
564
|
+
@media(max-width:768px){
|
|
565
|
+
.checkout-layout{flex-direction:column;}
|
|
566
|
+
.checkout-summary{padding:32px 20px;border-right:none;border-bottom:1px solid #0a3300;}
|
|
567
|
+
.checkout-form-side{padding:32px 20px;}
|
|
568
|
+
}
|
|
441
569
|
`;
|
|
442
570
|
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://emulate.dev" target="_blank" rel="noopener">emulate</a></div>`;
|
|
443
571
|
function emuBar(service) {
|
|
@@ -457,6 +585,7 @@ function head(title) {
|
|
|
457
585
|
<head>
|
|
458
586
|
<meta charset="utf-8"/>
|
|
459
587
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
588
|
+
<link rel="icon" href="/_emulate/favicon.ico"/>
|
|
460
589
|
<title>${escapeHtml(title)} | emulate</title>
|
|
461
590
|
<style>${CSS}</style>
|
|
462
591
|
</head>`;
|
|
@@ -938,7 +1067,9 @@ function projectsRoutes({ app, store, baseUrl }) {
|
|
|
938
1067
|
key,
|
|
939
1068
|
value: typeof ev.value === "string" ? ev.value : String(ev.value ?? ""),
|
|
940
1069
|
type: ev.type === "system" || ev.type === "encrypted" || ev.type === "plain" || ev.type === "secret" || ev.type === "sensitive" ? ev.type : "encrypted",
|
|
941
|
-
target: Array.isArray(ev.target) ? ev.target.filter(
|
|
1070
|
+
target: Array.isArray(ev.target) ? ev.target.filter(
|
|
1071
|
+
(t) => t === "production" || t === "preview" || t === "development"
|
|
1072
|
+
) : ["production", "preview", "development"],
|
|
942
1073
|
gitBranch: typeof ev.gitBranch === "string" ? ev.gitBranch : null,
|
|
943
1074
|
customEnvironmentIds: Array.isArray(ev.customEnvironmentIds) ? ev.customEnvironmentIds : [],
|
|
944
1075
|
comment: typeof ev.comment === "string" ? ev.comment : null,
|
|
@@ -1927,11 +2058,17 @@ function parseEnvRow(body) {
|
|
|
1927
2058
|
}
|
|
1928
2059
|
const type = parseType(body.type);
|
|
1929
2060
|
if (type === "invalid") {
|
|
1930
|
-
return {
|
|
2061
|
+
return {
|
|
2062
|
+
row: {},
|
|
2063
|
+
error: "Invalid value: type must be one of system, encrypted, plain, secret, sensitive"
|
|
2064
|
+
};
|
|
1931
2065
|
}
|
|
1932
2066
|
const target = parseTarget(body.target);
|
|
1933
2067
|
if (target === "invalid") {
|
|
1934
|
-
return {
|
|
2068
|
+
return {
|
|
2069
|
+
row: {},
|
|
2070
|
+
error: "Invalid value: target must be a non-empty array of production, preview, development"
|
|
2071
|
+
};
|
|
1935
2072
|
}
|
|
1936
2073
|
const customEnvironmentIds = parseCustomEnvironmentIds(body.customEnvironmentIds);
|
|
1937
2074
|
if (customEnvironmentIds === "invalid") {
|
|
@@ -2039,9 +2176,7 @@ function envRoutes({ app, store }) {
|
|
|
2039
2176
|
}
|
|
2040
2177
|
const { row } = parsed;
|
|
2041
2178
|
const existingDb = findEnvByKeyAndTargetsOverlap(vs, project.uid, row.key, row.target);
|
|
2042
|
-
const existingPending = pending.find(
|
|
2043
|
-
(e) => e.key === row.key && targetsOverlap(e.target, row.target)
|
|
2044
|
-
);
|
|
2179
|
+
const existingPending = pending.find((e) => e.key === row.key && targetsOverlap(e.target, row.target));
|
|
2045
2180
|
if (upsert) {
|
|
2046
2181
|
const toUpdate = existingDb ?? existingPending;
|
|
2047
2182
|
if (toUpdate) {
|
|
@@ -2140,14 +2275,24 @@ function envRoutes({ app, store }) {
|
|
|
2140
2275
|
if ("type" in body) {
|
|
2141
2276
|
const t = parseType(body.type);
|
|
2142
2277
|
if (t === "invalid") {
|
|
2143
|
-
return vercelErr5(
|
|
2278
|
+
return vercelErr5(
|
|
2279
|
+
c,
|
|
2280
|
+
400,
|
|
2281
|
+
"bad_request",
|
|
2282
|
+
"Invalid value: type must be one of system, encrypted, plain, secret, sensitive"
|
|
2283
|
+
);
|
|
2144
2284
|
}
|
|
2145
2285
|
patch.type = t;
|
|
2146
2286
|
}
|
|
2147
2287
|
if ("target" in body) {
|
|
2148
2288
|
const t = parseTarget(body.target);
|
|
2149
2289
|
if (t === "invalid") {
|
|
2150
|
-
return vercelErr5(
|
|
2290
|
+
return vercelErr5(
|
|
2291
|
+
c,
|
|
2292
|
+
400,
|
|
2293
|
+
"bad_request",
|
|
2294
|
+
"Invalid value: target must be a non-empty array of production, preview, development"
|
|
2295
|
+
);
|
|
2151
2296
|
}
|
|
2152
2297
|
patch.target = t;
|
|
2153
2298
|
}
|
|
@@ -2245,11 +2390,23 @@ function oauthRoutes({ app, store, tokenMap }) {
|
|
|
2245
2390
|
if (integrationsConfigured) {
|
|
2246
2391
|
const integration = vs.integrations.findOneBy("client_id", client_id);
|
|
2247
2392
|
if (!integration) {
|
|
2248
|
-
return c.html(
|
|
2393
|
+
return c.html(
|
|
2394
|
+
renderErrorPage("Application not found", `The client_id '${client_id}' is not registered.`, SERVICE_LABEL),
|
|
2395
|
+
400
|
|
2396
|
+
);
|
|
2249
2397
|
}
|
|
2250
2398
|
if (redirect_uri && !matchesRedirectUri(redirect_uri, integration.redirect_uris)) {
|
|
2251
|
-
console.warn(
|
|
2252
|
-
|
|
2399
|
+
console.warn(
|
|
2400
|
+
`[OAuth] redirect_uri mismatch: got "${redirect_uri}", registered: ${JSON.stringify(integration.redirect_uris)}`
|
|
2401
|
+
);
|
|
2402
|
+
return c.html(
|
|
2403
|
+
renderErrorPage(
|
|
2404
|
+
"Redirect URI mismatch",
|
|
2405
|
+
"The redirect_uri is not registered for this application.",
|
|
2406
|
+
SERVICE_LABEL
|
|
2407
|
+
),
|
|
2408
|
+
400
|
|
2409
|
+
);
|
|
2253
2410
|
}
|
|
2254
2411
|
integrationName = integration.name;
|
|
2255
2412
|
}
|
|
@@ -2297,7 +2454,10 @@ function oauthRoutes({ app, store, tokenMap }) {
|
|
|
2297
2454
|
codeChallengeMethod: code_challenge_method || null,
|
|
2298
2455
|
created_at: Date.now()
|
|
2299
2456
|
});
|
|
2300
|
-
debug(
|
|
2457
|
+
debug(
|
|
2458
|
+
"vercel.oauth",
|
|
2459
|
+
`[Vercel callback] generated code: ${code.slice(0, 8)}... for username=${username}, challenge=${code_challenge ? "present" : "none"}, pendingCodes size: ${pendingCodes.size}`
|
|
2460
|
+
);
|
|
2301
2461
|
const url = new URL(redirect_uri);
|
|
2302
2462
|
url.searchParams.set("code", code);
|
|
2303
2463
|
if (state !== "") url.searchParams.set("state", state);
|
|
@@ -2309,7 +2469,10 @@ function oauthRoutes({ app, store, tokenMap }) {
|
|
|
2309
2469
|
const pendingCodes = getPendingCodes(store);
|
|
2310
2470
|
debug("vercel.oauth", `[Vercel token] Content-Type: ${contentType}`);
|
|
2311
2471
|
debug("vercel.oauth", `[Vercel token] pendingCodes size: ${pendingCodes.size}`);
|
|
2312
|
-
debug(
|
|
2472
|
+
debug(
|
|
2473
|
+
"vercel.oauth",
|
|
2474
|
+
`[Vercel token] pendingCodes keys: ${[...pendingCodes.keys()].map((k) => k.slice(0, 8) + "...").join(", ")}`
|
|
2475
|
+
);
|
|
2313
2476
|
const rawText = await c.req.text();
|
|
2314
2477
|
debug("vercel.oauth", `[Vercel token] raw body: ${rawText.slice(0, 500)}`);
|
|
2315
2478
|
let body;
|
|
@@ -2331,73 +2494,70 @@ function oauthRoutes({ app, store, tokenMap }) {
|
|
|
2331
2494
|
debug("vercel.oauth", `[Vercel token] code: ${code.slice(0, 8)}... (len=${code.length})`);
|
|
2332
2495
|
debug("vercel.oauth", `[Vercel token] client_id: ${bodyClientId}`);
|
|
2333
2496
|
debug("vercel.oauth", `[Vercel token] client_secret: ${bodyClientSecret.slice(0, 4)}****`);
|
|
2334
|
-
debug(
|
|
2497
|
+
debug(
|
|
2498
|
+
"vercel.oauth",
|
|
2499
|
+
`[Vercel token] code_verifier: ${code_verifier ? code_verifier.slice(0, 8) + "..." : "undefined"}`
|
|
2500
|
+
);
|
|
2335
2501
|
const integrationsConfigured = vs.integrations.all().length > 0;
|
|
2336
2502
|
if (integrationsConfigured) {
|
|
2337
2503
|
const integration = vs.integrations.findOneBy("client_id", bodyClientId);
|
|
2338
2504
|
if (!integration) {
|
|
2339
2505
|
debug("vercel.oauth", `[Vercel token] REJECTED: client_id not found`);
|
|
2340
|
-
return c.json(
|
|
2506
|
+
return c.json(
|
|
2507
|
+
{ error: "invalid_client", error_description: "The client_id and/or client_secret passed are incorrect." },
|
|
2508
|
+
401
|
|
2509
|
+
);
|
|
2341
2510
|
}
|
|
2342
2511
|
if (!constantTimeSecretEqual(bodyClientSecret, integration.client_secret)) {
|
|
2343
2512
|
debug("vercel.oauth", `[Vercel token] REJECTED: client_secret mismatch`);
|
|
2344
|
-
return c.json(
|
|
2513
|
+
return c.json(
|
|
2514
|
+
{ error: "invalid_client", error_description: "The client_id and/or client_secret passed are incorrect." },
|
|
2515
|
+
401
|
|
2516
|
+
);
|
|
2345
2517
|
}
|
|
2346
2518
|
debug("vercel.oauth", `[Vercel token] client credentials OK (${integration.name})`);
|
|
2347
2519
|
}
|
|
2348
2520
|
const pending = pendingCodes.get(code);
|
|
2349
2521
|
if (!pending) {
|
|
2350
2522
|
debug("vercel.oauth", `[Vercel token] REJECTED: code not found in pendingCodes`);
|
|
2351
|
-
return c.json(
|
|
2352
|
-
{ error: "invalid_grant", error_description: "The code passed is incorrect or expired." },
|
|
2353
|
-
400
|
|
2354
|
-
);
|
|
2523
|
+
return c.json({ error: "invalid_grant", error_description: "The code passed is incorrect or expired." }, 400);
|
|
2355
2524
|
}
|
|
2356
2525
|
if (isPendingCodeExpired(pending)) {
|
|
2357
2526
|
debug("vercel.oauth", `[Vercel token] REJECTED: code expired`);
|
|
2358
2527
|
pendingCodes.delete(code);
|
|
2359
|
-
return c.json(
|
|
2360
|
-
{ error: "invalid_grant", error_description: "The code passed is incorrect or expired." },
|
|
2361
|
-
400
|
|
2362
|
-
);
|
|
2528
|
+
return c.json({ error: "invalid_grant", error_description: "The code passed is incorrect or expired." }, 400);
|
|
2363
2529
|
}
|
|
2364
2530
|
debug("vercel.oauth", `[Vercel token] code valid, username=${pending.username}, scope=${pending.scope}`);
|
|
2365
2531
|
if (redirect_uri && pending.redirectUri && redirect_uri !== pending.redirectUri) {
|
|
2366
|
-
debug(
|
|
2532
|
+
debug(
|
|
2533
|
+
"vercel.oauth",
|
|
2534
|
+
`[Vercel token] REJECTED: redirect_uri mismatch (got "${redirect_uri}", expected "${pending.redirectUri}")`
|
|
2535
|
+
);
|
|
2367
2536
|
pendingCodes.delete(code);
|
|
2368
2537
|
return c.json(
|
|
2369
|
-
{
|
|
2538
|
+
{
|
|
2539
|
+
error: "invalid_grant",
|
|
2540
|
+
error_description: "The redirect_uri does not match the one used during authorization."
|
|
2541
|
+
},
|
|
2370
2542
|
400
|
|
2371
2543
|
);
|
|
2372
2544
|
}
|
|
2373
2545
|
if (pending.codeChallenge != null) {
|
|
2374
2546
|
if (code_verifier === void 0) {
|
|
2375
|
-
return c.json(
|
|
2376
|
-
{ error: "invalid_grant", error_description: "PKCE verification failed." },
|
|
2377
|
-
400
|
|
2378
|
-
);
|
|
2547
|
+
return c.json({ error: "invalid_grant", error_description: "PKCE verification failed." }, 400);
|
|
2379
2548
|
}
|
|
2380
2549
|
const method = (pending.codeChallengeMethod ?? "plain").toLowerCase();
|
|
2381
2550
|
if (method === "s256") {
|
|
2382
2551
|
const expected = createHash("sha256").update(code_verifier).digest("base64url");
|
|
2383
2552
|
if (expected !== pending.codeChallenge) {
|
|
2384
|
-
return c.json(
|
|
2385
|
-
{ error: "invalid_grant", error_description: "PKCE verification failed." },
|
|
2386
|
-
400
|
|
2387
|
-
);
|
|
2553
|
+
return c.json({ error: "invalid_grant", error_description: "PKCE verification failed." }, 400);
|
|
2388
2554
|
}
|
|
2389
2555
|
} else if (method === "plain") {
|
|
2390
2556
|
if (code_verifier !== pending.codeChallenge) {
|
|
2391
|
-
return c.json(
|
|
2392
|
-
{ error: "invalid_grant", error_description: "PKCE verification failed." },
|
|
2393
|
-
400
|
|
2394
|
-
);
|
|
2557
|
+
return c.json({ error: "invalid_grant", error_description: "PKCE verification failed." }, 400);
|
|
2395
2558
|
}
|
|
2396
2559
|
} else {
|
|
2397
|
-
return c.json(
|
|
2398
|
-
{ error: "invalid_grant", error_description: "PKCE verification failed." },
|
|
2399
|
-
400
|
|
2400
|
-
);
|
|
2560
|
+
return c.json({ error: "invalid_grant", error_description: "PKCE verification failed." }, 400);
|
|
2401
2561
|
}
|
|
2402
2562
|
}
|
|
2403
2563
|
debug("vercel.oauth", `[Vercel token] PKCE OK (challenge=${pending.codeChallenge ? "present" : "none"})`);
|
|
@@ -2415,7 +2575,10 @@ function oauthRoutes({ app, store, tokenMap }) {
|
|
|
2415
2575
|
if (tokenMap) {
|
|
2416
2576
|
tokenMap.set(token, { login: user.username, id: user.id, scopes });
|
|
2417
2577
|
}
|
|
2418
|
-
debug(
|
|
2578
|
+
debug(
|
|
2579
|
+
"vercel.oauth",
|
|
2580
|
+
`[Vercel token] SUCCESS: issued token for ${user.username} (scopes: ${scopes.join(",") || "none"})`
|
|
2581
|
+
);
|
|
2419
2582
|
return c.json({
|
|
2420
2583
|
access_token: token,
|
|
2421
2584
|
token_type: "Bearer",
|