strapi-plugin-oidc 1.0.4 → 1.0.6

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.
@@ -118,10 +118,22 @@ const contentTypes = {
118
118
  };
119
119
  function configValidation() {
120
120
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
121
- if (config2["OIDC_CLIENT_ID"] && config2["OIDC_CLIENT_SECRET"] && config2["OIDC_REDIRECT_URI"] && config2["OIDC_SCOPES"] && config2["OIDC_TOKEN_ENDPOINT"] && config2["OIDC_USER_INFO_ENDPOINT"] && config2["OIDC_GRANT_TYPE"] && config2["OIDC_FAMILY_NAME_FIELD"] && config2["OIDC_GIVEN_NAME_FIELD"] && config2["OIDC_AUTHORIZATION_ENDPOINT"]) {
121
+ const requiredKeys = [
122
+ "OIDC_CLIENT_ID",
123
+ "OIDC_CLIENT_SECRET",
124
+ "OIDC_REDIRECT_URI",
125
+ "OIDC_SCOPES",
126
+ "OIDC_TOKEN_ENDPOINT",
127
+ "OIDC_USER_INFO_ENDPOINT",
128
+ "OIDC_GRANT_TYPE",
129
+ "OIDC_FAMILY_NAME_FIELD",
130
+ "OIDC_GIVEN_NAME_FIELD",
131
+ "OIDC_AUTHORIZATION_ENDPOINT"
132
+ ];
133
+ if (requiredKeys.every((key) => config2[key])) {
122
134
  return config2;
123
135
  }
124
- throw new Error("OIDC_AUTHORIZATION_ENDPOINT,OIDC_TOKEN_ENDPOINT, OIDC_USER_INFO_ENDPOINT,OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI, and OIDC_SCOPES are required");
136
+ throw new Error(`The following configuration keys are required: ${requiredKeys.join(", ")}`);
125
137
  }
126
138
  async function oidcSignIn(ctx) {
127
139
  let { state } = ctx.query;
@@ -144,6 +156,66 @@ async function oidcSignIn(ctx) {
144
156
  ctx.set("Location", authorizationUrl);
145
157
  return ctx.send({}, 302);
146
158
  }
159
+ async function exchangeTokenAndFetchUserInfo(httpClient, config2, params) {
160
+ const response = await httpClient.post(config2.OIDC_TOKEN_ENDPOINT, params, {
161
+ headers: {
162
+ "Content-Type": "application/x-www-form-urlencoded"
163
+ }
164
+ });
165
+ let userInfoEndpointHeaders = {};
166
+ let userInfoEndpointParameters = `?access_token=${response.data.access_token}`;
167
+ if (config2.OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER) {
168
+ userInfoEndpointHeaders = {
169
+ headers: { Authorization: `Bearer ${response.data.access_token}` }
170
+ };
171
+ userInfoEndpointParameters = "";
172
+ }
173
+ const userInfoEndpoint = `${config2.OIDC_USER_INFO_ENDPOINT}${userInfoEndpointParameters}`;
174
+ const userResponse = await httpClient.get(userInfoEndpoint, userInfoEndpointHeaders);
175
+ return userResponse.data;
176
+ }
177
+ async function registerNewUser(userService, oauthService2, roleService2, email, userResponseData, whitelistUser, config2, ctx) {
178
+ let roles2 = [];
179
+ if (whitelistUser?.roles?.length > 0) {
180
+ roles2 = whitelistUser.roles;
181
+ } else {
182
+ const oidcRoles = await roleService2.oidcRoles();
183
+ roles2 = oidcRoles?.roles || [];
184
+ }
185
+ const defaultLocale = oauthService2.localeFindByHeader(ctx.request.headers);
186
+ const activateUser = await oauthService2.createUser(
187
+ email,
188
+ userResponseData[config2.OIDC_FAMILY_NAME_FIELD],
189
+ userResponseData[config2.OIDC_GIVEN_NAME_FIELD],
190
+ defaultLocale,
191
+ roles2
192
+ );
193
+ await oauthService2.triggerWebHook(activateUser);
194
+ return activateUser;
195
+ }
196
+ async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
197
+ const email = String(userResponseData.email).toLowerCase();
198
+ const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
199
+ const dbUser = await userService.findOneByEmail(email);
200
+ let activateUser;
201
+ if (dbUser) {
202
+ activateUser = dbUser;
203
+ } else {
204
+ activateUser = await registerNewUser(
205
+ userService,
206
+ oauthService2,
207
+ roleService2,
208
+ email,
209
+ userResponseData,
210
+ whitelistUser,
211
+ config2,
212
+ ctx
213
+ );
214
+ }
215
+ const jwtToken = await oauthService2.generateToken(activateUser, ctx);
216
+ oauthService2.triggerSignInSuccess(activateUser);
217
+ return { activateUser, jwtToken };
218
+ }
147
219
  async function oidcSignInCallback(ctx) {
148
220
  const config2 = configValidation();
149
221
  const httpClient = axios.create();
@@ -152,77 +224,41 @@ async function oidcSignInCallback(ctx) {
152
224
  const roleService2 = strapi.plugin("strapi-plugin-oidc").service("role");
153
225
  const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
154
226
  if (!ctx.query.code) {
155
- return ctx.send(oauthService2.renderSignUpError(`code Not Found`));
227
+ return ctx.send(oauthService2.renderSignUpError("code Not Found"));
156
228
  }
157
229
  if (!ctx.query.state || ctx.query.state !== ctx.session.oidcState) {
158
- return ctx.send(oauthService2.renderSignUpError(`Invalid state`));
230
+ return ctx.send(oauthService2.renderSignUpError("Invalid state"));
159
231
  }
160
232
  const params = new URLSearchParams();
161
233
  params.append("code", ctx.query.code);
162
- params.append("client_id", config2["OIDC_CLIENT_ID"]);
163
- params.append("client_secret", config2["OIDC_CLIENT_SECRET"]);
164
- params.append("redirect_uri", config2["OIDC_REDIRECT_URI"]);
165
- params.append("grant_type", config2["OIDC_GRANT_TYPE"]);
234
+ params.append("client_id", config2.OIDC_CLIENT_ID);
235
+ params.append("client_secret", config2.OIDC_CLIENT_SECRET);
236
+ params.append("redirect_uri", config2.OIDC_REDIRECT_URI);
237
+ params.append("grant_type", config2.OIDC_GRANT_TYPE);
166
238
  params.append("code_verifier", ctx.session.codeVerifier);
167
239
  try {
168
- const response = await httpClient.post(config2["OIDC_TOKEN_ENDPOINT"], params, {
169
- headers: {
170
- "Content-Type": "application/x-www-form-urlencoded"
171
- }
172
- });
173
- let userInfoEndpointHeaders = {};
174
- let userInfoEndpointParameters = `?access_token=${response.data.access_token}`;
175
- if (config2["OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER"]) {
176
- userInfoEndpointHeaders = {
177
- headers: { Authorization: `Bearer ${response.data.access_token}` }
178
- };
179
- userInfoEndpointParameters = "";
180
- }
181
- const userInfoEndpoint = `${config2["OIDC_USER_INFO_ENDPOINT"]}${userInfoEndpointParameters}`;
182
- const userResponse = await httpClient.get(
183
- userInfoEndpoint,
184
- userInfoEndpointHeaders
240
+ const userResponseData = await exchangeTokenAndFetchUserInfo(httpClient, config2, params);
241
+ const { activateUser, jwtToken } = await handleUserAuthentication(
242
+ userService,
243
+ oauthService2,
244
+ roleService2,
245
+ whitelistService2,
246
+ userResponseData,
247
+ config2,
248
+ ctx
185
249
  );
186
- const email = userResponse.data.email;
187
- const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
188
- const dbUser = await userService.findOneByEmail(email);
189
- let activateUser;
190
- let jwtToken;
191
- if (dbUser) {
192
- activateUser = dbUser;
193
- jwtToken = await oauthService2.generateToken(dbUser, ctx);
194
- } else {
195
- let roles2 = [];
196
- if (whitelistUser && whitelistUser.roles && whitelistUser.roles.length > 0) {
197
- roles2 = whitelistUser.roles;
198
- } else {
199
- const oidcRoles = await roleService2.oidcRoles();
200
- roles2 = oidcRoles && oidcRoles["roles"] ? oidcRoles["roles"] : [];
201
- }
202
- const defaultLocale = oauthService2.localeFindByHeader(ctx.request.headers);
203
- activateUser = await oauthService2.createUser(
204
- email,
205
- userResponse.data[config2["OIDC_FAMILY_NAME_FIELD"]],
206
- userResponse.data[config2["OIDC_GIVEN_NAME_FIELD"]],
207
- defaultLocale,
208
- roles2
209
- );
210
- jwtToken = await oauthService2.generateToken(activateUser, ctx);
211
- await oauthService2.triggerWebHook(activateUser);
212
- }
213
- oauthService2.triggerSignInSuccess(activateUser);
214
250
  const nonce = randomUUID();
215
251
  const html = oauthService2.renderSignUpSuccess(jwtToken, activateUser, nonce);
216
252
  ctx.set("Content-Security-Policy", `script-src 'nonce-${nonce}'`);
217
253
  ctx.send(html);
218
254
  } catch (e) {
219
- console.error(e);
255
+ console.error("ERROR CAUGHT IN OIDC SIGNIN:", e);
220
256
  ctx.send(oauthService2.renderSignUpError(e.message));
221
257
  }
222
258
  }
223
259
  async function logout(ctx) {
224
260
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
225
- const logoutUrl = config2["OIDC_LOGOUT_URL"];
261
+ const logoutUrl = config2.OIDC_LOGOUT_URL;
226
262
  if (logoutUrl) {
227
263
  ctx.redirect(logoutUrl);
228
264
  } else {
@@ -240,10 +276,9 @@ async function find(ctx) {
240
276
  const roles2 = await roleService2.find();
241
277
  const oidcConstants = roleService2.getOidcRoles();
242
278
  for (const oidc2 of oidcConstants) {
243
- for (const role2 of roles2) {
244
- if (role2["oauth_type"] === oidc2["oauth_type"]) {
245
- oidc2["role"] = role2["roles"];
246
- }
279
+ const matchedRole = roles2.find((r) => r.oauth_type === oidc2.oauth_type);
280
+ if (matchedRole) {
281
+ oidc2.role = matchedRole.roles;
247
282
  }
248
283
  }
249
284
  ctx.send(oidcConstants);
@@ -255,7 +290,7 @@ async function update(ctx) {
255
290
  await roleService2.update(roles2);
256
291
  ctx.send({}, 204);
257
292
  } catch (e) {
258
- console.log(e);
293
+ console.error(e);
259
294
  ctx.send({}, 400);
260
295
  }
261
296
  }
@@ -274,8 +309,14 @@ async function info(ctx) {
274
309
  };
275
310
  }
276
311
  async function updateSettings(ctx) {
277
- const { useWhitelist, enforceOIDC } = ctx.request.body;
312
+ let { useWhitelist, enforceOIDC } = ctx.request.body;
278
313
  const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
314
+ if (useWhitelist && enforceOIDC) {
315
+ const users = await whitelistService2.getUsers();
316
+ if (users.length === 0) {
317
+ enforceOIDC = false;
318
+ }
319
+ }
279
320
  await whitelistService2.setSettings({ useWhitelist, enforceOIDC });
280
321
  ctx.body = { useWhitelist, enforceOIDC };
281
322
  }
@@ -289,12 +330,11 @@ async function publicSettings(ctx) {
289
330
  async function register(ctx) {
290
331
  const { email, roles: roles2 } = ctx.request.body;
291
332
  if (!email) {
292
- ctx.body = {
293
- message: "Please enter a valid email address"
294
- };
333
+ ctx.body = { message: "Please enter a valid email address" };
295
334
  return;
296
335
  }
297
- const emailList = Array.isArray(email) ? email : email.split(",").map((e) => e.trim()).filter((e) => e);
336
+ const rawEmails = Array.isArray(email) ? email : email.split(",");
337
+ const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
298
338
  const existingUsers = await strapi.query("admin::user").findMany({
299
339
  where: { email: { $in: emailList } },
300
340
  populate: ["roles"]
@@ -304,8 +344,8 @@ async function register(ctx) {
304
344
  for (const singleEmail of emailList) {
305
345
  const existingUser = existingUsers.find((u) => u.email === singleEmail);
306
346
  let finalRoles = roles2;
307
- if (existingUser && existingUser.roles) {
308
- finalRoles = existingUser.roles.map((r) => r.id.toString());
347
+ if (existingUser?.roles) {
348
+ finalRoles = existingUser.roles.map((r) => String(r.id));
309
349
  matchedExistingUsersCount++;
310
350
  }
311
351
  const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({
@@ -324,7 +364,8 @@ async function removeEmail(ctx) {
324
364
  ctx.body = {};
325
365
  }
326
366
  async function syncUsers(ctx) {
327
- const { users } = ctx.request.body;
367
+ let { users } = ctx.request.body;
368
+ users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
328
369
  const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
329
370
  const currentUsers = await whitelistService2.getUsers();
330
371
  let matchedExistingUsersCount = 0;
@@ -342,8 +383,8 @@ async function syncUsers(ctx) {
342
383
  const existingStrapiUser = existingStrapiUsers.find((u) => u.email === user.email);
343
384
  let finalRoles = user.roles;
344
385
  const currUser = currentUsers.find((u) => u.email === user.email);
345
- if (!currUser && existingStrapiUser && existingStrapiUser.roles) {
346
- finalRoles = existingStrapiUser.roles.map((r) => r.id.toString());
386
+ if (!currUser && existingStrapiUser?.roles) {
387
+ finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
347
388
  matchedExistingUsersCount++;
348
389
  }
349
390
  if (currUser) {
@@ -370,6 +411,23 @@ const controllers = {
370
411
  role,
371
412
  whitelist
372
413
  };
414
+ const rateLimitMap = /* @__PURE__ */ new Map();
415
+ const RATE_LIMIT_WINDOW = 6e4;
416
+ const MAX_REQUESTS = 20;
417
+ const rateLimitMiddleware = async (ctx, next) => {
418
+ const ip = ctx.request.ip;
419
+ const now = Date.now();
420
+ const windowStart = now - RATE_LIMIT_WINDOW;
421
+ const requestStamps = (rateLimitMap.get(ip) || []).filter((timestamp) => timestamp > windowStart);
422
+ if (requestStamps.length >= MAX_REQUESTS) {
423
+ ctx.status = 429;
424
+ ctx.body = "Too Many Requests";
425
+ return;
426
+ }
427
+ requestStamps.push(now);
428
+ rateLimitMap.set(ip, requestStamps);
429
+ await next();
430
+ };
373
431
  const routes = [
374
432
  {
375
433
  method: "GET",
@@ -389,7 +447,10 @@ const routes = [
389
447
  config: {
390
448
  policies: [
391
449
  "admin::isAuthenticatedAdmin",
392
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.update"] } }
450
+ {
451
+ name: "admin::hasPermissions",
452
+ config: { actions: ["plugin::strapi-plugin-oidc.update"] }
453
+ }
393
454
  ]
394
455
  }
395
456
  },
@@ -398,7 +459,8 @@ const routes = [
398
459
  path: "/oidc",
399
460
  handler: "oidc.oidcSignIn",
400
461
  config: {
401
- auth: false
462
+ auth: false,
463
+ middlewares: [rateLimitMiddleware]
402
464
  }
403
465
  },
404
466
  {
@@ -406,7 +468,8 @@ const routes = [
406
468
  path: "/oidc/callback",
407
469
  handler: "oidc.oidcSignInCallback",
408
470
  config: {
409
- auth: false
471
+ auth: false,
472
+ middlewares: [rateLimitMiddleware]
410
473
  }
411
474
  },
412
475
  {
@@ -435,7 +498,10 @@ const routes = [
435
498
  config: {
436
499
  policies: [
437
500
  "admin::isAuthenticatedAdmin",
438
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.update"] } }
501
+ {
502
+ name: "admin::hasPermissions",
503
+ config: { actions: ["plugin::strapi-plugin-oidc.update"] }
504
+ }
439
505
  ]
440
506
  }
441
507
  },
@@ -454,7 +520,10 @@ const routes = [
454
520
  config: {
455
521
  policies: [
456
522
  "admin::isAuthenticatedAdmin",
457
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.update"] } }
523
+ {
524
+ name: "admin::hasPermissions",
525
+ config: { actions: ["plugin::strapi-plugin-oidc.update"] }
526
+ }
458
527
  ]
459
528
  }
460
529
  },
@@ -465,7 +534,10 @@ const routes = [
465
534
  config: {
466
535
  policies: [
467
536
  "admin::isAuthenticatedAdmin",
468
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.update"] } }
537
+ {
538
+ name: "admin::hasPermissions",
539
+ config: { actions: ["plugin::strapi-plugin-oidc.update"] }
540
+ }
469
541
  ]
470
542
  }
471
543
  },
@@ -476,12 +548,132 @@ const routes = [
476
548
  config: {
477
549
  policies: [
478
550
  "admin::isAuthenticatedAdmin",
479
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.update"] } }
551
+ {
552
+ name: "admin::hasPermissions",
553
+ config: { actions: ["plugin::strapi-plugin-oidc.update"] }
554
+ }
480
555
  ]
481
556
  }
482
557
  }
483
558
  ];
484
559
  const policies = {};
560
+ function renderHtmlTemplate(title, content) {
561
+ return `
562
+ <!doctype html>
563
+ <html lang="en">
564
+ <head>
565
+ <meta charset="utf-8">
566
+ <meta name="viewport" content="width=device-width, initial-scale=1">
567
+ <title>${title}</title>
568
+ <style>
569
+ :root {
570
+ --bg-color: #f6f6f9;
571
+ --card-bg: #ffffff;
572
+ --text-color: #32324d;
573
+ --text-muted: #666687;
574
+ --btn-bg: #4945ff;
575
+ --btn-hover: #271fe0;
576
+ --btn-text: #ffffff;
577
+ --icon-bg: #fcecea;
578
+ --icon-color: #d02b20;
579
+ --success-bg: #eafbe7;
580
+ --success-color: #328048;
581
+ --shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
582
+ }
583
+ @media (prefers-color-scheme: dark) {
584
+ :root {
585
+ --bg-color: #181826;
586
+ --card-bg: #212134;
587
+ --text-color: #ffffff;
588
+ --text-muted: #a5a5ba;
589
+ --btn-bg: #4945ff;
590
+ --btn-hover: #7b79ff;
591
+ --btn-text: #ffffff;
592
+ --icon-bg: #4a2123;
593
+ --icon-color: #f23628;
594
+ --success-bg: #1c3523;
595
+ --success-color: #55ca76;
596
+ --shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
597
+ }
598
+ }
599
+ body {
600
+ margin: 0;
601
+ padding: 0;
602
+ display: flex;
603
+ justify-content: center;
604
+ align-items: center;
605
+ height: 100vh;
606
+ background-color: var(--bg-color);
607
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
608
+ color: var(--text-color);
609
+ }
610
+ .card {
611
+ background: var(--card-bg);
612
+ padding: 32px 40px;
613
+ border-radius: 8px;
614
+ box-shadow: var(--shadow);
615
+ max-width: 400px;
616
+ width: 100%;
617
+ text-align: center;
618
+ box-sizing: border-box;
619
+ }
620
+ .icon {
621
+ width: 48px;
622
+ height: 48px;
623
+ background-color: var(--icon-bg);
624
+ color: var(--icon-color);
625
+ border-radius: 50%;
626
+ display: inline-flex;
627
+ justify-content: center;
628
+ align-items: center;
629
+ margin-bottom: 24px;
630
+ }
631
+ .icon.success {
632
+ background-color: var(--success-bg);
633
+ color: var(--success-color);
634
+ }
635
+ .icon svg {
636
+ width: 24px;
637
+ height: 24px;
638
+ stroke: currentColor;
639
+ stroke-width: 2;
640
+ stroke-linecap: round;
641
+ stroke-linejoin: round;
642
+ fill: none;
643
+ }
644
+ h1 {
645
+ margin: 0 0 12px 0;
646
+ font-size: 20px;
647
+ font-weight: 600;
648
+ color: var(--text-color);
649
+ }
650
+ p {
651
+ margin: 0 0 32px 0;
652
+ font-size: 14px;
653
+ line-height: 1.5;
654
+ color: var(--text-muted);
655
+ }
656
+ .btn {
657
+ display: inline-block;
658
+ background-color: var(--btn-bg);
659
+ color: var(--btn-text);
660
+ padding: 10px 16px;
661
+ border-radius: 4px;
662
+ text-decoration: none;
663
+ font-size: 14px;
664
+ font-weight: 500;
665
+ transition: background-color 0.2s;
666
+ }
667
+ .btn:hover {
668
+ background-color: var(--btn-hover);
669
+ }
670
+ </style>
671
+ </head>
672
+ <body>
673
+ ${content}
674
+ </body>
675
+ </html>`;
676
+ }
485
677
  function oauthService({ strapi: strapi2 }) {
486
678
  return {
487
679
  async createUser(email, lastname, firstname, locale, roles2 = []) {
@@ -493,8 +685,8 @@ function oauthService({ strapi: strapi2 }) {
493
685
  }
494
686
  }
495
687
  const createdUser = await userService.create({
496
- firstname: firstname ? firstname : "unset",
497
- lastname: lastname ? lastname : "",
688
+ firstname: firstname || "unset",
689
+ lastname: lastname || "",
498
690
  email: email.toLocaleLowerCase(),
499
691
  roles: roles2,
500
692
  preferedLanguage: locale
@@ -502,8 +694,8 @@ function oauthService({ strapi: strapi2 }) {
502
694
  return await userService.register({
503
695
  registrationToken: createdUser.registrationToken,
504
696
  userInfo: {
505
- firstname: firstname ? firstname : "unset",
506
- lastname: lastname ? lastname : "user",
697
+ firstname: firstname || "unset",
698
+ lastname: lastname || "user",
507
699
  password: generator.generate({
508
700
  length: 43,
509
701
  // 256 bits (https://en.wikipedia.org/wiki/Password_strength#Random_passwords)
@@ -527,11 +719,7 @@ function oauthService({ strapi: strapi2 }) {
527
719
  return `${origin}+${alias}${domain}`;
528
720
  },
529
721
  localeFindByHeader(headers) {
530
- if (headers["accept-language"] && headers["accept-language"].includes("ja")) {
531
- return "ja";
532
- } else {
533
- return "en";
534
- }
722
+ return headers["accept-language"]?.includes("ja") ? "ja" : "en";
535
723
  },
536
724
  async triggerWebHook(user) {
537
725
  let ENTRY_CREATE;
@@ -554,7 +742,7 @@ function oauthService({ strapi: strapi2 }) {
554
742
  });
555
743
  },
556
744
  triggerSignInSuccess(user) {
557
- delete user["password"];
745
+ delete user.password;
558
746
  const eventHub = strapi2.serviceMap.get("eventHub");
559
747
  eventHub.emit("admin.auth.success", {
560
748
  user,
@@ -566,40 +754,48 @@ function oauthService({ strapi: strapi2 }) {
566
754
  const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
567
755
  const REMEMBER_ME = config2["REMEMBER_ME"];
568
756
  const isRememberMe = !!REMEMBER_ME;
569
- return `
570
- <!doctype html>
571
- <html>
572
- <head>
573
- <noscript>
574
- <h3>JavaScript must be enabled for authentication</h3>
575
- </noscript>
576
- <script nonce="${nonce}">
577
- window.addEventListener('load', function() {
578
- if(${isRememberMe}){
579
- localStorage.setItem('jwtToken', '"${jwtToken}"');
580
- }else{
581
- document.cookie = 'jwtToken=${encodeURIComponent(jwtToken)}; Path=/';
582
- }
583
- localStorage.setItem('isLoggedIn', 'true');
584
- location.href = '${strapi2.config.admin.url}'
585
- })
586
- <\/script>
587
- </head>
588
- <body>
589
- </body>
590
- </html>`;
757
+ const content = `
758
+ <noscript>
759
+ <div class="card">
760
+ <div class="icon success">
761
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check">
762
+ <path d="M20 6 9 17l-5-5"/>
763
+ </svg>
764
+ </div>
765
+ <h1>JavaScript Required</h1>
766
+ <p>JavaScript must be enabled for authentication to complete.</p>
767
+ </div>
768
+ </noscript>
769
+ <script nonce="${nonce}">
770
+ window.addEventListener('load', function() {
771
+ if(${isRememberMe}){
772
+ localStorage.setItem('jwtToken', '"${jwtToken}"');
773
+ }else{
774
+ document.cookie = 'jwtToken=${encodeURIComponent(jwtToken)}; Path=/';
775
+ }
776
+ localStorage.setItem('isLoggedIn', 'true');
777
+ location.href = '${strapi2.config.admin.url}'
778
+ })
779
+ <\/script>`;
780
+ return renderHtmlTemplate("Authenticating...", content);
591
781
  },
592
782
  // Sign In Error
593
783
  renderSignUpError(message) {
594
- return `
595
- <!doctype html>
596
- <html>
597
- <head></head>
598
- <body>
599
- <h3>Authentication failed</h3>
600
- <p>${message}</p>
601
- </body>
602
- </html>`;
784
+ const safeMessage = String(message).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
785
+ const content = `
786
+ <div class="card">
787
+ <div class="icon">
788
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert">
789
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/>
790
+ <path d="M12 9v4"/>
791
+ <path d="M12 17h.01"/>
792
+ </svg>
793
+ </div>
794
+ <h1>Authentication Failed</h1>
795
+ <p>${safeMessage}</p>
796
+ <a href="${strapi2.config.admin.url}" class="btn">Return to Login</a>
797
+ </div>`;
798
+ return renderHtmlTemplate("Authentication Failed", content);
603
799
  },
604
800
  async generateToken(user, ctx) {
605
801
  const sessionManager = strapi2.sessionManager;
@@ -654,35 +850,35 @@ function roleService({ strapi: strapi2 }) {
654
850
  getOidcRoles() {
655
851
  return [
656
852
  {
657
- "oauth_type": this.OIDC_TYPE,
853
+ oauth_type: this.OIDC_TYPE,
658
854
  name: "OIDC"
659
855
  }
660
856
  ];
661
857
  },
662
858
  async oidcRoles() {
663
- return await strapi2.query("plugin::strapi-plugin-oidc.roles").findOne({
859
+ return strapi2.query("plugin::strapi-plugin-oidc.roles").findOne({
664
860
  where: {
665
- "oauth_type": this.OIDC_TYPE
861
+ oauth_type: this.OIDC_TYPE
666
862
  }
667
863
  });
668
864
  },
669
865
  async find() {
670
- return await strapi2.query("plugin::strapi-plugin-oidc.roles").findMany();
866
+ return strapi2.query("plugin::strapi-plugin-oidc.roles").findMany();
671
867
  },
672
868
  async update(roles2) {
673
869
  const query = strapi2.query("plugin::strapi-plugin-oidc.roles");
674
870
  await Promise.all(
675
871
  roles2.map(async (role2) => {
676
- const oidcRole = await query.findOne({ where: { "oauth_type": role2["oauth_type"] } });
872
+ const oidcRole = await query.findOne({ where: { oauth_type: role2.oauth_type } });
677
873
  if (oidcRole) {
678
874
  await query.update({
679
- where: { "oauth_type": role2["oauth_type"] },
875
+ where: { oauth_type: role2.oauth_type },
680
876
  data: { roles: role2.role }
681
877
  });
682
878
  } else {
683
879
  await query.create({
684
880
  data: {
685
- "oauth_type": role2["oauth_type"],
881
+ oauth_type: role2.oauth_type,
686
882
  roles: role2.role
687
883
  }
688
884
  });
@@ -693,53 +889,48 @@ function roleService({ strapi: strapi2 }) {
693
889
  };
694
890
  }
695
891
  function whitelistService({ strapi: strapi2 }) {
892
+ const getPluginStore = () => strapi2.store({
893
+ environment: "",
894
+ type: "plugin",
895
+ name: "strapi-plugin-oidc"
896
+ });
897
+ const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
696
898
  return {
697
899
  async getSettings() {
698
- const pluginStore = strapi2.store({ type: "plugin", name: "strapi-plugin-oidc" });
699
- let settings = await pluginStore.get({ key: "settings" });
900
+ let settings = await getPluginStore().get({ key: "settings" });
700
901
  if (!settings) {
701
902
  settings = { useWhitelist: true, enforceOIDC: false };
702
- await pluginStore.set({ key: "settings", value: settings });
903
+ await getPluginStore().set({ key: "settings", value: settings });
703
904
  }
704
905
  return settings;
705
906
  },
706
907
  async setSettings(settings) {
707
- const pluginStore = strapi2.store({ type: "plugin", name: "strapi-plugin-oidc" });
708
- await pluginStore.set({ key: "settings", value: settings });
908
+ await getPluginStore().set({ key: "settings", value: settings });
709
909
  },
710
910
  async getUsers() {
711
- const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
712
- return await query.findMany();
911
+ return getWhitelistQuery().findMany();
713
912
  },
714
913
  async registerUser(email, roles2) {
715
- const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
716
- await query.create({
717
- data: {
718
- email,
719
- roles: roles2
720
- }
914
+ await getWhitelistQuery().create({
915
+ data: { email, roles: roles2 }
721
916
  });
722
917
  },
723
918
  async removeUser(id) {
724
- const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
725
- await query.delete({
726
- where: {
727
- id
728
- }
919
+ await getWhitelistQuery().delete({
920
+ where: { id }
729
921
  });
730
922
  },
731
923
  async checkWhitelistForEmail(email) {
732
924
  const settings = await this.getSettings();
925
+ console.log("checkWhitelistForEmail settings:", settings);
733
926
  if (!settings.useWhitelist) {
734
927
  return null;
735
928
  }
736
- const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
737
- const result = await query.findOne({
738
- where: {
739
- email
740
- }
929
+ const result = await getWhitelistQuery().findOne({
930
+ where: { email }
741
931
  });
742
- if (result === null) {
932
+ console.log("checkWhitelistForEmail result:", result);
933
+ if (!result) {
743
934
  throw new Error("Not present in whitelist");
744
935
  }
745
936
  return result;