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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
3
  const node_crypto = require("node:crypto");
4
4
  const pkceChallenge = require("pkce-challenge");
5
+ const jose = require("jose");
5
6
  const node_stream = require("node:stream");
6
7
  const strapiUtils = require("@strapi/utils");
7
8
  const generator = require("generate-password");
@@ -99,6 +100,16 @@ async function bootstrap({ strapi: strapi2 }) {
99
100
  { section: "plugins", displayName: "Update", uid: "update", pluginName: "strapi-plugin-oidc" }
100
101
  ];
101
102
  await strapi2.admin.services.permission.actionProvider.registerMany(actions);
103
+ const contentApiScopeUids = [
104
+ "plugin::strapi-plugin-oidc.whitelist.read",
105
+ "plugin::strapi-plugin-oidc.whitelist.write",
106
+ "plugin::strapi-plugin-oidc.whitelist.delete",
107
+ "plugin::strapi-plugin-oidc.audit.read",
108
+ "plugin::strapi-plugin-oidc.audit.delete"
109
+ ];
110
+ for (const uid of contentApiScopeUids) {
111
+ strapi2.contentAPI.permissions.providers.action.register(uid, { uid });
112
+ }
102
113
  const enforceOIDCConfig = getEnforceOIDCConfig(strapi2);
103
114
  if (enforceOIDCConfig !== null) {
104
115
  try {
@@ -162,7 +173,12 @@ const config = {
162
173
  // null = use DB setting; true/false = override DB (useful for lockout recovery)
163
174
  AUDIT_LOG_RETENTION_DAYS: 90,
164
175
  OIDC_GROUP_FIELD: "groups",
165
- OIDC_GROUP_ROLE_MAP: "{}"
176
+ OIDC_GROUP_ROLE_MAP: "{}",
177
+ OIDC_REQUIRE_EMAIL_VERIFIED: true,
178
+ OIDC_TRUSTED_IP_HEADER: "",
179
+ OIDC_JWKS_URI: "",
180
+ OIDC_ISSUER: "",
181
+ OIDC_FORCE_SECURE_COOKIES: false
166
182
  },
167
183
  validator() {
168
184
  }
@@ -211,11 +227,21 @@ const contentTypes = {
211
227
  whitelists,
212
228
  "audit-log": auditLog$1
213
229
  };
214
- function getExpiredCookieOptions(strapi2, ctx) {
230
+ function shouldMarkSecure(strapi2, ctx) {
215
231
  const isProduction = strapi2.config.get("environment") === "production";
232
+ if (!isProduction) return false;
233
+ const config2 = strapi2.config.get("plugin::strapi-plugin-oidc") ?? {};
234
+ if (config2.OIDC_FORCE_SECURE_COOKIES === true) return true;
235
+ if (ctx.request.secure) return true;
236
+ const proxyTrusted = ctx.app?.proxy === true;
237
+ if (proxyTrusted && typeof ctx.get === "function" && ctx.get("x-forwarded-proto") === "https")
238
+ return true;
239
+ return false;
240
+ }
241
+ function getExpiredCookieOptions(strapi2, ctx) {
216
242
  return {
217
243
  httpOnly: true,
218
- secure: isProduction && ctx.request.secure,
244
+ secure: shouldMarkSecure(strapi2, ctx),
219
245
  path: strapi2.config.get("admin.auth.cookie.path", "/admin"),
220
246
  domain: strapi2.config.get("admin.auth.cookie.domain") || strapi2.config.get("admin.auth.domain"),
221
247
  sameSite: strapi2.config.get("admin.auth.cookie.sameSite", "lax"),
@@ -242,7 +268,9 @@ const errorCodes = {
242
268
  NONCE_MISMATCH: "NONCE_MISMATCH",
243
269
  ROLE_UPDATE_FAILED: "ROLE_UPDATE_FAILED",
244
270
  USER_CREATION_FAILED: "USER_CREATION_FAILED",
245
- WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED"
271
+ WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED",
272
+ EMAIL_NOT_VERIFIED: "EMAIL_NOT_VERIFIED",
273
+ ID_TOKEN_INVALID: "ID_TOKEN_INVALID"
246
274
  };
247
275
  const ERROR_DETAIL_TEMPLATES = {
248
276
  token_exchange_failed: "Token exchange failed with HTTP status {status}",
@@ -252,6 +280,8 @@ const ERROR_DETAIL_TEMPLATES = {
252
280
  id_token_parse_failed: "ID token parse failed: {error}",
253
281
  sign_in_unknown: "Unknown sign-in error: {error}",
254
282
  invalid_email: "Invalid email address received from OIDC provider",
283
+ email_not_verified: "Email address has not been verified by the OIDC provider",
284
+ id_token_invalid: "ID token verification failed: {error}",
255
285
  whitelist_not_present: "Email not present in whitelist",
256
286
  session_manager_unsupported: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
257
287
  missing_config: "Missing required config keys: {keys}"
@@ -271,6 +301,8 @@ const errorMessages = {
271
301
  ID_TOKEN_PARSE_FAILED: "Failed to parse ID token",
272
302
  NONCE_MISMATCH: "Nonce mismatch",
273
303
  INVALID_EMAIL: "Invalid email address received from OIDC provider",
304
+ EMAIL_NOT_VERIFIED: "Email address has not been verified by the OIDC provider",
305
+ ID_TOKEN_INVALID: "ID token verification failed",
274
306
  WHITELIST_NOT_PRESENT: "Not present in whitelist",
275
307
  SESSION_MANAGER_UNSUPPORTED: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
276
308
  MISSING_CONFIG: (keys) => `Missing required config keys: ${keys}`
@@ -368,6 +400,8 @@ const en = {
368
400
  "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.",
369
401
  "auditlog.action.token_exchange_failed": "The authorisation code could not be exchanged for tokens. The OIDC provider rejected the request.",
370
402
  "auditlog.action.whitelist_rejected": "The user's email address is not on the whitelist. Access was denied.",
403
+ "auditlog.action.email_not_verified": "The OIDC provider did not confirm the user's email address as verified. Access was denied.",
404
+ "auditlog.action.id_token_invalid": "The ID token failed signature, issuer, audience, or expiry validation. Access was denied.",
371
405
  "auth.page.authenticating.title": "Authenticating...",
372
406
  "auth.page.authenticating.noscript.heading": "JavaScript Required",
373
407
  "auth.page.authenticating.noscript.body": "JavaScript must be enabled for authentication to complete.",
@@ -477,24 +511,42 @@ const OIDC_ERROR_DISPATCH = {
477
511
  code: errorCodes.TOKEN_EXCHANGE_FAILED,
478
512
  key: "sign_in_unknown"
479
513
  },
514
+ email_not_verified: {
515
+ action: "email_not_verified",
516
+ code: errorCodes.EMAIL_NOT_VERIFIED,
517
+ key: "email_not_verified"
518
+ },
519
+ id_token_invalid: {
520
+ action: "id_token_invalid",
521
+ code: errorCodes.ID_TOKEN_INVALID,
522
+ key: "id_token_invalid"
523
+ },
480
524
  unknown: {
481
525
  action: "login_failure",
482
526
  code: errorCodes.TOKEN_EXCHANGE_FAILED,
483
527
  key: "sign_in_unknown"
484
528
  }
485
529
  };
530
+ const TRUSTED_HEADER_WHITELIST = /* @__PURE__ */ new Set(["cf-connecting-ip"]);
531
+ function getTrustedHeaderName() {
532
+ const config2 = strapi.config.get("plugin::strapi-plugin-oidc") ?? {};
533
+ const raw = config2.OIDC_TRUSTED_IP_HEADER;
534
+ if (typeof raw !== "string" || !raw) return void 0;
535
+ const normalized = raw.trim().toLowerCase();
536
+ return TRUSTED_HEADER_WHITELIST.has(normalized) ? normalized : void 0;
537
+ }
486
538
  function getClientIp(ctx) {
487
- const cfConnectingIp = ctx.get("CF-Connecting-IP");
488
- if (cfConnectingIp) {
489
- return cfConnectingIp.split(",")[0].trim();
490
- }
491
- const forwardedFor = ctx.get("X-Forwarded-For");
492
- if (forwardedFor) {
493
- return forwardedFor.split(",")[0].trim();
494
- }
495
- const realIp = ctx.get("X-Real-IP");
496
- if (realIp) {
497
- return realIp.trim();
539
+ const proxyTrusted = ctx.app?.proxy === true;
540
+ if (proxyTrusted) {
541
+ const trustedHeader = getTrustedHeaderName();
542
+ if (trustedHeader) {
543
+ const value = ctx.get(trustedHeader);
544
+ if (value) return value.split(",")[0].trim();
545
+ }
546
+ const forwarded = ctx.request.ips;
547
+ if (forwarded && forwarded.length > 0) {
548
+ return forwarded[0];
549
+ }
498
550
  }
499
551
  return ctx.ip;
500
552
  }
@@ -511,6 +563,43 @@ const REQUIRED_CONFIG_KEYS = [
511
563
  "OIDC_AUTHORIZATION_ENDPOINT"
512
564
  ];
513
565
  const LOGOUT_USERINFO_TIMEOUT_MS = 3e3;
566
+ const jwksCache = /* @__PURE__ */ new Map();
567
+ let jwksDisabledWarned = false;
568
+ function getJwks(uri) {
569
+ let jwks = jwksCache.get(uri);
570
+ if (!jwks) {
571
+ jwks = jose.createRemoteJWKSet(new URL(uri));
572
+ jwksCache.set(uri, jwks);
573
+ }
574
+ return jwks;
575
+ }
576
+ async function verifyIdToken(idToken, config2) {
577
+ const jwksUri = config2.OIDC_JWKS_URI;
578
+ const issuer = config2.OIDC_ISSUER;
579
+ if (!jwksUri) {
580
+ if (!jwksDisabledWarned) {
581
+ jwksDisabledWarned = true;
582
+ strapi.log.warn(
583
+ "[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."
584
+ );
585
+ }
586
+ return null;
587
+ }
588
+ try {
589
+ const jwks = getJwks(jwksUri);
590
+ const { payload } = await jose.jwtVerify(idToken, jwks, {
591
+ issuer: issuer || void 0,
592
+ audience: config2.OIDC_CLIENT_ID
593
+ });
594
+ return payload;
595
+ } catch (e) {
596
+ if (e instanceof jose.errors.JWTClaimValidationFailed || e instanceof jose.errors.JWSSignatureVerificationFailed || e instanceof jose.errors.JWTExpired || e instanceof jose.errors.JWTInvalid || e instanceof jose.errors.JWSInvalid) {
597
+ const msg = e instanceof Error ? e.message : String(e);
598
+ throw new OidcError("id_token_invalid", msg, e);
599
+ }
600
+ throw e;
601
+ }
602
+ }
514
603
  function configValidation() {
515
604
  const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
516
605
  const missing = REQUIRED_CONFIG_KEYS.filter((key) => !config2[key]);
@@ -524,11 +613,10 @@ async function oidcSignIn(ctx) {
524
613
  const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge__default.default();
525
614
  const state = node_crypto.randomBytes(32).toString("base64url");
526
615
  const nonce = node_crypto.randomBytes(32).toString("base64url");
527
- const isProduction = strapi.config.get("environment") === "production";
528
616
  const cookieOptions = {
529
617
  httpOnly: true,
530
618
  maxAge: 6e5,
531
- secure: isProduction && ctx.request.secure,
619
+ secure: shouldMarkSecure(strapi, ctx),
532
620
  sameSite: "lax"
533
621
  };
534
622
  ctx.cookies.set("oidc_code_verifier", codeVerifier, cookieOptions);
@@ -561,14 +649,16 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
561
649
  }
562
650
  const tokenData = await response.json();
563
651
  if (tokenData.id_token) {
652
+ const verifiedPayload = await verifyIdToken(tokenData.id_token, config2);
564
653
  try {
565
- const payloadB64 = tokenData.id_token.split(".")[1];
566
- const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
654
+ const idTokenPayload = verifiedPayload ?? JSON.parse(
655
+ Buffer.from(tokenData.id_token.split(".")[1], "base64url").toString("utf8")
656
+ );
567
657
  if (idTokenPayload.nonce !== expectedNonce) {
568
658
  throw new OidcError("nonce_mismatch", errorMessages.NONCE_MISMATCH);
569
659
  }
570
660
  } catch (e) {
571
- if (e instanceof OidcError && e.kind === "nonce_mismatch") throw e;
661
+ if (e instanceof OidcError) throw e;
572
662
  throw new OidcError("id_token_parse_failed", errorMessages.ID_TOKEN_PARSE_FAILED, e);
573
663
  }
574
664
  }
@@ -714,6 +804,13 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
714
804
  if (!email || !isValidEmail(email)) {
715
805
  throw new OidcError("invalid_email", errorMessages.INVALID_EMAIL);
716
806
  }
807
+ if (config2.OIDC_REQUIRE_EMAIL_VERIFIED !== false) {
808
+ const emailVerified = userResponseData.email_verified;
809
+ const isVerified = emailVerified === true || emailVerified === "true";
810
+ if (!isVerified) {
811
+ throw new OidcError("email_not_verified", errorMessages.EMAIL_NOT_VERIFIED);
812
+ }
813
+ }
717
814
  await whitelistService2.checkWhitelistForEmail(email);
718
815
  const resolved = await resolveRoles(userResponseData, config2, roleService2);
719
816
  const { user, userCreated, rolesUpdated } = await ensureUser(
@@ -740,7 +837,7 @@ function classifyOidcError(e, userInfo) {
740
837
  const dispatch = OIDC_ERROR_DISPATCH[kind];
741
838
  const msg = e instanceof Error ? e.message : String(e);
742
839
  let params;
743
- if (kind === "id_token_parse_failed" || kind === "unknown") {
840
+ if (kind === "id_token_parse_failed" || kind === "id_token_invalid" || kind === "unknown") {
744
841
  params = { error: msg };
745
842
  } else if (kind === "user_creation_failed" && userInfo?.email) {
746
843
  params = { email: userInfo.email, error: msg };
@@ -835,8 +932,7 @@ async function oidcSignInCallback(ctx) {
835
932
  try {
836
933
  const exchangeResult = await exchangeTokenAndFetchUserInfo(config2, params, oidcNonce ?? "");
837
934
  userInfo = exchangeResult.userInfo;
838
- const isProduction = strapi.config.get("environment") === "production";
839
- const secureFlag = isProduction && ctx.request.secure;
935
+ const secureFlag = shouldMarkSecure(strapi, ctx);
840
936
  ctx.cookies.set("oidc_access_token", exchangeResult.accessToken, {
841
937
  httpOnly: true,
842
938
  maxAge: 3e5,
@@ -1006,16 +1102,34 @@ async function register(ctx) {
1006
1102
  return;
1007
1103
  }
1008
1104
  const rawEmails = Array.isArray(email) ? email : email.split(",");
1009
- const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
1105
+ const normalized = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
1106
+ const rejectedEmails = [];
1107
+ const validEmails = [];
1108
+ for (const e of normalized) {
1109
+ if (isValidEmail(e)) {
1110
+ validEmails.push(e);
1111
+ } else {
1112
+ rejectedEmails.push(e);
1113
+ }
1114
+ }
1115
+ if (validEmails.length === 0) {
1116
+ ctx.status = 400;
1117
+ ctx.body = { error: "No valid email addresses supplied", rejectedEmails };
1118
+ return;
1119
+ }
1010
1120
  const whitelistService2 = getWhitelistService();
1011
- const matchedExistingUsersCount = await whitelistService2.countAdminUsersByEmails(emailList);
1012
- for (const singleEmail of emailList) {
1121
+ let acceptedCount = 0;
1122
+ let alreadyWhitelistedCount = 0;
1123
+ for (const singleEmail of validEmails) {
1013
1124
  const alreadyWhitelisted = await whitelistService2.hasUser(singleEmail);
1014
- if (!alreadyWhitelisted) {
1125
+ if (alreadyWhitelisted) {
1126
+ alreadyWhitelistedCount++;
1127
+ } else {
1015
1128
  await whitelistService2.registerUser(singleEmail);
1129
+ acceptedCount++;
1016
1130
  }
1017
1131
  }
1018
- ctx.body = { matchedExistingUsersCount };
1132
+ ctx.body = { acceptedCount, alreadyWhitelistedCount, rejectedEmails };
1019
1133
  }
1020
1134
  async function removeEmail(ctx) {
1021
1135
  const { email } = ctx.params;
@@ -1071,7 +1185,7 @@ async function syncUsers(ctx) {
1071
1185
  await whitelistService2.registerUser(email);
1072
1186
  }
1073
1187
  }
1074
- ctx.body = { matchedExistingUsersCount: 0 };
1188
+ ctx.body = {};
1075
1189
  }
1076
1190
  const whitelist = {
1077
1191
  info,
@@ -1092,6 +1206,8 @@ const AUDIT_ACTIONS = [
1092
1206
  "nonce_mismatch",
1093
1207
  "token_exchange_failed",
1094
1208
  "whitelist_rejected",
1209
+ "email_not_verified",
1210
+ "id_token_invalid",
1095
1211
  "logout",
1096
1212
  "session_expired",
1097
1213
  "user_created"
@@ -1291,6 +1407,22 @@ const controllers = {
1291
1407
  const rateLimitMap = /* @__PURE__ */ new Map();
1292
1408
  const RATE_LIMIT_WINDOW = 6e4;
1293
1409
  const MAX_REQUESTS = 1e3;
1410
+ const MAX_MAP_SIZE = 1e4;
1411
+ const PRUNE_THRESHOLD = 1e3;
1412
+ function pruneExpiredEntries(now) {
1413
+ const windowStart = now - RATE_LIMIT_WINDOW;
1414
+ for (const [key, stamps] of rateLimitMap) {
1415
+ if (stamps.length === 0 || stamps[stamps.length - 1] <= windowStart) {
1416
+ rateLimitMap.delete(key);
1417
+ }
1418
+ }
1419
+ }
1420
+ function evictOldestEntry() {
1421
+ const oldest = rateLimitMap.keys().next().value;
1422
+ if (oldest !== void 0) {
1423
+ rateLimitMap.delete(oldest);
1424
+ }
1425
+ }
1294
1426
  function getRateLimitKey(ctx) {
1295
1427
  const ip = getClientIp(ctx);
1296
1428
  const ua = ctx.request.header["user-agent"] ?? "";
@@ -1301,6 +1433,9 @@ function rateLimitMiddleware(ctx, next) {
1301
1433
  const key = getRateLimitKey(ctx);
1302
1434
  const now = Date.now();
1303
1435
  const windowStart = now - RATE_LIMIT_WINDOW;
1436
+ if (rateLimitMap.size > PRUNE_THRESHOLD) {
1437
+ pruneExpiredEntries(now);
1438
+ }
1304
1439
  const requestStamps = (rateLimitMap.get(key) ?? []).filter((ts) => ts > windowStart);
1305
1440
  if (requestStamps.length >= MAX_REQUESTS) {
1306
1441
  ctx.status = 429;
@@ -1308,6 +1443,9 @@ function rateLimitMiddleware(ctx, next) {
1308
1443
  return;
1309
1444
  }
1310
1445
  requestStamps.push(now);
1446
+ if (!rateLimitMap.has(key) && rateLimitMap.size >= MAX_MAP_SIZE) {
1447
+ evictOldestEntry();
1448
+ }
1311
1449
  rateLimitMap.set(key, requestStamps);
1312
1450
  return next();
1313
1451
  }
@@ -1351,7 +1489,7 @@ const routes = {
1351
1489
  config: { auth: false, middlewares: [rateLimitMiddleware] }
1352
1490
  },
1353
1491
  {
1354
- method: "GET",
1492
+ method: "POST",
1355
1493
  path: "/logout",
1356
1494
  handler: "oidc.logout",
1357
1495
  config: { auth: false }
@@ -1433,53 +1571,63 @@ const routes = {
1433
1571
  // API-token-authenticated routes for programmatic whitelist management.
1434
1572
  // Accessible at /strapi-plugin-oidc/... using a Strapi API token
1435
1573
  // (full-access or custom) in the Authorization: Bearer <token> header.
1574
+ // Custom tokens must be granted one or more of the semantic scopes below.
1436
1575
  "content-api": {
1437
1576
  type: "content-api",
1438
1577
  routes: [
1439
1578
  {
1440
1579
  method: "GET",
1441
1580
  path: "/whitelist",
1442
- handler: "whitelist.info"
1581
+ handler: "whitelist.info",
1582
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.read"] } }
1443
1583
  },
1444
1584
  {
1445
1585
  method: "POST",
1446
1586
  path: "/whitelist",
1447
- handler: "whitelist.register"
1587
+ handler: "whitelist.register",
1588
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.write"] } }
1448
1589
  },
1449
1590
  {
1450
1591
  method: "POST",
1451
1592
  path: "/whitelist/import",
1452
- handler: "whitelist.importUsers"
1593
+ handler: "whitelist.importUsers",
1594
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.write"] } }
1453
1595
  },
1454
1596
  {
1455
1597
  method: "DELETE",
1456
1598
  path: "/whitelist/:email",
1457
- handler: "whitelist.removeEmail"
1599
+ handler: "whitelist.removeEmail",
1600
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.delete"] } }
1458
1601
  },
1459
1602
  {
1460
1603
  method: "DELETE",
1461
1604
  path: "/whitelist",
1462
- handler: "whitelist.deleteAll"
1605
+ handler: "whitelist.deleteAll",
1606
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.delete"] } }
1463
1607
  },
1464
1608
  {
1465
1609
  method: "GET",
1466
1610
  path: "/whitelist/export",
1467
- handler: "whitelist.exportWhitelist"
1611
+ handler: "whitelist.exportWhitelist",
1612
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.whitelist.read"] } }
1468
1613
  },
1469
1614
  {
1470
1615
  method: "GET",
1471
1616
  path: "/audit-logs",
1472
- handler: "auditLog.find"
1617
+ handler: "auditLog.find",
1618
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.read"] } }
1473
1619
  },
1474
1620
  {
1475
1621
  method: "GET",
1476
1622
  path: "/audit-logs/export",
1477
- handler: "auditLog.export"
1623
+ handler: "auditLog.export",
1624
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.read"] } }
1478
1625
  },
1479
1626
  {
1480
1627
  method: "DELETE",
1481
1628
  path: "/audit-logs",
1482
- handler: "auditLog.clearAll"
1629
+ handler: "auditLog.clearAll",
1630
+ config: { auth: { scope: ["plugin::strapi-plugin-oidc.audit.delete"] } }
1483
1631
  }
1484
1632
  ]
1485
1633
  }
@@ -1731,13 +1879,12 @@ function oauthService({ strapi: strapi2 }) {
1731
1879
  type: rememberMe ? "refresh" : "session"
1732
1880
  }
1733
1881
  );
1734
- const isProduction = strapi2.config.get("environment") === "production";
1735
1882
  const domain = strapi2.config.get("admin.auth.cookie.domain") || strapi2.config.get("admin.auth.domain");
1736
1883
  const path = strapi2.config.get("admin.auth.cookie.path", "/admin");
1737
1884
  const sameSite = strapi2.config.get("admin.auth.cookie.sameSite", "lax");
1738
1885
  const cookieOptions = {
1739
1886
  httpOnly: true,
1740
- secure: isProduction && ctx.request.secure,
1887
+ secure: shouldMarkSecure(strapi2, ctx),
1741
1888
  overwrite: true,
1742
1889
  domain,
1743
1890
  path,
@@ -1856,14 +2003,6 @@ function whitelistService({ strapi: strapi2 }) {
1856
2003
  },
1857
2004
  async deleteAllUsers() {
1858
2005
  await getWhitelistQuery().deleteMany({});
1859
- },
1860
- async countAdminUsersByEmails(emails) {
1861
- if (emails.length === 0) return 0;
1862
- const rows = await strapi2.query("admin::user").findMany({
1863
- where: { email: { $in: emails } },
1864
- select: ["id"]
1865
- });
1866
- return rows.length;
1867
2006
  }
1868
2007
  };
1869
2008
  }