strapi-plugin-oidc 1.7.5 → 1.8.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.
@@ -1,5 +1,6 @@
1
1
  import { randomUUID, randomBytes, createHash } from "node:crypto";
2
2
  import pkceChallenge from "pkce-challenge";
3
+ import { jwtVerify, errors, createRemoteJWKSet } from "jose";
3
4
  import { Readable } from "node:stream";
4
5
  import strapiUtils from "@strapi/utils";
5
6
  import generator from "generate-password";
@@ -93,6 +94,16 @@ async function bootstrap({ strapi: strapi2 }) {
93
94
  { section: "plugins", displayName: "Update", uid: "update", pluginName: "strapi-plugin-oidc" }
94
95
  ];
95
96
  await strapi2.admin.services.permission.actionProvider.registerMany(actions);
97
+ const contentApiScopeUids = [
98
+ "plugin::strapi-plugin-oidc.whitelist.read",
99
+ "plugin::strapi-plugin-oidc.whitelist.write",
100
+ "plugin::strapi-plugin-oidc.whitelist.delete",
101
+ "plugin::strapi-plugin-oidc.audit.read",
102
+ "plugin::strapi-plugin-oidc.audit.delete"
103
+ ];
104
+ for (const uid of contentApiScopeUids) {
105
+ strapi2.contentAPI.permissions.providers.action.register(uid, { uid });
106
+ }
96
107
  const enforceOIDCConfig = getEnforceOIDCConfig(strapi2);
97
108
  if (enforceOIDCConfig !== null) {
98
109
  try {
@@ -156,7 +167,12 @@ const config = {
156
167
  // null = use DB setting; true/false = override DB (useful for lockout recovery)
157
168
  AUDIT_LOG_RETENTION_DAYS: 90,
158
169
  OIDC_GROUP_FIELD: "groups",
159
- OIDC_GROUP_ROLE_MAP: "{}"
170
+ OIDC_GROUP_ROLE_MAP: "{}",
171
+ OIDC_REQUIRE_EMAIL_VERIFIED: true,
172
+ OIDC_TRUSTED_IP_HEADER: "",
173
+ OIDC_JWKS_URI: "",
174
+ OIDC_ISSUER: "",
175
+ OIDC_FORCE_SECURE_COOKIES: false
160
176
  },
161
177
  validator() {
162
178
  }
@@ -205,11 +221,21 @@ const contentTypes = {
205
221
  whitelists,
206
222
  "audit-log": auditLog$1
207
223
  };
208
- function getExpiredCookieOptions(strapi2, ctx) {
224
+ function shouldMarkSecure(strapi2, ctx) {
209
225
  const isProduction = strapi2.config.get("environment") === "production";
226
+ if (!isProduction) return false;
227
+ const config2 = strapi2.config.get("plugin::strapi-plugin-oidc") ?? {};
228
+ if (config2.OIDC_FORCE_SECURE_COOKIES === true) return true;
229
+ if (ctx.request.secure) return true;
230
+ const proxyTrusted = ctx.app?.proxy === true;
231
+ if (proxyTrusted && typeof ctx.get === "function" && ctx.get("x-forwarded-proto") === "https")
232
+ return true;
233
+ return false;
234
+ }
235
+ function getExpiredCookieOptions(strapi2, ctx) {
210
236
  return {
211
237
  httpOnly: true,
212
- secure: isProduction && ctx.request.secure,
238
+ secure: shouldMarkSecure(strapi2, ctx),
213
239
  path: strapi2.config.get("admin.auth.cookie.path", "/admin"),
214
240
  domain: strapi2.config.get("admin.auth.cookie.domain") || strapi2.config.get("admin.auth.domain"),
215
241
  sameSite: strapi2.config.get("admin.auth.cookie.sameSite", "lax"),
@@ -236,7 +262,9 @@ const errorCodes = {
236
262
  NONCE_MISMATCH: "NONCE_MISMATCH",
237
263
  ROLE_UPDATE_FAILED: "ROLE_UPDATE_FAILED",
238
264
  USER_CREATION_FAILED: "USER_CREATION_FAILED",
239
- WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED"
265
+ WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED",
266
+ EMAIL_NOT_VERIFIED: "EMAIL_NOT_VERIFIED",
267
+ ID_TOKEN_INVALID: "ID_TOKEN_INVALID"
240
268
  };
241
269
  const ERROR_DETAIL_TEMPLATES = {
242
270
  token_exchange_failed: "Token exchange failed with HTTP status {status}",
@@ -246,6 +274,8 @@ const ERROR_DETAIL_TEMPLATES = {
246
274
  id_token_parse_failed: "ID token parse failed: {error}",
247
275
  sign_in_unknown: "Unknown sign-in error: {error}",
248
276
  invalid_email: "Invalid email address received from OIDC provider",
277
+ email_not_verified: "Email address has not been verified by the OIDC provider",
278
+ id_token_invalid: "ID token verification failed: {error}",
249
279
  whitelist_not_present: "Email not present in whitelist",
250
280
  session_manager_unsupported: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
251
281
  missing_config: "Missing required config keys: {keys}"
@@ -265,6 +295,8 @@ const errorMessages = {
265
295
  ID_TOKEN_PARSE_FAILED: "Failed to parse ID token",
266
296
  NONCE_MISMATCH: "Nonce mismatch",
267
297
  INVALID_EMAIL: "Invalid email address received from OIDC provider",
298
+ EMAIL_NOT_VERIFIED: "Email address has not been verified by the OIDC provider",
299
+ ID_TOKEN_INVALID: "ID token verification failed",
268
300
  WHITELIST_NOT_PRESENT: "Not present in whitelist",
269
301
  SESSION_MANAGER_UNSUPPORTED: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
270
302
  MISSING_CONFIG: (keys) => `Missing required config keys: ${keys}`
@@ -362,6 +394,8 @@ const en = {
362
394
  "auditlog.action.nonce_mismatch": "The nonce in the ID token did not match the one generated at login. This may indicate a token replay attack.",
363
395
  "auditlog.action.token_exchange_failed": "The authorisation code could not be exchanged for tokens. The OIDC provider rejected the request.",
364
396
  "auditlog.action.whitelist_rejected": "The user's email address is not on the whitelist. Access was denied.",
397
+ "auditlog.action.email_not_verified": "The OIDC provider did not confirm the user's email address as verified. Access was denied.",
398
+ "auditlog.action.id_token_invalid": "The ID token failed signature, issuer, audience, or expiry validation. Access was denied.",
365
399
  "auth.page.authenticating.title": "Authenticating...",
366
400
  "auth.page.authenticating.noscript.heading": "JavaScript Required",
367
401
  "auth.page.authenticating.noscript.body": "JavaScript must be enabled for authentication to complete.",
@@ -471,24 +505,42 @@ const OIDC_ERROR_DISPATCH = {
471
505
  code: errorCodes.TOKEN_EXCHANGE_FAILED,
472
506
  key: "sign_in_unknown"
473
507
  },
508
+ email_not_verified: {
509
+ action: "email_not_verified",
510
+ code: errorCodes.EMAIL_NOT_VERIFIED,
511
+ key: "email_not_verified"
512
+ },
513
+ id_token_invalid: {
514
+ action: "id_token_invalid",
515
+ code: errorCodes.ID_TOKEN_INVALID,
516
+ key: "id_token_invalid"
517
+ },
474
518
  unknown: {
475
519
  action: "login_failure",
476
520
  code: errorCodes.TOKEN_EXCHANGE_FAILED,
477
521
  key: "sign_in_unknown"
478
522
  }
479
523
  };
524
+ const TRUSTED_HEADER_WHITELIST = /* @__PURE__ */ new Set(["cf-connecting-ip"]);
525
+ function getTrustedHeaderName() {
526
+ const config2 = strapi.config.get("plugin::strapi-plugin-oidc") ?? {};
527
+ const raw = config2.OIDC_TRUSTED_IP_HEADER;
528
+ if (typeof raw !== "string" || !raw) return void 0;
529
+ const normalized = raw.trim().toLowerCase();
530
+ return TRUSTED_HEADER_WHITELIST.has(normalized) ? normalized : void 0;
531
+ }
480
532
  function getClientIp(ctx) {
481
- const cfConnectingIp = ctx.get("CF-Connecting-IP");
482
- if (cfConnectingIp) {
483
- return cfConnectingIp.split(",")[0].trim();
484
- }
485
- const forwardedFor = ctx.get("X-Forwarded-For");
486
- if (forwardedFor) {
487
- return forwardedFor.split(",")[0].trim();
488
- }
489
- const realIp = ctx.get("X-Real-IP");
490
- if (realIp) {
491
- return realIp.trim();
533
+ const proxyTrusted = ctx.app?.proxy === true;
534
+ if (proxyTrusted) {
535
+ const trustedHeader = getTrustedHeaderName();
536
+ if (trustedHeader) {
537
+ const value = ctx.get(trustedHeader);
538
+ if (value) return value.split(",")[0].trim();
539
+ }
540
+ const forwarded = ctx.request.ips;
541
+ if (forwarded && forwarded.length > 0) {
542
+ return forwarded[0];
543
+ }
492
544
  }
493
545
  return ctx.ip;
494
546
  }
@@ -505,6 +557,43 @@ const REQUIRED_CONFIG_KEYS = [
505
557
  "OIDC_AUTHORIZATION_ENDPOINT"
506
558
  ];
507
559
  const LOGOUT_USERINFO_TIMEOUT_MS = 3e3;
560
+ const jwksCache = /* @__PURE__ */ new Map();
561
+ let jwksDisabledWarned = false;
562
+ function getJwks(uri) {
563
+ let jwks = jwksCache.get(uri);
564
+ if (!jwks) {
565
+ jwks = createRemoteJWKSet(new URL(uri));
566
+ jwksCache.set(uri, jwks);
567
+ }
568
+ return jwks;
569
+ }
570
+ async function verifyIdToken(idToken, config2) {
571
+ const jwksUri = config2.OIDC_JWKS_URI;
572
+ const issuer = config2.OIDC_ISSUER;
573
+ if (!jwksUri) {
574
+ if (!jwksDisabledWarned) {
575
+ jwksDisabledWarned = true;
576
+ strapi.log.warn(
577
+ "[OIDC] OIDC_JWKS_URI is not configured — ID token signature verification is disabled. Set OIDC_JWKS_URI and OIDC_ISSUER from your provider's discovery document."
578
+ );
579
+ }
580
+ return null;
581
+ }
582
+ try {
583
+ const jwks = getJwks(jwksUri);
584
+ const { payload } = await jwtVerify(idToken, jwks, {
585
+ issuer: issuer || void 0,
586
+ audience: config2.OIDC_CLIENT_ID
587
+ });
588
+ return payload;
589
+ } catch (e) {
590
+ if (e instanceof errors.JWTClaimValidationFailed || e instanceof errors.JWSSignatureVerificationFailed || e instanceof errors.JWTExpired || e instanceof errors.JWTInvalid || e instanceof errors.JWSInvalid) {
591
+ const msg = e instanceof Error ? e.message : String(e);
592
+ throw new OidcError("id_token_invalid", msg, e);
593
+ }
594
+ throw e;
595
+ }
596
+ }
508
597
  function configValidation() {
509
598
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
510
599
  const missing = REQUIRED_CONFIG_KEYS.filter((key) => !config2[key]);
@@ -518,11 +607,10 @@ async function oidcSignIn(ctx) {
518
607
  const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge();
519
608
  const state = randomBytes(32).toString("base64url");
520
609
  const nonce = randomBytes(32).toString("base64url");
521
- const isProduction = strapi.config.get("environment") === "production";
522
610
  const cookieOptions = {
523
611
  httpOnly: true,
524
612
  maxAge: 6e5,
525
- secure: isProduction && ctx.request.secure,
613
+ secure: shouldMarkSecure(strapi, ctx),
526
614
  sameSite: "lax"
527
615
  };
528
616
  ctx.cookies.set("oidc_code_verifier", codeVerifier, cookieOptions);
@@ -555,14 +643,16 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
555
643
  }
556
644
  const tokenData = await response.json();
557
645
  if (tokenData.id_token) {
646
+ const verifiedPayload = await verifyIdToken(tokenData.id_token, config2);
558
647
  try {
559
- const payloadB64 = tokenData.id_token.split(".")[1];
560
- const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
648
+ const idTokenPayload = verifiedPayload ?? JSON.parse(
649
+ Buffer.from(tokenData.id_token.split(".")[1], "base64url").toString("utf8")
650
+ );
561
651
  if (idTokenPayload.nonce !== expectedNonce) {
562
652
  throw new OidcError("nonce_mismatch", errorMessages.NONCE_MISMATCH);
563
653
  }
564
654
  } catch (e) {
565
- if (e instanceof OidcError && e.kind === "nonce_mismatch") throw e;
655
+ if (e instanceof OidcError) throw e;
566
656
  throw new OidcError("id_token_parse_failed", errorMessages.ID_TOKEN_PARSE_FAILED, e);
567
657
  }
568
658
  }
@@ -708,6 +798,13 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
708
798
  if (!email || !isValidEmail(email)) {
709
799
  throw new OidcError("invalid_email", errorMessages.INVALID_EMAIL);
710
800
  }
801
+ if (config2.OIDC_REQUIRE_EMAIL_VERIFIED !== false) {
802
+ const emailVerified = userResponseData.email_verified;
803
+ const isVerified = emailVerified === true || emailVerified === "true";
804
+ if (!isVerified) {
805
+ throw new OidcError("email_not_verified", errorMessages.EMAIL_NOT_VERIFIED);
806
+ }
807
+ }
711
808
  await whitelistService2.checkWhitelistForEmail(email);
712
809
  const resolved = await resolveRoles(userResponseData, config2, roleService2);
713
810
  const { user, userCreated, rolesUpdated } = await ensureUser(
@@ -734,7 +831,7 @@ function classifyOidcError(e, userInfo) {
734
831
  const dispatch = OIDC_ERROR_DISPATCH[kind];
735
832
  const msg = e instanceof Error ? e.message : String(e);
736
833
  let params;
737
- if (kind === "id_token_parse_failed" || kind === "unknown") {
834
+ if (kind === "id_token_parse_failed" || kind === "id_token_invalid" || kind === "unknown") {
738
835
  params = { error: msg };
739
836
  } else if (kind === "user_creation_failed" && userInfo?.email) {
740
837
  params = { email: userInfo.email, error: msg };
@@ -829,8 +926,7 @@ async function oidcSignInCallback(ctx) {
829
926
  try {
830
927
  const exchangeResult = await exchangeTokenAndFetchUserInfo(config2, params, oidcNonce ?? "");
831
928
  userInfo = exchangeResult.userInfo;
832
- const isProduction = strapi.config.get("environment") === "production";
833
- const secureFlag = isProduction && ctx.request.secure;
929
+ const secureFlag = shouldMarkSecure(strapi, ctx);
834
930
  ctx.cookies.set("oidc_access_token", exchangeResult.accessToken, {
835
931
  httpOnly: true,
836
932
  maxAge: 3e5,
@@ -1000,16 +1096,34 @@ async function register(ctx) {
1000
1096
  return;
1001
1097
  }
1002
1098
  const rawEmails = Array.isArray(email) ? email : email.split(",");
1003
- const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
1099
+ const normalized = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
1100
+ const rejectedEmails = [];
1101
+ const validEmails = [];
1102
+ for (const e of normalized) {
1103
+ if (isValidEmail(e)) {
1104
+ validEmails.push(e);
1105
+ } else {
1106
+ rejectedEmails.push(e);
1107
+ }
1108
+ }
1109
+ if (validEmails.length === 0) {
1110
+ ctx.status = 400;
1111
+ ctx.body = { error: "No valid email addresses supplied", rejectedEmails };
1112
+ return;
1113
+ }
1004
1114
  const whitelistService2 = getWhitelistService();
1005
- const matchedExistingUsersCount = await whitelistService2.countAdminUsersByEmails(emailList);
1006
- for (const singleEmail of emailList) {
1115
+ let acceptedCount = 0;
1116
+ let alreadyWhitelistedCount = 0;
1117
+ for (const singleEmail of validEmails) {
1007
1118
  const alreadyWhitelisted = await whitelistService2.hasUser(singleEmail);
1008
- if (!alreadyWhitelisted) {
1119
+ if (alreadyWhitelisted) {
1120
+ alreadyWhitelistedCount++;
1121
+ } else {
1009
1122
  await whitelistService2.registerUser(singleEmail);
1123
+ acceptedCount++;
1010
1124
  }
1011
1125
  }
1012
- ctx.body = { matchedExistingUsersCount };
1126
+ ctx.body = { acceptedCount, alreadyWhitelistedCount, rejectedEmails };
1013
1127
  }
1014
1128
  async function removeEmail(ctx) {
1015
1129
  const { email } = ctx.params;
@@ -1065,7 +1179,7 @@ async function syncUsers(ctx) {
1065
1179
  await whitelistService2.registerUser(email);
1066
1180
  }
1067
1181
  }
1068
- ctx.body = { matchedExistingUsersCount: 0 };
1182
+ ctx.body = {};
1069
1183
  }
1070
1184
  const whitelist = {
1071
1185
  info,
@@ -1086,6 +1200,8 @@ const AUDIT_ACTIONS = [
1086
1200
  "nonce_mismatch",
1087
1201
  "token_exchange_failed",
1088
1202
  "whitelist_rejected",
1203
+ "email_not_verified",
1204
+ "id_token_invalid",
1089
1205
  "logout",
1090
1206
  "session_expired",
1091
1207
  "user_created"
@@ -1285,6 +1401,22 @@ const controllers = {
1285
1401
  const rateLimitMap = /* @__PURE__ */ new Map();
1286
1402
  const RATE_LIMIT_WINDOW = 6e4;
1287
1403
  const MAX_REQUESTS = 1e3;
1404
+ const MAX_MAP_SIZE = 1e4;
1405
+ const PRUNE_THRESHOLD = 1e3;
1406
+ function pruneExpiredEntries(now) {
1407
+ const windowStart = now - RATE_LIMIT_WINDOW;
1408
+ for (const [key, stamps] of rateLimitMap) {
1409
+ if (stamps.length === 0 || stamps[stamps.length - 1] <= windowStart) {
1410
+ rateLimitMap.delete(key);
1411
+ }
1412
+ }
1413
+ }
1414
+ function evictOldestEntry() {
1415
+ const oldest = rateLimitMap.keys().next().value;
1416
+ if (oldest !== void 0) {
1417
+ rateLimitMap.delete(oldest);
1418
+ }
1419
+ }
1288
1420
  function getRateLimitKey(ctx) {
1289
1421
  const ip = getClientIp(ctx);
1290
1422
  const ua = ctx.request.header["user-agent"] ?? "";
@@ -1295,6 +1427,9 @@ function rateLimitMiddleware(ctx, next) {
1295
1427
  const key = getRateLimitKey(ctx);
1296
1428
  const now = Date.now();
1297
1429
  const windowStart = now - RATE_LIMIT_WINDOW;
1430
+ if (rateLimitMap.size > PRUNE_THRESHOLD) {
1431
+ pruneExpiredEntries(now);
1432
+ }
1298
1433
  const requestStamps = (rateLimitMap.get(key) ?? []).filter((ts) => ts > windowStart);
1299
1434
  if (requestStamps.length >= MAX_REQUESTS) {
1300
1435
  ctx.status = 429;
@@ -1302,6 +1437,9 @@ function rateLimitMiddleware(ctx, next) {
1302
1437
  return;
1303
1438
  }
1304
1439
  requestStamps.push(now);
1440
+ if (!rateLimitMap.has(key) && rateLimitMap.size >= MAX_MAP_SIZE) {
1441
+ evictOldestEntry();
1442
+ }
1305
1443
  rateLimitMap.set(key, requestStamps);
1306
1444
  return next();
1307
1445
  }
@@ -1345,7 +1483,7 @@ const routes = {
1345
1483
  config: { auth: false, middlewares: [rateLimitMiddleware] }
1346
1484
  },
1347
1485
  {
1348
- method: "GET",
1486
+ method: "POST",
1349
1487
  path: "/logout",
1350
1488
  handler: "oidc.logout",
1351
1489
  config: { auth: false }
@@ -1427,53 +1565,63 @@ const routes = {
1427
1565
  // API-token-authenticated routes for programmatic whitelist management.
1428
1566
  // Accessible at /strapi-plugin-oidc/... using a Strapi API token
1429
1567
  // (full-access or custom) in the Authorization: Bearer <token> header.
1568
+ // Custom tokens must be granted one or more of the semantic scopes below.
1430
1569
  "content-api": {
1431
1570
  type: "content-api",
1432
1571
  routes: [
1433
1572
  {
1434
1573
  method: "GET",
1435
1574
  path: "/whitelist",
1436
- handler: "whitelist.info"
1575
+ handler: "whitelist.info",
1576
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.read"] } }
1437
1577
  },
1438
1578
  {
1439
1579
  method: "POST",
1440
1580
  path: "/whitelist",
1441
- handler: "whitelist.register"
1581
+ handler: "whitelist.register",
1582
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.write"] } }
1442
1583
  },
1443
1584
  {
1444
1585
  method: "POST",
1445
1586
  path: "/whitelist/import",
1446
- handler: "whitelist.importUsers"
1587
+ handler: "whitelist.importUsers",
1588
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.write"] } }
1447
1589
  },
1448
1590
  {
1449
1591
  method: "DELETE",
1450
1592
  path: "/whitelist/:email",
1451
- handler: "whitelist.removeEmail"
1593
+ handler: "whitelist.removeEmail",
1594
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.delete"] } }
1452
1595
  },
1453
1596
  {
1454
1597
  method: "DELETE",
1455
1598
  path: "/whitelist",
1456
- handler: "whitelist.deleteAll"
1599
+ handler: "whitelist.deleteAll",
1600
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.delete"] } }
1457
1601
  },
1458
1602
  {
1459
1603
  method: "GET",
1460
1604
  path: "/whitelist/export",
1461
- handler: "whitelist.exportWhitelist"
1605
+ handler: "whitelist.exportWhitelist",
1606
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.read"] } }
1462
1607
  },
1463
1608
  {
1464
1609
  method: "GET",
1465
1610
  path: "/audit-logs",
1466
- handler: "auditLog.find"
1611
+ handler: "auditLog.find",
1612
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.read"] } }
1467
1613
  },
1468
1614
  {
1469
1615
  method: "GET",
1470
1616
  path: "/audit-logs/export",
1471
- handler: "auditLog.export"
1617
+ handler: "auditLog.export",
1618
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.read"] } }
1472
1619
  },
1473
1620
  {
1474
1621
  method: "DELETE",
1475
1622
  path: "/audit-logs",
1476
- handler: "auditLog.clearAll"
1623
+ handler: "auditLog.clearAll",
1624
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.delete"] } }
1477
1625
  }
1478
1626
  ]
1479
1627
  }
@@ -1725,13 +1873,12 @@ function oauthService({ strapi: strapi2 }) {
1725
1873
  type: rememberMe ? "refresh" : "session"
1726
1874
  }
1727
1875
  );
1728
- const isProduction = strapi2.config.get("environment") === "production";
1729
1876
  const domain = strapi2.config.get("admin.auth.cookie.domain") || strapi2.config.get("admin.auth.domain");
1730
1877
  const path = strapi2.config.get("admin.auth.cookie.path", "/admin");
1731
1878
  const sameSite = strapi2.config.get("admin.auth.cookie.sameSite", "lax");
1732
1879
  const cookieOptions = {
1733
1880
  httpOnly: true,
1734
- secure: isProduction && ctx.request.secure,
1881
+ secure: shouldMarkSecure(strapi2, ctx),
1735
1882
  overwrite: true,
1736
1883
  domain,
1737
1884
  path,
@@ -1850,14 +1997,6 @@ function whitelistService({ strapi: strapi2 }) {
1850
1997
  },
1851
1998
  async deleteAllUsers() {
1852
1999
  await getWhitelistQuery().deleteMany({});
1853
- },
1854
- async countAdminUsersByEmails(emails) {
1855
- if (emails.length === 0) return 0;
1856
- const rows = await strapi2.query("admin::user").findMany({
1857
- where: { email: { $in: emails } },
1858
- select: ["id"]
1859
- });
1860
- return rows.length;
1861
2000
  }
1862
2001
  };
1863
2002
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-oidc",
3
- "version": "1.7.5",
3
+ "version": "1.8.0",
4
4
  "description": "A Strapi plugin that provides OpenID Connect (OIDC) authentication functionality for the Strapi Admin Panel.",
5
5
  "strapi": {
6
6
  "displayName": "OIDC Plugin",
@@ -50,6 +50,7 @@
50
50
  "@strapi/icons": "^2.2.0",
51
51
  "@strapi/utils": "^5.41.1",
52
52
  "generate-password": "^1.7.1",
53
+ "jose": "^6.2.2",
53
54
  "lucide-react": "^1.8.0",
54
55
  "pkce-challenge": "^6.0.0",
55
56
  "react-intl": "^6.8.9"