@emulators/okta 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 +215 -75
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -99,8 +99,6 @@ function createDefaultApp() {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// ../core/dist/index.js
|
|
102
|
-
import { Hono } from "hono";
|
|
103
|
-
import { cors } from "hono/cors";
|
|
104
102
|
import { jwtVerify, importPKCS8 } from "jose";
|
|
105
103
|
import { readFileSync } from "fs";
|
|
106
104
|
import { fileURLToPath } from "url";
|
|
@@ -126,6 +124,7 @@ var FONTS = {
|
|
|
126
124
|
"geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
|
|
127
125
|
"GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
128
126
|
};
|
|
127
|
+
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
|
|
129
128
|
function parsePagination(c) {
|
|
130
129
|
const page = Math.max(1, parseInt(c.req.query("page") ?? "1", 10) || 1);
|
|
131
130
|
const per_page = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
|
|
@@ -303,6 +302,132 @@ body{
|
|
|
303
302
|
.app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
|
|
304
303
|
.app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
305
304
|
.empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
|
|
305
|
+
|
|
306
|
+
.inspector-layout{max-width:960px;margin:0 auto;padding:28px 20px;}
|
|
307
|
+
.inspector-tabs{display:flex;gap:4px;margin-bottom:20px;}
|
|
308
|
+
.inspector-tabs a{
|
|
309
|
+
padding:7px 16px;border-radius:6px;text-decoration:none;
|
|
310
|
+
font-size:.8125rem;color:#1a8c00;border:1px solid transparent;
|
|
311
|
+
transition:color .15s,border-color .15s;
|
|
312
|
+
}
|
|
313
|
+
.inspector-tabs a:hover{color:#33ff00;}
|
|
314
|
+
.inspector-tabs a.active{color:#33ff00;font-weight:600;border-color:#0a3300;background:#0a3300;}
|
|
315
|
+
.inspector-section{margin-bottom:24px;}
|
|
316
|
+
.inspector-section h2{
|
|
317
|
+
font-family:'Geist Pixel',monospace;
|
|
318
|
+
font-size:1rem;font-weight:600;color:#33ff00;margin-bottom:10px;
|
|
319
|
+
}
|
|
320
|
+
.inspector-section h3{
|
|
321
|
+
font-family:'Geist Pixel',monospace;
|
|
322
|
+
font-size:.875rem;font-weight:600;color:#1a8c00;margin:16px 0 8px;
|
|
323
|
+
}
|
|
324
|
+
.inspector-table{width:100%;border-collapse:collapse;margin-bottom:12px;}
|
|
325
|
+
.inspector-table th,.inspector-table td{
|
|
326
|
+
text-align:left;padding:8px 12px;border-bottom:1px solid #0a3300;
|
|
327
|
+
font-size:.8125rem;
|
|
328
|
+
}
|
|
329
|
+
.inspector-table th{color:#1a8c00;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;}
|
|
330
|
+
.inspector-table td{color:#33ff00;}
|
|
331
|
+
.inspector-table tbody tr{transition:background .1s;}
|
|
332
|
+
.inspector-table tbody tr:hover{background:#0a3300;}
|
|
333
|
+
.inspector-empty{color:#1a8c00;text-align:center;padding:20px 0;font-size:.8125rem;}
|
|
334
|
+
|
|
335
|
+
.checkout-layout{
|
|
336
|
+
display:flex;min-height:calc(100vh - 42px);
|
|
337
|
+
}
|
|
338
|
+
.checkout-summary{
|
|
339
|
+
flex:1;background:#020;padding:48px 40px 48px 10%;
|
|
340
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
341
|
+
border-right:1px solid #0a3300;
|
|
342
|
+
}
|
|
343
|
+
.checkout-form-side{
|
|
344
|
+
flex:1;background:#000;padding:48px 10% 48px 40px;
|
|
345
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
346
|
+
}
|
|
347
|
+
.checkout-merchant{
|
|
348
|
+
display:flex;align-items:center;gap:10px;margin-bottom:6px;
|
|
349
|
+
}
|
|
350
|
+
.checkout-merchant-name{
|
|
351
|
+
font-family:'Geist Pixel',monospace;
|
|
352
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
353
|
+
}
|
|
354
|
+
.checkout-test-badge{
|
|
355
|
+
font-size:.625rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
|
|
356
|
+
background:#0a3300;color:#1a8c00;padding:2px 8px;border-radius:4px;
|
|
357
|
+
}
|
|
358
|
+
.checkout-total{
|
|
359
|
+
font-family:'Geist Pixel',monospace;
|
|
360
|
+
font-size:2rem;font-weight:700;color:#33ff00;margin:8px 0 28px;
|
|
361
|
+
}
|
|
362
|
+
.checkout-line-item{
|
|
363
|
+
display:flex;align-items:center;gap:14px;padding:14px 0;
|
|
364
|
+
border-bottom:1px solid #0a3300;
|
|
365
|
+
}
|
|
366
|
+
.checkout-line-item:first-child{border-top:1px solid #0a3300;}
|
|
367
|
+
.checkout-item-icon{
|
|
368
|
+
width:42px;height:42px;border-radius:6px;background:#0a3300;
|
|
369
|
+
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
370
|
+
font-family:'Geist Pixel',monospace;font-size:.875rem;font-weight:700;color:#116600;
|
|
371
|
+
}
|
|
372
|
+
.checkout-item-details{flex:1;min-width:0;}
|
|
373
|
+
.checkout-item-name{font-size:.875rem;font-weight:600;color:#33ff00;}
|
|
374
|
+
.checkout-item-qty{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
375
|
+
.checkout-item-price{
|
|
376
|
+
font-size:.875rem;font-weight:600;color:#33ff00;text-align:right;white-space:nowrap;
|
|
377
|
+
}
|
|
378
|
+
.checkout-item-unit{font-size:.6875rem;color:#1a8c00;text-align:right;margin-top:2px;}
|
|
379
|
+
.checkout-totals{margin-top:20px;}
|
|
380
|
+
.checkout-totals-row{
|
|
381
|
+
display:flex;justify-content:space-between;padding:6px 0;
|
|
382
|
+
font-size:.8125rem;color:#1a8c00;
|
|
383
|
+
}
|
|
384
|
+
.checkout-totals-row.total{
|
|
385
|
+
border-top:1px solid #0a3300;margin-top:8px;padding-top:14px;
|
|
386
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
387
|
+
}
|
|
388
|
+
.checkout-form-section{margin-bottom:24px;}
|
|
389
|
+
.checkout-form-label{
|
|
390
|
+
font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:8px;display:block;
|
|
391
|
+
}
|
|
392
|
+
.checkout-input{
|
|
393
|
+
width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
|
|
394
|
+
background:#020;color:#33ff00;font:inherit;font-size:.875rem;
|
|
395
|
+
transition:border-color .15s;outline:none;
|
|
396
|
+
}
|
|
397
|
+
.checkout-input:focus{border-color:#33ff00;}
|
|
398
|
+
.checkout-input::placeholder{color:#116600;}
|
|
399
|
+
.checkout-card-box{
|
|
400
|
+
border:1px solid #0a3300;border-radius:6px;padding:14px;
|
|
401
|
+
background:#020;
|
|
402
|
+
}
|
|
403
|
+
.checkout-card-row{
|
|
404
|
+
display:flex;gap:12px;margin-top:10px;
|
|
405
|
+
}
|
|
406
|
+
.checkout-card-row .checkout-input{flex:1;}
|
|
407
|
+
.checkout-sim-note{
|
|
408
|
+
font-size:.6875rem;color:#1a8c00;margin-top:10px;text-align:center;
|
|
409
|
+
font-style:italic;
|
|
410
|
+
}
|
|
411
|
+
.checkout-pay-btn{
|
|
412
|
+
width:100%;padding:14px;border:none;border-radius:8px;
|
|
413
|
+
background:#33ff00;color:#000;font:inherit;font-size:.9375rem;font-weight:700;
|
|
414
|
+
cursor:pointer;transition:background .15s;
|
|
415
|
+
font-family:'Geist Pixel',monospace;
|
|
416
|
+
}
|
|
417
|
+
.checkout-pay-btn:hover{background:#44ff22;}
|
|
418
|
+
.checkout-cancel{
|
|
419
|
+
text-align:center;margin-top:14px;
|
|
420
|
+
}
|
|
421
|
+
.checkout-cancel a{
|
|
422
|
+
color:#1a8c00;text-decoration:none;font-size:.8125rem;
|
|
423
|
+
transition:color .15s;
|
|
424
|
+
}
|
|
425
|
+
.checkout-cancel a:hover{color:#33ff00;}
|
|
426
|
+
@media(max-width:768px){
|
|
427
|
+
.checkout-layout{flex-direction:column;}
|
|
428
|
+
.checkout-summary{padding:32px 20px;border-right:none;border-bottom:1px solid #0a3300;}
|
|
429
|
+
.checkout-form-side{padding:32px 20px;}
|
|
430
|
+
}
|
|
306
431
|
`;
|
|
307
432
|
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://emulate.dev" target="_blank" rel="noopener">emulate</a></div>`;
|
|
308
433
|
function emuBar(service) {
|
|
@@ -322,6 +447,7 @@ function head(title) {
|
|
|
322
447
|
<head>
|
|
323
448
|
<meta charset="utf-8"/>
|
|
324
449
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
450
|
+
<link rel="icon" href="/_emulate/favicon.ico"/>
|
|
325
451
|
<title>${escapeHtml(title)} | emulate</title>
|
|
326
452
|
<style>${CSS}</style>
|
|
327
453
|
</head>`;
|
|
@@ -353,6 +479,25 @@ ${emuBar(service)}
|
|
|
353
479
|
${POWERED_BY}
|
|
354
480
|
</body></html>`;
|
|
355
481
|
}
|
|
482
|
+
function renderFormPostPage(action, fields, service) {
|
|
483
|
+
const hiddens = Object.entries(fields).filter(([, v]) => v != null).map(([k, v]) => `<input type="hidden" name="${escapeAttr(k)}" value="${escapeAttr(v)}"/>`).join("\n");
|
|
484
|
+
return `${head("Redirecting")}
|
|
485
|
+
<body onload="document.forms[0].submit()">
|
|
486
|
+
${emuBar(service)}
|
|
487
|
+
<div class="content">
|
|
488
|
+
<div class="content-inner" style="text-align:center">
|
|
489
|
+
<div class="card-subtitle">Redirecting…</div>
|
|
490
|
+
<form method="POST" action="${escapeAttr(action)}">
|
|
491
|
+
${hiddens}
|
|
492
|
+
<noscript><button type="submit" class="user-btn" style="margin-top:12px;justify-content:center">
|
|
493
|
+
<span class="user-login">Continue</span>
|
|
494
|
+
</button></noscript>
|
|
495
|
+
</form>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
${POWERED_BY}
|
|
499
|
+
</body></html>`;
|
|
500
|
+
}
|
|
356
501
|
function renderUserButton(opts) {
|
|
357
502
|
const hiddens = Object.entries(opts.hiddenFields).map(([k, v]) => `<input type="hidden" name="${escapeAttr(k)}" value="${escapeAttr(v)}"/>`).join("");
|
|
358
503
|
const nameLine = opts.name ? `<div class="user-meta">${escapeHtml(opts.name)}</div>` : "";
|
|
@@ -539,7 +684,10 @@ function getOktaStore(store) {
|
|
|
539
684
|
apps: store.collection("okta.apps", ["okta_id", "name"]),
|
|
540
685
|
oauthClients: store.collection("okta.oauth_clients", ["client_id", "auth_server_id"]),
|
|
541
686
|
authorizationServers: store.collection("okta.auth_servers", ["server_id"]),
|
|
542
|
-
groupMemberships: store.collection("okta.group_memberships", [
|
|
687
|
+
groupMemberships: store.collection("okta.group_memberships", [
|
|
688
|
+
"group_okta_id",
|
|
689
|
+
"user_okta_id"
|
|
690
|
+
]),
|
|
543
691
|
appAssignments: store.collection("okta.app_assignments", ["app_okta_id", "user_okta_id"])
|
|
544
692
|
};
|
|
545
693
|
}
|
|
@@ -553,9 +701,7 @@ function appRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
553
701
|
const q = (c.req.query("q") ?? "").toLowerCase();
|
|
554
702
|
let apps = oktaStore.apps.all();
|
|
555
703
|
if (q) {
|
|
556
|
-
apps = apps.filter(
|
|
557
|
-
(entry) => `${entry.name} ${entry.label}`.toLowerCase().includes(q)
|
|
558
|
-
);
|
|
704
|
+
apps = apps.filter((entry) => `${entry.name} ${entry.label}`.toLowerCase().includes(q));
|
|
559
705
|
}
|
|
560
706
|
const { page, per_page } = parsePagination(c);
|
|
561
707
|
const total = apps.length;
|
|
@@ -783,9 +929,7 @@ function groupRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
783
929
|
const q = (c.req.query("q") ?? "").toLowerCase();
|
|
784
930
|
let groups = oktaStore.groups.all();
|
|
785
931
|
if (q) {
|
|
786
|
-
groups = groups.filter(
|
|
787
|
-
(group) => `${group.name} ${group.description ?? ""}`.toLowerCase().includes(q)
|
|
788
|
-
);
|
|
932
|
+
groups = groups.filter((group) => `${group.name} ${group.description ?? ""}`.toLowerCase().includes(q));
|
|
789
933
|
}
|
|
790
934
|
const { page, per_page } = parsePagination(c);
|
|
791
935
|
const total = groups.length;
|
|
@@ -1091,10 +1235,10 @@ async function createIdToken(oktaStore, user, clientId, nonce, issuer, scope) {
|
|
|
1091
1235
|
return new SignJWT(claims).setProtectedHeader({ alg: "RS256", kid: KID, typ: "JWT" }).setIssuer(issuer).setAudience(clientId).setIssuedAt(now).setExpirationTime("1h").sign(privateKey);
|
|
1092
1236
|
}
|
|
1093
1237
|
function unauthorizedOAuthError() {
|
|
1094
|
-
return new Response(
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
);
|
|
1238
|
+
return new Response(JSON.stringify({ error: "invalid_token", error_description: "The access token is invalid." }), {
|
|
1239
|
+
status: 401,
|
|
1240
|
+
headers: { "Content-Type": "application/json" }
|
|
1241
|
+
});
|
|
1098
1242
|
}
|
|
1099
1243
|
function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
1100
1244
|
const oktaStore = getOktaStore(store);
|
|
@@ -1163,7 +1307,11 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1163
1307
|
}
|
|
1164
1308
|
if (!matchesRedirectUri(redirectUri, client.redirect_uris)) {
|
|
1165
1309
|
return c.html(
|
|
1166
|
-
renderErrorPage(
|
|
1310
|
+
renderErrorPage(
|
|
1311
|
+
"Redirect URI mismatch",
|
|
1312
|
+
"The redirect_uri is not registered for this application.",
|
|
1313
|
+
SERVICE_LABEL
|
|
1314
|
+
),
|
|
1167
1315
|
400
|
|
1168
1316
|
);
|
|
1169
1317
|
}
|
|
@@ -1171,25 +1319,27 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1171
1319
|
}
|
|
1172
1320
|
const users = oktaStore.users.all();
|
|
1173
1321
|
const callbackPath = `${buildOAuthBasePath(authServerId)}/authorize/callback`;
|
|
1174
|
-
const buttons = users.map(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1322
|
+
const buttons = users.map(
|
|
1323
|
+
(user) => renderUserButton({
|
|
1324
|
+
letter: (user.login[0] ?? "?").toUpperCase(),
|
|
1325
|
+
login: user.login,
|
|
1326
|
+
name: userDisplayName(user),
|
|
1327
|
+
email: user.email,
|
|
1328
|
+
formAction: callbackPath,
|
|
1329
|
+
hiddenFields: {
|
|
1330
|
+
user_ref: user.okta_id,
|
|
1331
|
+
redirect_uri: redirectUri,
|
|
1332
|
+
scope,
|
|
1333
|
+
state,
|
|
1334
|
+
nonce,
|
|
1335
|
+
client_id: clientId,
|
|
1336
|
+
response_mode: responseMode,
|
|
1337
|
+
code_challenge: codeChallenge,
|
|
1338
|
+
code_challenge_method: codeChallengeMethod,
|
|
1339
|
+
auth_server_id: authServerId
|
|
1340
|
+
}
|
|
1341
|
+
})
|
|
1342
|
+
).join("\n");
|
|
1193
1343
|
const subtitle = clientName ? `Sign in to <strong>${escapeHtml(clientName)}</strong> with your Okta account.` : "Choose a seeded user to continue.";
|
|
1194
1344
|
return c.html(
|
|
1195
1345
|
renderCardPage(
|
|
@@ -1223,10 +1373,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1223
1373
|
}
|
|
1224
1374
|
const user = findUserByRef(oktaStore, userRef);
|
|
1225
1375
|
if (!user) {
|
|
1226
|
-
return c.html(
|
|
1227
|
-
renderErrorPage("Unknown user", "The selected user is not available.", SERVICE_LABEL),
|
|
1228
|
-
400
|
|
1229
|
-
);
|
|
1376
|
+
return c.html(renderErrorPage("Unknown user", "The selected user is not available.", SERVICE_LABEL), 400);
|
|
1230
1377
|
}
|
|
1231
1378
|
const configuredClients = getClientsForServer(oktaStore.oauthClients.all(), authServerId);
|
|
1232
1379
|
if (configuredClients.length > 0) {
|
|
@@ -1239,7 +1386,11 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1239
1386
|
}
|
|
1240
1387
|
if (!matchesRedirectUri(redirectUri, client.redirect_uris)) {
|
|
1241
1388
|
return c.html(
|
|
1242
|
-
renderErrorPage(
|
|
1389
|
+
renderErrorPage(
|
|
1390
|
+
"Redirect URI mismatch",
|
|
1391
|
+
"The redirect_uri is not registered for this application.",
|
|
1392
|
+
SERVICE_LABEL
|
|
1393
|
+
),
|
|
1243
1394
|
400
|
|
1244
1395
|
);
|
|
1245
1396
|
}
|
|
@@ -1258,17 +1409,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1258
1409
|
});
|
|
1259
1410
|
debug("okta.oauth", `[callback] code=${code.slice(0, 8)}... user=${user.login} server=${authServerId}`);
|
|
1260
1411
|
if (responseMode === "form_post") {
|
|
1261
|
-
|
|
1262
|
-
<html>
|
|
1263
|
-
<head><title>Submit</title></head>
|
|
1264
|
-
<body onload="document.forms[0].submit()">
|
|
1265
|
-
<form method="POST" action="${escapeAttr(redirectUri)}">
|
|
1266
|
-
<input type="hidden" name="code" value="${escapeAttr(code)}" />
|
|
1267
|
-
<input type="hidden" name="state" value="${escapeAttr(state)}" />
|
|
1268
|
-
</form>
|
|
1269
|
-
</body>
|
|
1270
|
-
</html>`;
|
|
1271
|
-
return c.html(html);
|
|
1412
|
+
return c.html(renderFormPostPage(redirectUri, { code, state }, SERVICE_LABEL));
|
|
1272
1413
|
}
|
|
1273
1414
|
const url = new URL(redirectUri);
|
|
1274
1415
|
url.searchParams.set("code", code);
|
|
@@ -1276,7 +1417,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1276
1417
|
return c.redirect(url.toString(), 302);
|
|
1277
1418
|
};
|
|
1278
1419
|
app.post("/oauth2/v1/authorize/callback", (c) => handleAuthorizeCallback(c, ORG_AUTH_SERVER_ID));
|
|
1279
|
-
app.post(
|
|
1420
|
+
app.post(
|
|
1421
|
+
"/oauth2/:authServerId/v1/authorize/callback",
|
|
1422
|
+
(c) => handleAuthorizeCallback(c, c.req.param("authServerId"))
|
|
1423
|
+
);
|
|
1280
1424
|
const handleToken = async (c, authServerId) => {
|
|
1281
1425
|
const server = resolveServer(authServerId, baseUrl, oktaStore);
|
|
1282
1426
|
if (!server) return oktaError(c, 404, "E0000007", `Not found: authorization server '${authServerId}'`);
|
|
@@ -1306,7 +1450,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1306
1450
|
return c.json({ error: "invalid_grant", error_description: "redirect_uri does not match." }, 400);
|
|
1307
1451
|
}
|
|
1308
1452
|
if (validatedClient && validatedClient.client_id !== pending.clientId) {
|
|
1309
|
-
return c.json(
|
|
1453
|
+
return c.json(
|
|
1454
|
+
{ error: "invalid_grant", error_description: "Authorization code was not issued to this client." },
|
|
1455
|
+
400
|
|
1456
|
+
);
|
|
1310
1457
|
}
|
|
1311
1458
|
if (pending.codeChallenge !== null) {
|
|
1312
1459
|
if (!codeVerifier) {
|
|
@@ -1356,14 +1503,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1356
1503
|
id: user.id,
|
|
1357
1504
|
scopes: parseScope(scope)
|
|
1358
1505
|
});
|
|
1359
|
-
const idToken = await createIdToken(
|
|
1360
|
-
oktaStore,
|
|
1361
|
-
user,
|
|
1362
|
-
audienceClient,
|
|
1363
|
-
pending.nonce,
|
|
1364
|
-
server.issuer,
|
|
1365
|
-
scope
|
|
1366
|
-
);
|
|
1506
|
+
const idToken = await createIdToken(oktaStore, user, audienceClient, pending.nonce, server.issuer, scope);
|
|
1367
1507
|
return c.json({
|
|
1368
1508
|
token_type: "Bearer",
|
|
1369
1509
|
expires_in: 3600,
|
|
@@ -1382,7 +1522,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1382
1522
|
return c.json({ error: "invalid_grant", error_description: "Authorization server mismatch." }, 400);
|
|
1383
1523
|
}
|
|
1384
1524
|
if (validatedClient && validatedClient.client_id !== existing.clientId) {
|
|
1385
|
-
return c.json(
|
|
1525
|
+
return c.json(
|
|
1526
|
+
{ error: "invalid_grant", error_description: "Refresh token was not issued to this client." },
|
|
1527
|
+
400
|
|
1528
|
+
);
|
|
1386
1529
|
}
|
|
1387
1530
|
const user = oktaStore.users.findOneBy("okta_id", existing.userOktaId);
|
|
1388
1531
|
if (!user) return c.json({ error: "invalid_grant", error_description: "Unknown user." }, 400);
|
|
@@ -1552,9 +1695,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1552
1695
|
if (!postLogoutRedirectUri) return c.text("Logged out");
|
|
1553
1696
|
const scopedClients = getClientsForServer(oktaStore.oauthClients.all(), authServerId);
|
|
1554
1697
|
if (scopedClients.length > 0) {
|
|
1555
|
-
const isAllowed = scopedClients.some(
|
|
1556
|
-
(client) => matchesRedirectUri(postLogoutRedirectUri, client.redirect_uris)
|
|
1557
|
-
);
|
|
1698
|
+
const isAllowed = scopedClients.some((client) => matchesRedirectUri(postLogoutRedirectUri, client.redirect_uris));
|
|
1558
1699
|
if (!isAllowed) return c.text("Invalid post_logout_redirect_uri", 400);
|
|
1559
1700
|
}
|
|
1560
1701
|
return c.redirect(postLogoutRedirectUri, 302);
|
|
@@ -1678,14 +1819,16 @@ function userRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
1678
1819
|
if (!user) return oktaError(c, 404, "E0000007", "Not found: user");
|
|
1679
1820
|
const memberships = oktaStore.groupMemberships.findBy("user_okta_id", user.okta_id);
|
|
1680
1821
|
const groups = memberships.map((membership) => oktaStore.groups.findOneBy("okta_id", membership.group_okta_id)).filter((group) => Boolean(group));
|
|
1681
|
-
return c.json(
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1822
|
+
return c.json(
|
|
1823
|
+
groups.map((group) => ({
|
|
1824
|
+
id: group.okta_id,
|
|
1825
|
+
profile: {
|
|
1826
|
+
name: group.name,
|
|
1827
|
+
description: group.description
|
|
1828
|
+
},
|
|
1829
|
+
type: group.type
|
|
1830
|
+
}))
|
|
1831
|
+
);
|
|
1689
1832
|
});
|
|
1690
1833
|
app.post("/api/v1/users/:userId/lifecycle/activate", (c) => {
|
|
1691
1834
|
const auth = requireManagementAuth(c, tokenMap);
|
|
@@ -1833,10 +1976,7 @@ function seedDefaults(store, _baseUrl) {
|
|
|
1833
1976
|
client_id: "okta-test-app",
|
|
1834
1977
|
client_secret: "",
|
|
1835
1978
|
name: "Sample Public PKCE Client",
|
|
1836
|
-
redirect_uris: [
|
|
1837
|
-
"http://localhost:3000/official-sdk/callback",
|
|
1838
|
-
"http://localhost:3000/official-sdk"
|
|
1839
|
-
],
|
|
1979
|
+
redirect_uris: ["http://localhost:3000/official-sdk/callback", "http://localhost:3000/official-sdk"],
|
|
1840
1980
|
response_types: ["code"],
|
|
1841
1981
|
grant_types: ["authorization_code", "refresh_token"],
|
|
1842
1982
|
token_endpoint_auth_method: "none",
|