strapi-plugin-oidc 1.3.2 → 1.4.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.
@@ -164,7 +164,6 @@ const config = {
164
164
  OIDC_AUTHORIZATION_ENDPOINT: "",
165
165
  OIDC_TOKEN_ENDPOINT: "",
166
166
  OIDC_USER_INFO_ENDPOINT: "",
167
- OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER: false,
168
167
  OIDC_GRANT_TYPE: "authorization_code",
169
168
  OIDC_FAMILY_NAME_FIELD: "family_name",
170
169
  OIDC_GIVEN_NAME_FIELD: "given_name",
@@ -223,45 +222,43 @@ function clearAuthCookies(strapi2, ctx) {
223
222
  ctx.cookies.set("strapi_admin_refresh", "", options2);
224
223
  ctx.cookies.set("oidc_authenticated", "", { ...options2, path: "/" });
225
224
  }
225
+ const REQUIRED_CONFIG_KEYS = [
226
+ "OIDC_CLIENT_ID",
227
+ "OIDC_CLIENT_SECRET",
228
+ "OIDC_REDIRECT_URI",
229
+ "OIDC_SCOPES",
230
+ "OIDC_TOKEN_ENDPOINT",
231
+ "OIDC_USER_INFO_ENDPOINT",
232
+ "OIDC_GRANT_TYPE",
233
+ "OIDC_FAMILY_NAME_FIELD",
234
+ "OIDC_GIVEN_NAME_FIELD",
235
+ "OIDC_AUTHORIZATION_ENDPOINT"
236
+ ];
226
237
  function configValidation() {
227
238
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
228
- const requiredKeys = [
229
- "OIDC_CLIENT_ID",
230
- "OIDC_CLIENT_SECRET",
231
- "OIDC_REDIRECT_URI",
232
- "OIDC_SCOPES",
233
- "OIDC_TOKEN_ENDPOINT",
234
- "OIDC_USER_INFO_ENDPOINT",
235
- "OIDC_GRANT_TYPE",
236
- "OIDC_FAMILY_NAME_FIELD",
237
- "OIDC_GIVEN_NAME_FIELD",
238
- "OIDC_AUTHORIZATION_ENDPOINT"
239
- ];
240
- if (requiredKeys.every((key) => config2[key])) {
239
+ if (REQUIRED_CONFIG_KEYS.every((key) => config2[key])) {
241
240
  return config2;
242
241
  }
243
- throw new Error(`The following configuration keys are required: ${requiredKeys.join(", ")}`);
242
+ throw new Error(
243
+ `The following configuration keys are required: ${REQUIRED_CONFIG_KEYS.join(", ")}`
244
+ );
244
245
  }
245
246
  async function oidcSignIn(ctx) {
246
- let { state } = ctx.query;
247
247
  const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPES, OIDC_AUTHORIZATION_ENDPOINT } = configValidation();
248
248
  const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge__default.default();
249
- if (!state) {
250
- state = node_crypto.randomBytes(32).toString("base64url");
251
- }
249
+ const state = node_crypto.randomBytes(32).toString("base64url");
250
+ const nonce = node_crypto.randomBytes(32).toString("base64url");
252
251
  const isProduction = strapi.config.get("environment") === "production";
253
- ctx.cookies.set("oidc_code_verifier", codeVerifier, {
252
+ const cookieOptions = {
254
253
  httpOnly: true,
255
254
  maxAge: 6e5,
255
+ // 10 minutes
256
256
  secure: isProduction && ctx.request.secure,
257
257
  sameSite: "lax"
258
- });
259
- ctx.cookies.set("oidc_state", state, {
260
- httpOnly: true,
261
- maxAge: 6e5,
262
- secure: isProduction && ctx.request.secure,
263
- sameSite: "lax"
264
- });
258
+ };
259
+ ctx.cookies.set("oidc_code_verifier", codeVerifier, cookieOptions);
260
+ ctx.cookies.set("oidc_state", state, cookieOptions);
261
+ ctx.cookies.set("oidc_nonce", nonce, cookieOptions);
265
262
  const params = new URLSearchParams();
266
263
  params.append("response_type", "code");
267
264
  params.append("client_id", OIDC_CLIENT_ID);
@@ -270,11 +267,12 @@ async function oidcSignIn(ctx) {
270
267
  params.append("code_challenge", codeChallenge);
271
268
  params.append("code_challenge_method", "S256");
272
269
  params.append("state", state);
270
+ params.append("nonce", nonce);
273
271
  const authorizationUrl = `${OIDC_AUTHORIZATION_ENDPOINT}?${params.toString()}`;
274
272
  ctx.set("Location", authorizationUrl);
275
273
  return ctx.send({}, 302);
276
274
  }
277
- async function exchangeTokenAndFetchUserInfo(config2, params) {
275
+ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
278
276
  const response = await fetch(config2.OIDC_TOKEN_ENDPOINT, {
279
277
  method: "POST",
280
278
  body: params,
@@ -283,31 +281,28 @@ async function exchangeTokenAndFetchUserInfo(config2, params) {
283
281
  }
284
282
  });
285
283
  if (!response.ok) {
286
- const errText = await response.text();
287
- throw new Error(
288
- `Failed to exchange token: ${response.status} ${response.statusText} - ${errText}`
289
- );
284
+ throw new Error("Token exchange failed");
290
285
  }
291
286
  const tokenData = await response.json();
292
- let userInfoEndpointHeaders = {};
293
- let userInfoEndpointParameters = `?access_token=${tokenData.access_token}`;
294
- if (config2.OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER) {
295
- userInfoEndpointHeaders = {
296
- Authorization: `Bearer ${tokenData.access_token}`
297
- };
298
- userInfoEndpointParameters = "";
287
+ if (tokenData.id_token) {
288
+ try {
289
+ const payloadB64 = tokenData.id_token.split(".")[1];
290
+ const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
291
+ if (idTokenPayload.nonce !== expectedNonce) {
292
+ throw new Error("Nonce mismatch");
293
+ }
294
+ } catch (e) {
295
+ if (e.message === "Nonce mismatch") throw e;
296
+ throw new Error("Failed to parse ID token");
297
+ }
299
298
  }
300
- const userInfoEndpoint = `${config2.OIDC_USER_INFO_ENDPOINT}${userInfoEndpointParameters}`;
301
- const userResponse = await fetch(userInfoEndpoint, {
302
- headers: userInfoEndpointHeaders
299
+ const userResponse = await fetch(config2.OIDC_USER_INFO_ENDPOINT, {
300
+ headers: { Authorization: `Bearer ${tokenData.access_token}` }
303
301
  });
304
302
  if (!userResponse.ok) {
305
- const errText = await userResponse.text();
306
- throw new Error(
307
- `Failed to fetch user info: ${userResponse.status} ${userResponse.statusText} - ${errText}`
308
- );
303
+ throw new Error("Failed to fetch user info");
309
304
  }
310
- return await userResponse.json();
305
+ return userResponse.json();
311
306
  }
312
307
  async function registerNewUser(userService, oauthService2, roleService2, email, userResponseData, whitelistUser, config2, ctx) {
313
308
  let roles2 = [];
@@ -331,22 +326,16 @@ async function registerNewUser(userService, oauthService2, roleService2, email,
331
326
  async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
332
327
  const email = String(userResponseData.email).toLowerCase();
333
328
  const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
334
- const dbUser = await userService.findOneByEmail(email);
335
- let activateUser;
336
- if (dbUser) {
337
- activateUser = dbUser;
338
- } else {
339
- activateUser = await registerNewUser(
340
- userService,
341
- oauthService2,
342
- roleService2,
343
- email,
344
- userResponseData,
345
- whitelistUser,
346
- config2,
347
- ctx
348
- );
349
- }
329
+ const activateUser = await userService.findOneByEmail(email) ?? await registerNewUser(
330
+ userService,
331
+ oauthService2,
332
+ roleService2,
333
+ email,
334
+ userResponseData,
335
+ whitelistUser,
336
+ config2,
337
+ ctx
338
+ );
350
339
  const jwtToken = await oauthService2.generateToken(activateUser, ctx);
351
340
  oauthService2.triggerSignInSuccess(activateUser);
352
341
  return { activateUser, jwtToken };
@@ -362,8 +351,10 @@ async function oidcSignInCallback(ctx) {
362
351
  }
363
352
  const oidcState = ctx.cookies.get("oidc_state");
364
353
  const codeVerifier = ctx.cookies.get("oidc_code_verifier");
354
+ const oidcNonce = ctx.cookies.get("oidc_nonce");
365
355
  ctx.cookies.set("oidc_state", null);
366
356
  ctx.cookies.set("oidc_code_verifier", null);
357
+ ctx.cookies.set("oidc_nonce", null);
367
358
  if (!ctx.query.state || ctx.query.state !== oidcState) {
368
359
  return ctx.send(oauthService2.renderSignUpError("Invalid state"));
369
360
  }
@@ -375,7 +366,7 @@ async function oidcSignInCallback(ctx) {
375
366
  params.append("grant_type", config2.OIDC_GRANT_TYPE);
376
367
  params.append("code_verifier", codeVerifier);
377
368
  try {
378
- const userResponseData = await exchangeTokenAndFetchUserInfo(config2, params);
369
+ const userResponseData = await exchangeTokenAndFetchUserInfo(config2, params, oidcNonce);
379
370
  const { activateUser, jwtToken } = await handleUserAuthentication(
380
371
  userService,
381
372
  oauthService2,
@@ -391,7 +382,7 @@ async function oidcSignInCallback(ctx) {
391
382
  ctx.send(html);
392
383
  } catch (e) {
393
384
  console.error("ERROR CAUGHT IN OIDC SIGNIN:", e);
394
- ctx.send(oauthService2.renderSignUpError(e.message));
385
+ ctx.send(oauthService2.renderSignUpError("Authentication failed. Please try again."));
395
386
  }
396
387
  }
397
388
  async function logout(ctx) {
@@ -438,8 +429,11 @@ const role = {
438
429
  find,
439
430
  update
440
431
  };
432
+ function getWhitelistService() {
433
+ return strapi.plugin("strapi-plugin-oidc").service("whitelist");
434
+ }
441
435
  async function info(ctx) {
442
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
436
+ const whitelistService2 = getWhitelistService();
443
437
  const settings = await whitelistService2.getSettings();
444
438
  const whitelistUsers = await whitelistService2.getUsers();
445
439
  ctx.body = {
@@ -450,8 +444,9 @@ async function info(ctx) {
450
444
  };
451
445
  }
452
446
  async function updateSettings(ctx) {
453
- let { useWhitelist, enforceOIDC } = ctx.request.body;
454
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
447
+ const { useWhitelist } = ctx.request.body;
448
+ let { enforceOIDC } = ctx.request.body;
449
+ const whitelistService2 = getWhitelistService();
455
450
  if (useWhitelist && enforceOIDC) {
456
451
  const users = await whitelistService2.getUsers();
457
452
  if (users.length === 0) {
@@ -462,7 +457,7 @@ async function updateSettings(ctx) {
462
457
  ctx.body = { useWhitelist, enforceOIDC };
463
458
  }
464
459
  async function publicSettings(ctx) {
465
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
460
+ const whitelistService2 = getWhitelistService();
466
461
  const settings = await whitelistService2.getSettings();
467
462
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
468
463
  ctx.body = {
@@ -482,10 +477,11 @@ async function register(ctx) {
482
477
  where: { email: { $in: emailList } },
483
478
  populate: ["roles"]
484
479
  });
485
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
480
+ const existingUsersByEmail = new Map(existingUsers.map((u) => [u.email, u]));
481
+ const whitelistService2 = getWhitelistService();
486
482
  let matchedExistingUsersCount = 0;
487
483
  for (const singleEmail of emailList) {
488
- const existingUser = existingUsers.find((u) => u.email === singleEmail);
484
+ const existingUser = existingUsersByEmail.get(singleEmail);
489
485
  let finalRoles = roles2;
490
486
  if (existingUser?.roles) {
491
487
  finalRoles = existingUser.roles.map((r) => String(r.id));
@@ -502,14 +498,56 @@ async function register(ctx) {
502
498
  }
503
499
  async function removeEmail(ctx) {
504
500
  const { id } = ctx.params;
505
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
501
+ const whitelistService2 = getWhitelistService();
506
502
  await whitelistService2.removeUser(id);
507
503
  ctx.body = {};
508
504
  }
505
+ async function deleteAll(ctx) {
506
+ await strapi.query("plugin::strapi-plugin-oidc.whitelists").deleteMany({});
507
+ ctx.body = {};
508
+ }
509
+ async function importUsers(ctx) {
510
+ const { users } = ctx.request.body;
511
+ if (!Array.isArray(users)) {
512
+ ctx.status = 400;
513
+ ctx.body = { error: "Expected { users: [{email, roles}] }" };
514
+ return;
515
+ }
516
+ const allRoles = await strapi.query("admin::role").findMany({});
517
+ const roleNameToId = new Map(allRoles.map((r) => [r.name, String(r.id)]));
518
+ const resolveRole = (nameOrId) => roleNameToId.get(nameOrId) ?? nameOrId;
519
+ const normalized = users.filter((u) => u?.email).map((u) => ({
520
+ email: String(u.email).trim().toLowerCase(),
521
+ roles: (Array.isArray(u.roles) ? u.roles : []).map(resolveRole)
522
+ }));
523
+ const seen = /* @__PURE__ */ new Set();
524
+ const deduped = normalized.filter((u) => {
525
+ if (seen.has(u.email)) return false;
526
+ seen.add(u.email);
527
+ return true;
528
+ });
529
+ const strapiUsers = await strapi.query("admin::user").findMany({
530
+ where: { email: { $in: deduped.map((u) => u.email) } },
531
+ populate: ["roles"]
532
+ });
533
+ const strapiUserMap = new Map(strapiUsers.map((u) => [u.email, u]));
534
+ const whitelistService2 = getWhitelistService();
535
+ const existing = await whitelistService2.getUsers();
536
+ const existingEmails = new Set(existing.map((u) => u.email));
537
+ let importedCount = 0;
538
+ for (const user of deduped) {
539
+ if (existingEmails.has(user.email)) continue;
540
+ const strapiUser = strapiUserMap.get(user.email);
541
+ const finalRoles = strapiUser?.roles?.length ? strapiUser.roles.map((r) => String(r.id)) : user.roles;
542
+ await whitelistService2.registerUser(user.email, finalRoles);
543
+ importedCount++;
544
+ }
545
+ ctx.body = { importedCount };
546
+ }
509
547
  async function syncUsers(ctx) {
510
- let { users } = ctx.request.body;
511
- users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
512
- const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
548
+ const { users: rawUsers } = ctx.request.body;
549
+ const users = rawUsers.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
550
+ const whitelistService2 = getWhitelistService();
513
551
  const currentUsers = await whitelistService2.getUsers();
514
552
  let matchedExistingUsersCount = 0;
515
553
  const emailsToSync = users.map((u) => u.email);
@@ -517,26 +555,29 @@ async function syncUsers(ctx) {
517
555
  where: { email: { $in: emailsToSync } },
518
556
  populate: ["roles"]
519
557
  });
558
+ const syncEmailSet = new Set(emailsToSync);
559
+ const currentUsersByEmail = new Map(currentUsers.map((u) => [u.email, u]));
560
+ const strapiUsersByEmail = new Map(existingStrapiUsers.map((u) => [u.email, u]));
520
561
  for (const currUser of currentUsers) {
521
- if (!users.find((u) => u.email === currUser.email)) {
562
+ if (!syncEmailSet.has(currUser.email)) {
522
563
  await whitelistService2.removeUser(currUser.id);
523
564
  }
524
565
  }
525
566
  for (const user of users) {
526
- const existingStrapiUser = existingStrapiUsers.find((u) => u.email === user.email);
567
+ const currUser = currentUsersByEmail.get(user.email);
527
568
  let finalRoles = user.roles;
528
- const currUser = currentUsers.find((u) => u.email === user.email);
529
- if (!currUser && existingStrapiUser?.roles) {
530
- finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
531
- matchedExistingUsersCount++;
532
- }
533
- if (currUser) {
569
+ if (!currUser) {
570
+ const existingStrapiUser = strapiUsersByEmail.get(user.email);
571
+ if (existingStrapiUser?.roles) {
572
+ finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
573
+ matchedExistingUsersCount++;
574
+ }
575
+ await whitelistService2.registerUser(user.email, finalRoles);
576
+ } else {
534
577
  await strapi.query("plugin::strapi-plugin-oidc.whitelists").update({
535
578
  where: { id: currUser.id },
536
579
  data: { roles: finalRoles }
537
580
  });
538
- } else {
539
- await whitelistService2.registerUser(user.email, finalRoles);
540
581
  }
541
582
  }
542
583
  ctx.body = { matchedExistingUsersCount };
@@ -547,7 +588,9 @@ const whitelist = {
547
588
  publicSettings,
548
589
  register,
549
590
  removeEmail,
550
- syncUsers
591
+ deleteAll,
592
+ syncUsers,
593
+ importUsers
551
594
  };
552
595
  const controllers = {
553
596
  oidc,
@@ -571,134 +614,133 @@ const rateLimitMiddleware = async (ctx, next) => {
571
614
  rateLimitMap.set(ip, requestStamps);
572
615
  await next();
573
616
  };
574
- const routes = [
575
- {
576
- method: "GET",
577
- path: "/oidc-roles",
578
- handler: "role.find",
579
- config: {
580
- policies: [
581
- "admin::isAuthenticatedAdmin",
582
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
583
- ]
584
- }
585
- },
586
- {
587
- method: "PUT",
588
- path: "/oidc-roles",
589
- handler: "role.update",
590
- config: {
591
- policies: [
592
- "admin::isAuthenticatedAdmin",
593
- {
594
- name: "admin::hasPermissions",
595
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
596
- }
597
- ]
598
- }
599
- },
600
- {
601
- method: "GET",
602
- path: "/oidc",
603
- handler: "oidc.oidcSignIn",
604
- config: {
605
- auth: false,
606
- middlewares: [rateLimitMiddleware]
607
- }
608
- },
609
- {
610
- method: "GET",
611
- path: "/oidc/callback",
612
- handler: "oidc.oidcSignInCallback",
613
- config: {
614
- auth: false,
615
- middlewares: [rateLimitMiddleware]
616
- }
617
- },
618
- {
619
- method: "GET",
620
- path: "/logout",
621
- handler: "oidc.logout",
622
- config: {
623
- auth: false
624
- }
625
- },
626
- {
627
- method: "GET",
628
- path: "/whitelist",
629
- handler: "whitelist.info",
630
- config: {
631
- policies: [
632
- "admin::isAuthenticatedAdmin",
633
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
634
- ]
635
- }
636
- },
637
- {
638
- method: "PUT",
639
- path: "/whitelist/settings",
640
- handler: "whitelist.updateSettings",
641
- config: {
642
- policies: [
643
- "admin::isAuthenticatedAdmin",
644
- {
645
- name: "admin::hasPermissions",
646
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
647
- }
648
- ]
649
- }
650
- },
651
- {
652
- method: "GET",
653
- path: "/settings/public",
654
- handler: "whitelist.publicSettings",
655
- config: {
656
- auth: false
657
- }
658
- },
659
- {
660
- method: "PUT",
661
- path: "/whitelist/sync",
662
- handler: "whitelist.syncUsers",
663
- config: {
664
- policies: [
665
- "admin::isAuthenticatedAdmin",
666
- {
667
- name: "admin::hasPermissions",
668
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
669
- }
670
- ]
671
- }
672
- },
673
- {
674
- method: "POST",
675
- path: "/whitelist",
676
- handler: "whitelist.register",
677
- config: {
678
- policies: [
679
- "admin::isAuthenticatedAdmin",
680
- {
681
- name: "admin::hasPermissions",
682
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
683
- }
684
- ]
617
+ const adminPolicies = (action) => ({
618
+ policies: [
619
+ "admin::isAuthenticatedAdmin",
620
+ {
621
+ name: "admin::hasPermissions",
622
+ config: { actions: [`plugin::strapi-plugin-oidc.${action}`] }
685
623
  }
624
+ ]
625
+ });
626
+ const routes = {
627
+ admin: {
628
+ type: "admin",
629
+ routes: [
630
+ {
631
+ method: "GET",
632
+ path: "/oidc-roles",
633
+ handler: "role.find",
634
+ config: adminPolicies("read")
635
+ },
636
+ {
637
+ method: "PUT",
638
+ path: "/oidc-roles",
639
+ handler: "role.update",
640
+ config: adminPolicies("update")
641
+ },
642
+ {
643
+ method: "GET",
644
+ path: "/oidc",
645
+ handler: "oidc.oidcSignIn",
646
+ config: { auth: false, middlewares: [rateLimitMiddleware] }
647
+ },
648
+ {
649
+ method: "GET",
650
+ path: "/oidc/callback",
651
+ handler: "oidc.oidcSignInCallback",
652
+ config: { auth: false, middlewares: [rateLimitMiddleware] }
653
+ },
654
+ {
655
+ method: "GET",
656
+ path: "/logout",
657
+ handler: "oidc.logout",
658
+ config: { auth: false }
659
+ },
660
+ {
661
+ method: "GET",
662
+ path: "/whitelist",
663
+ handler: "whitelist.info",
664
+ config: adminPolicies("read")
665
+ },
666
+ {
667
+ method: "PUT",
668
+ path: "/whitelist/settings",
669
+ handler: "whitelist.updateSettings",
670
+ config: adminPolicies("update")
671
+ },
672
+ {
673
+ method: "GET",
674
+ path: "/settings/public",
675
+ handler: "whitelist.publicSettings",
676
+ config: { auth: false }
677
+ },
678
+ {
679
+ method: "PUT",
680
+ path: "/whitelist/sync",
681
+ handler: "whitelist.syncUsers",
682
+ config: adminPolicies("update")
683
+ },
684
+ {
685
+ method: "POST",
686
+ path: "/whitelist/import",
687
+ handler: "whitelist.importUsers",
688
+ config: adminPolicies("update")
689
+ },
690
+ {
691
+ method: "POST",
692
+ path: "/whitelist",
693
+ handler: "whitelist.register",
694
+ config: adminPolicies("update")
695
+ },
696
+ {
697
+ method: "DELETE",
698
+ path: "/whitelist/:id",
699
+ handler: "whitelist.removeEmail",
700
+ config: adminPolicies("update")
701
+ },
702
+ {
703
+ method: "DELETE",
704
+ path: "/whitelist",
705
+ handler: "whitelist.deleteAll",
706
+ config: adminPolicies("update")
707
+ }
708
+ ]
686
709
  },
687
- {
688
- method: "DELETE",
689
- path: "/whitelist/:id",
690
- handler: "whitelist.removeEmail",
691
- config: {
692
- policies: [
693
- "admin::isAuthenticatedAdmin",
694
- {
695
- name: "admin::hasPermissions",
696
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
697
- }
698
- ]
699
- }
710
+ // API-token-authenticated routes for programmatic whitelist management.
711
+ // Accessible at /strapi-plugin-oidc/... using a Strapi API token
712
+ // (full-access or custom) in the Authorization: Bearer <token> header.
713
+ "content-api": {
714
+ type: "content-api",
715
+ routes: [
716
+ {
717
+ method: "GET",
718
+ path: "/whitelist",
719
+ handler: "whitelist.info"
720
+ },
721
+ {
722
+ method: "POST",
723
+ path: "/whitelist",
724
+ handler: "whitelist.register"
725
+ },
726
+ {
727
+ method: "POST",
728
+ path: "/whitelist/import",
729
+ handler: "whitelist.importUsers"
730
+ },
731
+ {
732
+ method: "DELETE",
733
+ path: "/whitelist/:id",
734
+ handler: "whitelist.removeEmail"
735
+ },
736
+ {
737
+ method: "DELETE",
738
+ path: "/whitelist",
739
+ handler: "whitelist.deleteAll"
740
+ }
741
+ ]
700
742
  }
701
- ];
743
+ };
702
744
  const policies = {};
703
745
  function renderHtmlTemplate(title, content) {
704
746
  return `
@@ -834,7 +876,7 @@ function oauthService({ strapi: strapi2 }) {
834
876
  roles: roles2,
835
877
  preferedLanguage: locale
836
878
  });
837
- return await userService.register({
879
+ return userService.register({
838
880
  registrationToken: createdUser.registrationToken,
839
881
  userInfo: {
840
882
  firstname: firstname || "unset",
@@ -855,7 +897,7 @@ function oauthService({ strapi: strapi2 }) {
855
897
  if (!baseAlias) {
856
898
  return baseEmail;
857
899
  }
858
- const alias = baseAlias.replace("/+/g", "");
900
+ const alias = baseAlias.replace(/\+/g, "");
859
901
  const beforePosition = baseEmail.indexOf("@");
860
902
  const origin = baseEmail.substring(0, beforePosition);
861
903
  const domain = baseEmail.substring(beforePosition);
@@ -892,11 +934,9 @@ function oauthService({ strapi: strapi2 }) {
892
934
  provider: "strapi-plugin-oidc"
893
935
  });
894
936
  },
895
- // Sign In Success
896
937
  renderSignUpSuccess(jwtToken, user, nonce) {
897
938
  const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
898
- const REMEMBER_ME = config2["REMEMBER_ME"];
899
- const isRememberMe = !!REMEMBER_ME;
939
+ const isRememberMe = !!config2["REMEMBER_ME"];
900
940
  const content = `
901
941
  <noscript>
902
942
  <div class="card">
@@ -922,7 +962,6 @@ function oauthService({ strapi: strapi2 }) {
922
962
  <\/script>`;
923
963
  return renderHtmlTemplate("Authenticating...", content);
924
964
  },
925
- // Sign In Error
926
965
  renderSignUpError(message) {
927
966
  const safeMessage = String(message).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
928
967
  const content = `
@@ -950,8 +989,7 @@ function oauthService({ strapi: strapi2 }) {
950
989
  const userId = String(user.id);
951
990
  const deviceId = node_crypto.randomUUID();
952
991
  const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
953
- const REMEMBER_ME = config2["REMEMBER_ME"];
954
- const rememberMe = !!REMEMBER_ME;
992
+ const rememberMe = !!config2["REMEMBER_ME"];
955
993
  const { token: refreshToken, absoluteExpiresAt } = await sessionManager(
956
994
  "admin"
957
995
  ).generateRefreshToken(userId, deviceId, {