strapi-plugin-oidc 1.6.2 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -5
- package/dist/admin/{index-P9HriRms.mjs → index-AxBC5YLT.mjs} +8 -5
- package/dist/admin/{index-DTOcUHZi.js → index-DowwUs07.js} +81 -110
- package/dist/admin/{index-f3cmU_tE.js → index-EAfqxfV4.js} +8 -5
- package/dist/admin/{index-DmJadA2p.mjs → index-MnV7H8G6.mjs} +81 -110
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +143 -192
- package/dist/server/index.mjs +143 -192
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -30,52 +30,45 @@ function getRetentionDays() {
|
|
|
30
30
|
function isAuditLogEnabled() {
|
|
31
31
|
return getRetentionDays() !== 0;
|
|
32
32
|
}
|
|
33
|
+
const AUTH_ROUTES = ["login", "register", "register-admin", "forgot-password", "reset-password"];
|
|
33
34
|
async function bootstrap({ strapi: strapi2 }) {
|
|
34
35
|
const adminUrl = strapi2.config.get("admin.url", "/admin");
|
|
35
|
-
const authRoutes = [
|
|
36
|
-
`${adminUrl}/login`,
|
|
37
|
-
`${adminUrl}/register`,
|
|
38
|
-
`${adminUrl}/register-admin`,
|
|
39
|
-
`${adminUrl}/forgot-password`,
|
|
40
|
-
`${adminUrl}/reset-password`
|
|
41
|
-
];
|
|
42
36
|
const tokenRefreshPath = `${adminUrl}/token/refresh`;
|
|
43
37
|
const enforceOidcMiddleware = async (ctx, next) => {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
38
|
+
const path = ctx.request.path;
|
|
39
|
+
const isPost = ctx.request.method === "POST";
|
|
40
|
+
const isAuthRoute = AUTH_ROUTES.some((r) => path.includes(r));
|
|
41
|
+
const isTokenRefresh = path === tokenRefreshPath;
|
|
42
|
+
if (isAuthRoute && isPost || isTokenRefresh) {
|
|
47
43
|
try {
|
|
48
44
|
const whitelistService2 = strapi2.plugin("strapi-plugin-oidc").service("whitelist");
|
|
49
45
|
const settings = await whitelistService2.getSettings();
|
|
50
46
|
const enforceOIDC = resolveEnforceOIDC(strapi2, settings?.enforceOIDC);
|
|
51
|
-
if (enforceOIDC) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
47
|
+
if (enforceOIDC && isAuthRoute && isPost) {
|
|
48
|
+
ctx.status = 403;
|
|
49
|
+
ctx.body = {
|
|
50
|
+
data: null,
|
|
51
|
+
error: {
|
|
52
|
+
status: 403,
|
|
53
|
+
name: "ForbiddenError",
|
|
54
|
+
message: "Local login is disabled. Please use OIDC.",
|
|
55
|
+
details: {}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (enforceOIDC && isTokenRefresh && !ctx.cookies.get("oidc_authenticated")) {
|
|
61
|
+
ctx.status = 401;
|
|
62
|
+
ctx.body = {
|
|
63
|
+
data: null,
|
|
64
|
+
error: {
|
|
65
|
+
status: 401,
|
|
66
|
+
name: "UnauthorizedError",
|
|
67
|
+
message: "Session was not created via OIDC. Please log in again.",
|
|
68
|
+
details: {}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return;
|
|
79
72
|
}
|
|
80
73
|
} catch (err) {
|
|
81
74
|
strapi2.log.error("Error checking OIDC enforcement in middleware:", err);
|
|
@@ -89,18 +82,8 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
89
82
|
strapi2.server.use(enforceOidcMiddleware);
|
|
90
83
|
}
|
|
91
84
|
const actions = [
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
displayName: "Read",
|
|
95
|
-
uid: "read",
|
|
96
|
-
pluginName: "strapi-plugin-oidc"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
section: "plugins",
|
|
100
|
-
displayName: "Update",
|
|
101
|
-
uid: "update",
|
|
102
|
-
pluginName: "strapi-plugin-oidc"
|
|
103
|
-
}
|
|
85
|
+
{ section: "plugins", displayName: "Read", uid: "read", pluginName: "strapi-plugin-oidc" },
|
|
86
|
+
{ section: "plugins", displayName: "Update", uid: "update", pluginName: "strapi-plugin-oidc" }
|
|
104
87
|
];
|
|
105
88
|
await strapi2.admin.services.permission.actionProvider.registerMany(actions);
|
|
106
89
|
const enforceOIDCConfig = getEnforceOIDCConfig(strapi2);
|
|
@@ -119,17 +102,12 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
119
102
|
}
|
|
120
103
|
}
|
|
121
104
|
try {
|
|
122
|
-
const oidcRoleCount = await strapi2.query("plugin::strapi-plugin-oidc.roles").count({
|
|
123
|
-
where: { oauth_type: "4" }
|
|
124
|
-
});
|
|
105
|
+
const oidcRoleCount = await strapi2.query("plugin::strapi-plugin-oidc.roles").count({ where: { oauth_type: "4" } });
|
|
125
106
|
if (oidcRoleCount === 0) {
|
|
126
107
|
const defaultRole = await strapi2.query("admin::role").findOne({ where: { code: "strapi-editor" } }) ?? await strapi2.query("admin::role").findOne({});
|
|
127
108
|
if (defaultRole) {
|
|
128
109
|
await strapi2.query("plugin::strapi-plugin-oidc.roles").create({
|
|
129
|
-
data: {
|
|
130
|
-
oauth_type: "4",
|
|
131
|
-
roles: [defaultRole.id.toString()]
|
|
132
|
-
}
|
|
110
|
+
data: { oauth_type: "4", roles: [String(defaultRole.id)] }
|
|
133
111
|
});
|
|
134
112
|
}
|
|
135
113
|
}
|
|
@@ -147,7 +125,6 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
147
125
|
}
|
|
148
126
|
},
|
|
149
127
|
options: { rule: "0 0 * * *" }
|
|
150
|
-
// daily at midnight
|
|
151
128
|
}
|
|
152
129
|
});
|
|
153
130
|
}
|
|
@@ -249,24 +226,37 @@ const errorCodes = {
|
|
|
249
226
|
USER_CREATION_FAILED: "USER_CREATION_FAILED",
|
|
250
227
|
WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED"
|
|
251
228
|
};
|
|
229
|
+
const ERROR_DETAIL_TEMPLATES = {
|
|
230
|
+
token_exchange_failed: "Token exchange failed with HTTP status {status}",
|
|
231
|
+
userinfo_fetch_failed: "UserInfo endpoint returned HTTP {status}",
|
|
232
|
+
role_update_failed: "Role update failed for user {userId}: {error}",
|
|
233
|
+
user_creation_failed: "User creation failed for {email}: {error}",
|
|
234
|
+
id_token_parse_failed: "ID token parse failed: {error}",
|
|
235
|
+
sign_in_unknown: "Unknown sign-in error: {error}",
|
|
236
|
+
invalid_email: "Invalid email address received from OIDC provider",
|
|
237
|
+
whitelist_not_present: "Email not present in whitelist",
|
|
238
|
+
session_manager_unsupported: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
|
|
239
|
+
missing_config: "Missing required config keys: {keys}"
|
|
240
|
+
};
|
|
241
|
+
function interpolate$1(template, params) {
|
|
242
|
+
if (!params) return template;
|
|
243
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
|
|
244
|
+
}
|
|
252
245
|
function getErrorDetail(key, params) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
case "userinfo_fetch_failed":
|
|
257
|
-
return `UserInfo endpoint returned HTTP ${params?.status ?? "unknown"}`;
|
|
258
|
-
case "role_update_failed":
|
|
259
|
-
return `Role update failed for user ${params?.userId}: ${params?.error ?? "unknown"}`;
|
|
260
|
-
case "user_creation_failed":
|
|
261
|
-
return `User creation failed for ${params?.email}: ${params?.error ?? "unknown"}`;
|
|
262
|
-
case "id_token_parse_failed":
|
|
263
|
-
return `ID token parse failed: ${params?.error ?? "unknown"}`;
|
|
264
|
-
case "sign_in_unknown":
|
|
265
|
-
return `Unknown sign-in error: ${params?.error ?? "unknown"}`;
|
|
266
|
-
default:
|
|
267
|
-
return void 0;
|
|
268
|
-
}
|
|
246
|
+
const template = ERROR_DETAIL_TEMPLATES[key];
|
|
247
|
+
if (!template) return void 0;
|
|
248
|
+
return interpolate$1(template, params);
|
|
269
249
|
}
|
|
250
|
+
const errorMessages = {
|
|
251
|
+
TOKEN_EXCHANGE_FAILED: "Token exchange failed",
|
|
252
|
+
USERINFO_FETCH_FAILED: "Failed to fetch user info",
|
|
253
|
+
ID_TOKEN_PARSE_FAILED: "Failed to parse ID token",
|
|
254
|
+
NONCE_MISMATCH: "Nonce mismatch",
|
|
255
|
+
INVALID_EMAIL: "Invalid email address received from OIDC provider",
|
|
256
|
+
WHITELIST_NOT_PRESENT: "Not present in whitelist",
|
|
257
|
+
SESSION_MANAGER_UNSUPPORTED: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
|
|
258
|
+
MISSING_CONFIG: (keys) => `Missing required config keys: ${keys}`
|
|
259
|
+
};
|
|
270
260
|
const en = {
|
|
271
261
|
"global.plugins.strapi-plugin-oidc": "OIDC Plugin",
|
|
272
262
|
"page.title": "Configure OIDC default role(s) and access controls.",
|
|
@@ -331,6 +321,7 @@ const en = {
|
|
|
331
321
|
"auditlog.table.ip": "IP",
|
|
332
322
|
"auditlog.table.details": "Details",
|
|
333
323
|
"auditlog.table.empty": "No audit log entries",
|
|
324
|
+
"auditlog.loading": "Loading…",
|
|
334
325
|
"auditlog.clear": "Clear Logs",
|
|
335
326
|
"auditlog.clear.title": "Clear All Logs",
|
|
336
327
|
"auditlog.clear.description": "This will permanently delete all {count, plural, one {# audit log entry} other {# audit log entries}}. This action cannot be undone.",
|
|
@@ -349,7 +340,9 @@ const en = {
|
|
|
349
340
|
"auditlog.action.whitelist_rejected": "The user's email address is not on the whitelist. Access was denied.",
|
|
350
341
|
"user.missing_code": "Authorisation code was not received from the OIDC provider.",
|
|
351
342
|
"user.invalid_state": "State parameter mismatch. Please restart the login flow.",
|
|
352
|
-
"user.signInError": "Authentication failed. Please try again."
|
|
343
|
+
"user.signInError": "Authentication failed. Please try again.",
|
|
344
|
+
"settings.section": "OIDC",
|
|
345
|
+
"settings.configuration": "Configuration"
|
|
353
346
|
};
|
|
354
347
|
const userFacingMessages = {
|
|
355
348
|
get missing_code() {
|
|
@@ -376,12 +369,11 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
376
369
|
];
|
|
377
370
|
function configValidation() {
|
|
378
371
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
379
|
-
|
|
372
|
+
const missing = REQUIRED_CONFIG_KEYS.filter((key) => !config2[key]);
|
|
373
|
+
if (missing.length === 0) {
|
|
380
374
|
return config2;
|
|
381
375
|
}
|
|
382
|
-
throw new Error(
|
|
383
|
-
`The following configuration keys are required: ${REQUIRED_CONFIG_KEYS.join(", ")}`
|
|
384
|
-
);
|
|
376
|
+
throw new Error(errorMessages.MISSING_CONFIG(missing.join(", ")));
|
|
385
377
|
}
|
|
386
378
|
async function oidcSignIn(ctx) {
|
|
387
379
|
const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPE, OIDC_AUTHORIZATION_ENDPOINT } = configValidation();
|
|
@@ -422,7 +414,7 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
|
|
|
422
414
|
}
|
|
423
415
|
});
|
|
424
416
|
if (!response.ok) {
|
|
425
|
-
throw new Error(
|
|
417
|
+
throw new Error(errorMessages.TOKEN_EXCHANGE_FAILED);
|
|
426
418
|
}
|
|
427
419
|
const tokenData = await response.json();
|
|
428
420
|
if (tokenData.id_token) {
|
|
@@ -430,18 +422,18 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
|
|
|
430
422
|
const payloadB64 = tokenData.id_token.split(".")[1];
|
|
431
423
|
const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
|
|
432
424
|
if (idTokenPayload.nonce !== expectedNonce) {
|
|
433
|
-
throw new Error(
|
|
425
|
+
throw new Error(errorMessages.NONCE_MISMATCH);
|
|
434
426
|
}
|
|
435
427
|
} catch (e) {
|
|
436
428
|
if (e.message === "Nonce mismatch") throw e;
|
|
437
|
-
throw new Error(
|
|
429
|
+
throw new Error(errorMessages.ID_TOKEN_PARSE_FAILED);
|
|
438
430
|
}
|
|
439
431
|
}
|
|
440
432
|
const userResponse = await fetch(config2.OIDC_USERINFO_ENDPOINT, {
|
|
441
433
|
headers: { Authorization: `Bearer ${tokenData.access_token}` }
|
|
442
434
|
});
|
|
443
435
|
if (!userResponse.ok) {
|
|
444
|
-
throw new Error(
|
|
436
|
+
throw new Error(errorMessages.USERINFO_FETCH_FAILED);
|
|
445
437
|
}
|
|
446
438
|
const userInfo = await userResponse.json();
|
|
447
439
|
return { userInfo, accessToken: tokenData.access_token };
|
|
@@ -470,9 +462,9 @@ function resolveRolesFromGroups(userInfo, config2, availableRoles) {
|
|
|
470
462
|
}
|
|
471
463
|
async function resolveRoles(userInfo, config2, roleService2, availableRoles) {
|
|
472
464
|
const groupRoles = resolveRolesFromGroups(userInfo, config2, availableRoles);
|
|
473
|
-
if (groupRoles.length > 0) return groupRoles;
|
|
465
|
+
if (groupRoles.length > 0) return { roles: groupRoles, fromGroupMapping: true };
|
|
474
466
|
const oidcRoles = await roleService2.oidcRoles();
|
|
475
|
-
return oidcRoles?.roles || [];
|
|
467
|
+
return { roles: oidcRoles?.roles || [], fromGroupMapping: false };
|
|
476
468
|
}
|
|
477
469
|
async function registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2) {
|
|
478
470
|
const defaultLocale = oauthService2.localeFindByHeader(
|
|
@@ -489,7 +481,11 @@ async function registerNewUser(oauthService2, email, userResponseData, config2,
|
|
|
489
481
|
return activateUser;
|
|
490
482
|
}
|
|
491
483
|
function rolesChanged(current, next) {
|
|
492
|
-
|
|
484
|
+
if (current.size !== next.size) return true;
|
|
485
|
+
for (const id of next) {
|
|
486
|
+
if (!current.has(id)) return true;
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
493
489
|
}
|
|
494
490
|
async function updateUserRoles(user, currentRoleIds, newRoleIds) {
|
|
495
491
|
try {
|
|
@@ -516,11 +512,16 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
516
512
|
const rawEmail = String(userResponseData.email ?? "");
|
|
517
513
|
const email = rawEmail.toLowerCase();
|
|
518
514
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
519
|
-
throw new Error(
|
|
515
|
+
throw new Error(errorMessages.INVALID_EMAIL);
|
|
520
516
|
}
|
|
521
517
|
await whitelistService2.checkWhitelistForEmail(email);
|
|
522
518
|
const allRoles = await strapi.db.query("admin::role").findMany();
|
|
523
|
-
const roles2 = await resolveRoles(
|
|
519
|
+
const { roles: roles2, fromGroupMapping } = await resolveRoles(
|
|
520
|
+
userResponseData,
|
|
521
|
+
config2,
|
|
522
|
+
roleService2,
|
|
523
|
+
allRoles
|
|
524
|
+
);
|
|
524
525
|
const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
|
|
525
526
|
let userCreated = false;
|
|
526
527
|
let rolesUpdated = false;
|
|
@@ -529,16 +530,11 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
529
530
|
user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
|
|
530
531
|
userCreated = true;
|
|
531
532
|
rolesUpdated = true;
|
|
532
|
-
} else if (roles2.length > 0) {
|
|
533
|
-
const defaultRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
533
|
+
} else if (fromGroupMapping && roles2.length > 0) {
|
|
534
534
|
const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (isOnDefaultRoles) {
|
|
539
|
-
await updateUserRoles(user, currentRoleIds, roles2);
|
|
540
|
-
rolesUpdated = true;
|
|
541
|
-
}
|
|
535
|
+
if (rolesChanged(currentRoleIds, new Set(roles2))) {
|
|
536
|
+
await updateUserRoles(user, currentRoleIds, roles2);
|
|
537
|
+
rolesUpdated = true;
|
|
542
538
|
}
|
|
543
539
|
}
|
|
544
540
|
const jwtToken = await oauthService2.generateToken(user, ctx);
|
|
@@ -546,52 +542,39 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
546
542
|
return { activateUser: user, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
547
543
|
}
|
|
548
544
|
function classifyOidcError(msg, userInfo) {
|
|
549
|
-
|
|
550
|
-
{
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
test: (m) => m === "User creation failed" || m.includes("createUser"),
|
|
585
|
-
result: {
|
|
586
|
-
action: "login_failure",
|
|
587
|
-
code: errorCodes.USER_CREATION_FAILED,
|
|
588
|
-
key: "user_creation_failed",
|
|
589
|
-
params: userInfo?.email ? { email: userInfo.email, error: msg } : void 0
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
];
|
|
593
|
-
for (const { test, result } of errorMap) {
|
|
594
|
-
if (test(msg)) return result;
|
|
545
|
+
if (msg.includes("whitelist")) {
|
|
546
|
+
return {
|
|
547
|
+
action: "whitelist_rejected",
|
|
548
|
+
code: errorCodes.WHITELIST_CHECK_FAILED,
|
|
549
|
+
key: "whitelist_rejected"
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (msg === "Nonce mismatch")
|
|
553
|
+
return { action: "nonce_mismatch", code: errorCodes.NONCE_MISMATCH };
|
|
554
|
+
if (msg === "Token exchange failed")
|
|
555
|
+
return { action: "token_exchange_failed", code: errorCodes.TOKEN_EXCHANGE_FAILED };
|
|
556
|
+
if (msg === "Failed to fetch user info") {
|
|
557
|
+
return {
|
|
558
|
+
action: "login_failure",
|
|
559
|
+
code: errorCodes.USERINFO_FETCH_FAILED,
|
|
560
|
+
key: "userinfo_fetch_failed"
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (msg === "Failed to parse ID token") {
|
|
564
|
+
return {
|
|
565
|
+
action: "login_failure",
|
|
566
|
+
code: errorCodes.ID_TOKEN_PARSE_FAILED,
|
|
567
|
+
key: "id_token_parse_failed",
|
|
568
|
+
params: { error: msg }
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (msg === "User creation failed" || msg.includes("createUser")) {
|
|
572
|
+
return {
|
|
573
|
+
action: "login_failure",
|
|
574
|
+
code: errorCodes.USER_CREATION_FAILED,
|
|
575
|
+
key: "user_creation_failed",
|
|
576
|
+
params: userInfo?.email ? { email: userInfo.email, error: msg } : void 0
|
|
577
|
+
};
|
|
595
578
|
}
|
|
596
579
|
return {
|
|
597
580
|
action: "login_failure",
|
|
@@ -824,13 +807,9 @@ async function register(ctx) {
|
|
|
824
807
|
const whitelistService2 = getWhitelistService();
|
|
825
808
|
let matchedExistingUsersCount = 0;
|
|
826
809
|
for (const singleEmail of emailList) {
|
|
827
|
-
const existingUser = await strapi.query("admin::user").findOne({
|
|
828
|
-
where: { email: singleEmail }
|
|
829
|
-
});
|
|
810
|
+
const existingUser = await strapi.query("admin::user").findOne({ where: { email: singleEmail } });
|
|
830
811
|
if (existingUser) matchedExistingUsersCount++;
|
|
831
|
-
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({
|
|
832
|
-
where: { email: singleEmail }
|
|
833
|
-
});
|
|
812
|
+
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({ where: { email: singleEmail } });
|
|
834
813
|
if (!alreadyWhitelisted) {
|
|
835
814
|
await whitelistService2.registerUser(singleEmail);
|
|
836
815
|
}
|
|
@@ -972,9 +951,7 @@ function rateLimitMiddleware(ctx, next) {
|
|
|
972
951
|
const key = getRateLimitKey(ctx);
|
|
973
952
|
const now = Date.now();
|
|
974
953
|
const windowStart = now - RATE_LIMIT_WINDOW;
|
|
975
|
-
const requestStamps = (rateLimitMap.get(key)
|
|
976
|
-
(timestamp) => timestamp > windowStart
|
|
977
|
-
);
|
|
954
|
+
const requestStamps = (rateLimitMap.get(key) ?? []).filter((ts) => ts > windowStart);
|
|
978
955
|
if (requestStamps.length >= MAX_REQUESTS) {
|
|
979
956
|
ctx.status = 429;
|
|
980
957
|
ctx.body = "Too Many Requests";
|
|
@@ -1281,9 +1258,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1281
1258
|
const userService = strapi2.service("admin::user");
|
|
1282
1259
|
if (/[A-Z]/.test(email)) {
|
|
1283
1260
|
const dbUser = await userService.findOneByEmail(email.toLocaleLowerCase());
|
|
1284
|
-
if (dbUser)
|
|
1285
|
-
return dbUser;
|
|
1286
|
-
}
|
|
1261
|
+
if (dbUser) return dbUser;
|
|
1287
1262
|
}
|
|
1288
1263
|
const createdUser = await userService.create({
|
|
1289
1264
|
firstname: firstname || "unset",
|
|
@@ -1299,7 +1274,6 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1299
1274
|
lastname: lastname || "user",
|
|
1300
1275
|
password: generator.generate({
|
|
1301
1276
|
length: 43,
|
|
1302
|
-
// 256 bits (https://en.wikipedia.org/wiki/Password_strength#Random_passwords)
|
|
1303
1277
|
numbers: true,
|
|
1304
1278
|
lowercase: true,
|
|
1305
1279
|
uppercase: true,
|
|
@@ -1310,14 +1284,10 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1310
1284
|
});
|
|
1311
1285
|
},
|
|
1312
1286
|
addGmailAlias(baseEmail, baseAlias) {
|
|
1313
|
-
if (!baseAlias)
|
|
1314
|
-
return baseEmail;
|
|
1315
|
-
}
|
|
1287
|
+
if (!baseAlias) return baseEmail;
|
|
1316
1288
|
const alias = baseAlias.replace(/\+/g, "");
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1319
|
-
const domain = baseEmail.substring(beforePosition);
|
|
1320
|
-
return `${origin}+${alias}${domain}`;
|
|
1289
|
+
const atIndex = baseEmail.indexOf("@");
|
|
1290
|
+
return `${baseEmail.slice(0, atIndex)}+${alias}${baseEmail.slice(atIndex)}`;
|
|
1321
1291
|
},
|
|
1322
1292
|
localeFindByHeader(headers) {
|
|
1323
1293
|
return headers["accept-language"]?.includes("ja") ? "ja" : "en";
|
|
@@ -1398,9 +1368,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1398
1368
|
async generateToken(user, ctx) {
|
|
1399
1369
|
const sessionManager = strapi2.sessionManager;
|
|
1400
1370
|
if (!sessionManager) {
|
|
1401
|
-
throw new Error(
|
|
1402
|
-
"sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later."
|
|
1403
|
-
);
|
|
1371
|
+
throw new Error(errorMessages.SESSION_MANAGER_UNSUPPORTED);
|
|
1404
1372
|
}
|
|
1405
1373
|
const userId = String(user.id);
|
|
1406
1374
|
const deviceId = randomUUID();
|
|
@@ -1490,15 +1458,11 @@ function roleService({ strapi: strapi2 }) {
|
|
|
1490
1458
|
}
|
|
1491
1459
|
};
|
|
1492
1460
|
}
|
|
1461
|
+
const SETTINGS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1493
1462
|
function whitelistService({ strapi: strapi2 }) {
|
|
1494
|
-
const getPluginStore = () => strapi2.store({
|
|
1495
|
-
environment: "",
|
|
1496
|
-
type: "plugin",
|
|
1497
|
-
name: "strapi-plugin-oidc"
|
|
1498
|
-
});
|
|
1499
|
-
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
1500
1463
|
let settingsCache = null;
|
|
1501
|
-
const
|
|
1464
|
+
const getPluginStore = () => strapi2.store({ environment: "", type: "plugin", name: "strapi-plugin-oidc" });
|
|
1465
|
+
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
1502
1466
|
return {
|
|
1503
1467
|
async getSettings() {
|
|
1504
1468
|
const now = Date.now();
|
|
@@ -1507,10 +1471,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1507
1471
|
}
|
|
1508
1472
|
let settings = await getPluginStore().get({ key: "settings" });
|
|
1509
1473
|
if (!settings) {
|
|
1510
|
-
settings = {
|
|
1511
|
-
useWhitelist: true,
|
|
1512
|
-
enforceOIDC: false
|
|
1513
|
-
};
|
|
1474
|
+
settings = { useWhitelist: true, enforceOIDC: false };
|
|
1514
1475
|
await getPluginStore().set({ key: "settings", value: settings });
|
|
1515
1476
|
}
|
|
1516
1477
|
settingsCache = { value: settings, ts: now };
|
|
@@ -1524,26 +1485,18 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1524
1485
|
return getWhitelistQuery().findMany();
|
|
1525
1486
|
},
|
|
1526
1487
|
async registerUser(email) {
|
|
1527
|
-
await getWhitelistQuery().create({
|
|
1528
|
-
data: { email }
|
|
1529
|
-
});
|
|
1488
|
+
await getWhitelistQuery().create({ data: { email } });
|
|
1530
1489
|
},
|
|
1531
1490
|
async removeUser(email) {
|
|
1532
|
-
await getWhitelistQuery().deleteMany({
|
|
1533
|
-
where: { email }
|
|
1534
|
-
});
|
|
1491
|
+
await getWhitelistQuery().deleteMany({ where: { email } });
|
|
1535
1492
|
},
|
|
1536
1493
|
async checkWhitelistForEmail(email) {
|
|
1537
1494
|
const settings = await this.getSettings();
|
|
1538
|
-
if (!settings.useWhitelist)
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1495
|
+
if (!settings.useWhitelist) return null;
|
|
1541
1496
|
const result = await getWhitelistQuery().findOne({
|
|
1542
1497
|
where: { email }
|
|
1543
1498
|
});
|
|
1544
|
-
if (!result)
|
|
1545
|
-
throw new Error("Not present in whitelist");
|
|
1546
|
-
}
|
|
1499
|
+
if (!result) throw new Error(errorMessages.WHITELIST_NOT_PRESENT);
|
|
1547
1500
|
return result;
|
|
1548
1501
|
}
|
|
1549
1502
|
};
|
|
@@ -1604,9 +1557,7 @@ function auditLogService({ strapi: strapi2 }) {
|
|
|
1604
1557
|
},
|
|
1605
1558
|
async cleanup(retentionDays) {
|
|
1606
1559
|
const cutoff = new Date(Date.now() - retentionDays * 864e5);
|
|
1607
|
-
await strapi2.db.query("plugin::strapi-plugin-oidc.audit-log").deleteMany({
|
|
1608
|
-
where: { createdAt: { $lt: cutoff } }
|
|
1609
|
-
});
|
|
1560
|
+
await strapi2.db.query("plugin::strapi-plugin-oidc.audit-log").deleteMany({ where: { createdAt: { $lt: cutoff } } });
|
|
1610
1561
|
}
|
|
1611
1562
|
};
|
|
1612
1563
|
}
|
package/package.json
CHANGED