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.js
CHANGED
|
@@ -36,52 +36,45 @@ function getRetentionDays() {
|
|
|
36
36
|
function isAuditLogEnabled() {
|
|
37
37
|
return getRetentionDays() !== 0;
|
|
38
38
|
}
|
|
39
|
+
const AUTH_ROUTES = ["login", "register", "register-admin", "forgot-password", "reset-password"];
|
|
39
40
|
async function bootstrap({ strapi: strapi2 }) {
|
|
40
41
|
const adminUrl = strapi2.config.get("admin.url", "/admin");
|
|
41
|
-
const authRoutes = [
|
|
42
|
-
`${adminUrl}/login`,
|
|
43
|
-
`${adminUrl}/register`,
|
|
44
|
-
`${adminUrl}/register-admin`,
|
|
45
|
-
`${adminUrl}/forgot-password`,
|
|
46
|
-
`${adminUrl}/reset-password`
|
|
47
|
-
];
|
|
48
42
|
const tokenRefreshPath = `${adminUrl}/token/refresh`;
|
|
49
43
|
const enforceOidcMiddleware = async (ctx, next) => {
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
44
|
+
const path = ctx.request.path;
|
|
45
|
+
const isPost = ctx.request.method === "POST";
|
|
46
|
+
const isAuthRoute = AUTH_ROUTES.some((r) => path.includes(r));
|
|
47
|
+
const isTokenRefresh = path === tokenRefreshPath;
|
|
48
|
+
if (isAuthRoute && isPost || isTokenRefresh) {
|
|
53
49
|
try {
|
|
54
50
|
const whitelistService2 = strapi2.plugin("strapi-plugin-oidc").service("whitelist");
|
|
55
51
|
const settings = await whitelistService2.getSettings();
|
|
56
52
|
const enforceOIDC = resolveEnforceOIDC(strapi2, settings?.enforceOIDC);
|
|
57
|
-
if (enforceOIDC) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
53
|
+
if (enforceOIDC && isAuthRoute && isPost) {
|
|
54
|
+
ctx.status = 403;
|
|
55
|
+
ctx.body = {
|
|
56
|
+
data: null,
|
|
57
|
+
error: {
|
|
58
|
+
status: 403,
|
|
59
|
+
name: "ForbiddenError",
|
|
60
|
+
message: "Local login is disabled. Please use OIDC.",
|
|
61
|
+
details: {}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (enforceOIDC && isTokenRefresh && !ctx.cookies.get("oidc_authenticated")) {
|
|
67
|
+
ctx.status = 401;
|
|
68
|
+
ctx.body = {
|
|
69
|
+
data: null,
|
|
70
|
+
error: {
|
|
71
|
+
status: 401,
|
|
72
|
+
name: "UnauthorizedError",
|
|
73
|
+
message: "Session was not created via OIDC. Please log in again.",
|
|
74
|
+
details: {}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return;
|
|
85
78
|
}
|
|
86
79
|
} catch (err) {
|
|
87
80
|
strapi2.log.error("Error checking OIDC enforcement in middleware:", err);
|
|
@@ -95,18 +88,8 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
95
88
|
strapi2.server.use(enforceOidcMiddleware);
|
|
96
89
|
}
|
|
97
90
|
const actions = [
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
displayName: "Read",
|
|
101
|
-
uid: "read",
|
|
102
|
-
pluginName: "strapi-plugin-oidc"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
section: "plugins",
|
|
106
|
-
displayName: "Update",
|
|
107
|
-
uid: "update",
|
|
108
|
-
pluginName: "strapi-plugin-oidc"
|
|
109
|
-
}
|
|
91
|
+
{ section: "plugins", displayName: "Read", uid: "read", pluginName: "strapi-plugin-oidc" },
|
|
92
|
+
{ section: "plugins", displayName: "Update", uid: "update", pluginName: "strapi-plugin-oidc" }
|
|
110
93
|
];
|
|
111
94
|
await strapi2.admin.services.permission.actionProvider.registerMany(actions);
|
|
112
95
|
const enforceOIDCConfig = getEnforceOIDCConfig(strapi2);
|
|
@@ -125,17 +108,12 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
125
108
|
}
|
|
126
109
|
}
|
|
127
110
|
try {
|
|
128
|
-
const oidcRoleCount = await strapi2.query("plugin::strapi-plugin-oidc.roles").count({
|
|
129
|
-
where: { oauth_type: "4" }
|
|
130
|
-
});
|
|
111
|
+
const oidcRoleCount = await strapi2.query("plugin::strapi-plugin-oidc.roles").count({ where: { oauth_type: "4" } });
|
|
131
112
|
if (oidcRoleCount === 0) {
|
|
132
113
|
const defaultRole = await strapi2.query("admin::role").findOne({ where: { code: "strapi-editor" } }) ?? await strapi2.query("admin::role").findOne({});
|
|
133
114
|
if (defaultRole) {
|
|
134
115
|
await strapi2.query("plugin::strapi-plugin-oidc.roles").create({
|
|
135
|
-
data: {
|
|
136
|
-
oauth_type: "4",
|
|
137
|
-
roles: [defaultRole.id.toString()]
|
|
138
|
-
}
|
|
116
|
+
data: { oauth_type: "4", roles: [String(defaultRole.id)] }
|
|
139
117
|
});
|
|
140
118
|
}
|
|
141
119
|
}
|
|
@@ -153,7 +131,6 @@ async function bootstrap({ strapi: strapi2 }) {
|
|
|
153
131
|
}
|
|
154
132
|
},
|
|
155
133
|
options: { rule: "0 0 * * *" }
|
|
156
|
-
// daily at midnight
|
|
157
134
|
}
|
|
158
135
|
});
|
|
159
136
|
}
|
|
@@ -255,24 +232,37 @@ const errorCodes = {
|
|
|
255
232
|
USER_CREATION_FAILED: "USER_CREATION_FAILED",
|
|
256
233
|
WHITELIST_CHECK_FAILED: "WHITELIST_CHECK_FAILED"
|
|
257
234
|
};
|
|
235
|
+
const ERROR_DETAIL_TEMPLATES = {
|
|
236
|
+
token_exchange_failed: "Token exchange failed with HTTP status {status}",
|
|
237
|
+
userinfo_fetch_failed: "UserInfo endpoint returned HTTP {status}",
|
|
238
|
+
role_update_failed: "Role update failed for user {userId}: {error}",
|
|
239
|
+
user_creation_failed: "User creation failed for {email}: {error}",
|
|
240
|
+
id_token_parse_failed: "ID token parse failed: {error}",
|
|
241
|
+
sign_in_unknown: "Unknown sign-in error: {error}",
|
|
242
|
+
invalid_email: "Invalid email address received from OIDC provider",
|
|
243
|
+
whitelist_not_present: "Email not present in whitelist",
|
|
244
|
+
session_manager_unsupported: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
|
|
245
|
+
missing_config: "Missing required config keys: {keys}"
|
|
246
|
+
};
|
|
247
|
+
function interpolate$1(template, params) {
|
|
248
|
+
if (!params) return template;
|
|
249
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
|
|
250
|
+
}
|
|
258
251
|
function getErrorDetail(key, params) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
case "userinfo_fetch_failed":
|
|
263
|
-
return `UserInfo endpoint returned HTTP ${params?.status ?? "unknown"}`;
|
|
264
|
-
case "role_update_failed":
|
|
265
|
-
return `Role update failed for user ${params?.userId}: ${params?.error ?? "unknown"}`;
|
|
266
|
-
case "user_creation_failed":
|
|
267
|
-
return `User creation failed for ${params?.email}: ${params?.error ?? "unknown"}`;
|
|
268
|
-
case "id_token_parse_failed":
|
|
269
|
-
return `ID token parse failed: ${params?.error ?? "unknown"}`;
|
|
270
|
-
case "sign_in_unknown":
|
|
271
|
-
return `Unknown sign-in error: ${params?.error ?? "unknown"}`;
|
|
272
|
-
default:
|
|
273
|
-
return void 0;
|
|
274
|
-
}
|
|
252
|
+
const template = ERROR_DETAIL_TEMPLATES[key];
|
|
253
|
+
if (!template) return void 0;
|
|
254
|
+
return interpolate$1(template, params);
|
|
275
255
|
}
|
|
256
|
+
const errorMessages = {
|
|
257
|
+
TOKEN_EXCHANGE_FAILED: "Token exchange failed",
|
|
258
|
+
USERINFO_FETCH_FAILED: "Failed to fetch user info",
|
|
259
|
+
ID_TOKEN_PARSE_FAILED: "Failed to parse ID token",
|
|
260
|
+
NONCE_MISMATCH: "Nonce mismatch",
|
|
261
|
+
INVALID_EMAIL: "Invalid email address received from OIDC provider",
|
|
262
|
+
WHITELIST_NOT_PRESENT: "Not present in whitelist",
|
|
263
|
+
SESSION_MANAGER_UNSUPPORTED: "sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later.",
|
|
264
|
+
MISSING_CONFIG: (keys) => `Missing required config keys: ${keys}`
|
|
265
|
+
};
|
|
276
266
|
const en = {
|
|
277
267
|
"global.plugins.strapi-plugin-oidc": "OIDC Plugin",
|
|
278
268
|
"page.title": "Configure OIDC default role(s) and access controls.",
|
|
@@ -337,6 +327,7 @@ const en = {
|
|
|
337
327
|
"auditlog.table.ip": "IP",
|
|
338
328
|
"auditlog.table.details": "Details",
|
|
339
329
|
"auditlog.table.empty": "No audit log entries",
|
|
330
|
+
"auditlog.loading": "Loading…",
|
|
340
331
|
"auditlog.clear": "Clear Logs",
|
|
341
332
|
"auditlog.clear.title": "Clear All Logs",
|
|
342
333
|
"auditlog.clear.description": "This will permanently delete all {count, plural, one {# audit log entry} other {# audit log entries}}. This action cannot be undone.",
|
|
@@ -355,7 +346,9 @@ const en = {
|
|
|
355
346
|
"auditlog.action.whitelist_rejected": "The user's email address is not on the whitelist. Access was denied.",
|
|
356
347
|
"user.missing_code": "Authorisation code was not received from the OIDC provider.",
|
|
357
348
|
"user.invalid_state": "State parameter mismatch. Please restart the login flow.",
|
|
358
|
-
"user.signInError": "Authentication failed. Please try again."
|
|
349
|
+
"user.signInError": "Authentication failed. Please try again.",
|
|
350
|
+
"settings.section": "OIDC",
|
|
351
|
+
"settings.configuration": "Configuration"
|
|
359
352
|
};
|
|
360
353
|
const userFacingMessages = {
|
|
361
354
|
get missing_code() {
|
|
@@ -382,12 +375,11 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
382
375
|
];
|
|
383
376
|
function configValidation() {
|
|
384
377
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
385
|
-
|
|
378
|
+
const missing = REQUIRED_CONFIG_KEYS.filter((key) => !config2[key]);
|
|
379
|
+
if (missing.length === 0) {
|
|
386
380
|
return config2;
|
|
387
381
|
}
|
|
388
|
-
throw new Error(
|
|
389
|
-
`The following configuration keys are required: ${REQUIRED_CONFIG_KEYS.join(", ")}`
|
|
390
|
-
);
|
|
382
|
+
throw new Error(errorMessages.MISSING_CONFIG(missing.join(", ")));
|
|
391
383
|
}
|
|
392
384
|
async function oidcSignIn(ctx) {
|
|
393
385
|
const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPE, OIDC_AUTHORIZATION_ENDPOINT } = configValidation();
|
|
@@ -428,7 +420,7 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
|
|
|
428
420
|
}
|
|
429
421
|
});
|
|
430
422
|
if (!response.ok) {
|
|
431
|
-
throw new Error(
|
|
423
|
+
throw new Error(errorMessages.TOKEN_EXCHANGE_FAILED);
|
|
432
424
|
}
|
|
433
425
|
const tokenData = await response.json();
|
|
434
426
|
if (tokenData.id_token) {
|
|
@@ -436,18 +428,18 @@ async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
|
|
|
436
428
|
const payloadB64 = tokenData.id_token.split(".")[1];
|
|
437
429
|
const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
|
|
438
430
|
if (idTokenPayload.nonce !== expectedNonce) {
|
|
439
|
-
throw new Error(
|
|
431
|
+
throw new Error(errorMessages.NONCE_MISMATCH);
|
|
440
432
|
}
|
|
441
433
|
} catch (e) {
|
|
442
434
|
if (e.message === "Nonce mismatch") throw e;
|
|
443
|
-
throw new Error(
|
|
435
|
+
throw new Error(errorMessages.ID_TOKEN_PARSE_FAILED);
|
|
444
436
|
}
|
|
445
437
|
}
|
|
446
438
|
const userResponse = await fetch(config2.OIDC_USERINFO_ENDPOINT, {
|
|
447
439
|
headers: { Authorization: `Bearer ${tokenData.access_token}` }
|
|
448
440
|
});
|
|
449
441
|
if (!userResponse.ok) {
|
|
450
|
-
throw new Error(
|
|
442
|
+
throw new Error(errorMessages.USERINFO_FETCH_FAILED);
|
|
451
443
|
}
|
|
452
444
|
const userInfo = await userResponse.json();
|
|
453
445
|
return { userInfo, accessToken: tokenData.access_token };
|
|
@@ -476,9 +468,9 @@ function resolveRolesFromGroups(userInfo, config2, availableRoles) {
|
|
|
476
468
|
}
|
|
477
469
|
async function resolveRoles(userInfo, config2, roleService2, availableRoles) {
|
|
478
470
|
const groupRoles = resolveRolesFromGroups(userInfo, config2, availableRoles);
|
|
479
|
-
if (groupRoles.length > 0) return groupRoles;
|
|
471
|
+
if (groupRoles.length > 0) return { roles: groupRoles, fromGroupMapping: true };
|
|
480
472
|
const oidcRoles = await roleService2.oidcRoles();
|
|
481
|
-
return oidcRoles?.roles || [];
|
|
473
|
+
return { roles: oidcRoles?.roles || [], fromGroupMapping: false };
|
|
482
474
|
}
|
|
483
475
|
async function registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2) {
|
|
484
476
|
const defaultLocale = oauthService2.localeFindByHeader(
|
|
@@ -495,7 +487,11 @@ async function registerNewUser(oauthService2, email, userResponseData, config2,
|
|
|
495
487
|
return activateUser;
|
|
496
488
|
}
|
|
497
489
|
function rolesChanged(current, next) {
|
|
498
|
-
|
|
490
|
+
if (current.size !== next.size) return true;
|
|
491
|
+
for (const id of next) {
|
|
492
|
+
if (!current.has(id)) return true;
|
|
493
|
+
}
|
|
494
|
+
return false;
|
|
499
495
|
}
|
|
500
496
|
async function updateUserRoles(user, currentRoleIds, newRoleIds) {
|
|
501
497
|
try {
|
|
@@ -522,11 +518,16 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
522
518
|
const rawEmail = String(userResponseData.email ?? "");
|
|
523
519
|
const email = rawEmail.toLowerCase();
|
|
524
520
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
525
|
-
throw new Error(
|
|
521
|
+
throw new Error(errorMessages.INVALID_EMAIL);
|
|
526
522
|
}
|
|
527
523
|
await whitelistService2.checkWhitelistForEmail(email);
|
|
528
524
|
const allRoles = await strapi.db.query("admin::role").findMany();
|
|
529
|
-
const roles2 = await resolveRoles(
|
|
525
|
+
const { roles: roles2, fromGroupMapping } = await resolveRoles(
|
|
526
|
+
userResponseData,
|
|
527
|
+
config2,
|
|
528
|
+
roleService2,
|
|
529
|
+
allRoles
|
|
530
|
+
);
|
|
530
531
|
const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
|
|
531
532
|
let userCreated = false;
|
|
532
533
|
let rolesUpdated = false;
|
|
@@ -535,16 +536,11 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
535
536
|
user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
|
|
536
537
|
userCreated = true;
|
|
537
538
|
rolesUpdated = true;
|
|
538
|
-
} else if (roles2.length > 0) {
|
|
539
|
-
const defaultRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
539
|
+
} else if (fromGroupMapping && roles2.length > 0) {
|
|
540
540
|
const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if (isOnDefaultRoles) {
|
|
545
|
-
await updateUserRoles(user, currentRoleIds, roles2);
|
|
546
|
-
rolesUpdated = true;
|
|
547
|
-
}
|
|
541
|
+
if (rolesChanged(currentRoleIds, new Set(roles2))) {
|
|
542
|
+
await updateUserRoles(user, currentRoleIds, roles2);
|
|
543
|
+
rolesUpdated = true;
|
|
548
544
|
}
|
|
549
545
|
}
|
|
550
546
|
const jwtToken = await oauthService2.generateToken(user, ctx);
|
|
@@ -552,52 +548,39 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
552
548
|
return { activateUser: user, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
553
549
|
}
|
|
554
550
|
function classifyOidcError(msg, userInfo) {
|
|
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
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
},
|
|
589
|
-
{
|
|
590
|
-
test: (m) => m === "User creation failed" || m.includes("createUser"),
|
|
591
|
-
result: {
|
|
592
|
-
action: "login_failure",
|
|
593
|
-
code: errorCodes.USER_CREATION_FAILED,
|
|
594
|
-
key: "user_creation_failed",
|
|
595
|
-
params: userInfo?.email ? { email: userInfo.email, error: msg } : void 0
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
];
|
|
599
|
-
for (const { test, result } of errorMap) {
|
|
600
|
-
if (test(msg)) return result;
|
|
551
|
+
if (msg.includes("whitelist")) {
|
|
552
|
+
return {
|
|
553
|
+
action: "whitelist_rejected",
|
|
554
|
+
code: errorCodes.WHITELIST_CHECK_FAILED,
|
|
555
|
+
key: "whitelist_rejected"
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
if (msg === "Nonce mismatch")
|
|
559
|
+
return { action: "nonce_mismatch", code: errorCodes.NONCE_MISMATCH };
|
|
560
|
+
if (msg === "Token exchange failed")
|
|
561
|
+
return { action: "token_exchange_failed", code: errorCodes.TOKEN_EXCHANGE_FAILED };
|
|
562
|
+
if (msg === "Failed to fetch user info") {
|
|
563
|
+
return {
|
|
564
|
+
action: "login_failure",
|
|
565
|
+
code: errorCodes.USERINFO_FETCH_FAILED,
|
|
566
|
+
key: "userinfo_fetch_failed"
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
if (msg === "Failed to parse ID token") {
|
|
570
|
+
return {
|
|
571
|
+
action: "login_failure",
|
|
572
|
+
code: errorCodes.ID_TOKEN_PARSE_FAILED,
|
|
573
|
+
key: "id_token_parse_failed",
|
|
574
|
+
params: { error: msg }
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
if (msg === "User creation failed" || msg.includes("createUser")) {
|
|
578
|
+
return {
|
|
579
|
+
action: "login_failure",
|
|
580
|
+
code: errorCodes.USER_CREATION_FAILED,
|
|
581
|
+
key: "user_creation_failed",
|
|
582
|
+
params: userInfo?.email ? { email: userInfo.email, error: msg } : void 0
|
|
583
|
+
};
|
|
601
584
|
}
|
|
602
585
|
return {
|
|
603
586
|
action: "login_failure",
|
|
@@ -830,13 +813,9 @@ async function register(ctx) {
|
|
|
830
813
|
const whitelistService2 = getWhitelistService();
|
|
831
814
|
let matchedExistingUsersCount = 0;
|
|
832
815
|
for (const singleEmail of emailList) {
|
|
833
|
-
const existingUser = await strapi.query("admin::user").findOne({
|
|
834
|
-
where: { email: singleEmail }
|
|
835
|
-
});
|
|
816
|
+
const existingUser = await strapi.query("admin::user").findOne({ where: { email: singleEmail } });
|
|
836
817
|
if (existingUser) matchedExistingUsersCount++;
|
|
837
|
-
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({
|
|
838
|
-
where: { email: singleEmail }
|
|
839
|
-
});
|
|
818
|
+
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({ where: { email: singleEmail } });
|
|
840
819
|
if (!alreadyWhitelisted) {
|
|
841
820
|
await whitelistService2.registerUser(singleEmail);
|
|
842
821
|
}
|
|
@@ -978,9 +957,7 @@ function rateLimitMiddleware(ctx, next) {
|
|
|
978
957
|
const key = getRateLimitKey(ctx);
|
|
979
958
|
const now = Date.now();
|
|
980
959
|
const windowStart = now - RATE_LIMIT_WINDOW;
|
|
981
|
-
const requestStamps = (rateLimitMap.get(key)
|
|
982
|
-
(timestamp) => timestamp > windowStart
|
|
983
|
-
);
|
|
960
|
+
const requestStamps = (rateLimitMap.get(key) ?? []).filter((ts) => ts > windowStart);
|
|
984
961
|
if (requestStamps.length >= MAX_REQUESTS) {
|
|
985
962
|
ctx.status = 429;
|
|
986
963
|
ctx.body = "Too Many Requests";
|
|
@@ -1287,9 +1264,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1287
1264
|
const userService = strapi2.service("admin::user");
|
|
1288
1265
|
if (/[A-Z]/.test(email)) {
|
|
1289
1266
|
const dbUser = await userService.findOneByEmail(email.toLocaleLowerCase());
|
|
1290
|
-
if (dbUser)
|
|
1291
|
-
return dbUser;
|
|
1292
|
-
}
|
|
1267
|
+
if (dbUser) return dbUser;
|
|
1293
1268
|
}
|
|
1294
1269
|
const createdUser = await userService.create({
|
|
1295
1270
|
firstname: firstname || "unset",
|
|
@@ -1305,7 +1280,6 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1305
1280
|
lastname: lastname || "user",
|
|
1306
1281
|
password: generator__default.default.generate({
|
|
1307
1282
|
length: 43,
|
|
1308
|
-
// 256 bits (https://en.wikipedia.org/wiki/Password_strength#Random_passwords)
|
|
1309
1283
|
numbers: true,
|
|
1310
1284
|
lowercase: true,
|
|
1311
1285
|
uppercase: true,
|
|
@@ -1316,14 +1290,10 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1316
1290
|
});
|
|
1317
1291
|
},
|
|
1318
1292
|
addGmailAlias(baseEmail, baseAlias) {
|
|
1319
|
-
if (!baseAlias)
|
|
1320
|
-
return baseEmail;
|
|
1321
|
-
}
|
|
1293
|
+
if (!baseAlias) return baseEmail;
|
|
1322
1294
|
const alias = baseAlias.replace(/\+/g, "");
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
const domain = baseEmail.substring(beforePosition);
|
|
1326
|
-
return `${origin}+${alias}${domain}`;
|
|
1295
|
+
const atIndex = baseEmail.indexOf("@");
|
|
1296
|
+
return `${baseEmail.slice(0, atIndex)}+${alias}${baseEmail.slice(atIndex)}`;
|
|
1327
1297
|
},
|
|
1328
1298
|
localeFindByHeader(headers) {
|
|
1329
1299
|
return headers["accept-language"]?.includes("ja") ? "ja" : "en";
|
|
@@ -1404,9 +1374,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
1404
1374
|
async generateToken(user, ctx) {
|
|
1405
1375
|
const sessionManager = strapi2.sessionManager;
|
|
1406
1376
|
if (!sessionManager) {
|
|
1407
|
-
throw new Error(
|
|
1408
|
-
"sessionManager is not supported. Please upgrade to Strapi v5.24.1 or later."
|
|
1409
|
-
);
|
|
1377
|
+
throw new Error(errorMessages.SESSION_MANAGER_UNSUPPORTED);
|
|
1410
1378
|
}
|
|
1411
1379
|
const userId = String(user.id);
|
|
1412
1380
|
const deviceId = node_crypto.randomUUID();
|
|
@@ -1496,15 +1464,11 @@ function roleService({ strapi: strapi2 }) {
|
|
|
1496
1464
|
}
|
|
1497
1465
|
};
|
|
1498
1466
|
}
|
|
1467
|
+
const SETTINGS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1499
1468
|
function whitelistService({ strapi: strapi2 }) {
|
|
1500
|
-
const getPluginStore = () => strapi2.store({
|
|
1501
|
-
environment: "",
|
|
1502
|
-
type: "plugin",
|
|
1503
|
-
name: "strapi-plugin-oidc"
|
|
1504
|
-
});
|
|
1505
|
-
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
1506
1469
|
let settingsCache = null;
|
|
1507
|
-
const
|
|
1470
|
+
const getPluginStore = () => strapi2.store({ environment: "", type: "plugin", name: "strapi-plugin-oidc" });
|
|
1471
|
+
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
1508
1472
|
return {
|
|
1509
1473
|
async getSettings() {
|
|
1510
1474
|
const now = Date.now();
|
|
@@ -1513,10 +1477,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1513
1477
|
}
|
|
1514
1478
|
let settings = await getPluginStore().get({ key: "settings" });
|
|
1515
1479
|
if (!settings) {
|
|
1516
|
-
settings = {
|
|
1517
|
-
useWhitelist: true,
|
|
1518
|
-
enforceOIDC: false
|
|
1519
|
-
};
|
|
1480
|
+
settings = { useWhitelist: true, enforceOIDC: false };
|
|
1520
1481
|
await getPluginStore().set({ key: "settings", value: settings });
|
|
1521
1482
|
}
|
|
1522
1483
|
settingsCache = { value: settings, ts: now };
|
|
@@ -1530,26 +1491,18 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1530
1491
|
return getWhitelistQuery().findMany();
|
|
1531
1492
|
},
|
|
1532
1493
|
async registerUser(email) {
|
|
1533
|
-
await getWhitelistQuery().create({
|
|
1534
|
-
data: { email }
|
|
1535
|
-
});
|
|
1494
|
+
await getWhitelistQuery().create({ data: { email } });
|
|
1536
1495
|
},
|
|
1537
1496
|
async removeUser(email) {
|
|
1538
|
-
await getWhitelistQuery().deleteMany({
|
|
1539
|
-
where: { email }
|
|
1540
|
-
});
|
|
1497
|
+
await getWhitelistQuery().deleteMany({ where: { email } });
|
|
1541
1498
|
},
|
|
1542
1499
|
async checkWhitelistForEmail(email) {
|
|
1543
1500
|
const settings = await this.getSettings();
|
|
1544
|
-
if (!settings.useWhitelist)
|
|
1545
|
-
return null;
|
|
1546
|
-
}
|
|
1501
|
+
if (!settings.useWhitelist) return null;
|
|
1547
1502
|
const result = await getWhitelistQuery().findOne({
|
|
1548
1503
|
where: { email }
|
|
1549
1504
|
});
|
|
1550
|
-
if (!result)
|
|
1551
|
-
throw new Error("Not present in whitelist");
|
|
1552
|
-
}
|
|
1505
|
+
if (!result) throw new Error(errorMessages.WHITELIST_NOT_PRESENT);
|
|
1553
1506
|
return result;
|
|
1554
1507
|
}
|
|
1555
1508
|
};
|
|
@@ -1610,9 +1563,7 @@ function auditLogService({ strapi: strapi2 }) {
|
|
|
1610
1563
|
},
|
|
1611
1564
|
async cleanup(retentionDays) {
|
|
1612
1565
|
const cutoff = new Date(Date.now() - retentionDays * 864e5);
|
|
1613
|
-
await strapi2.db.query("plugin::strapi-plugin-oidc.audit-log").deleteMany({
|
|
1614
|
-
where: { createdAt: { $lt: cutoff } }
|
|
1615
|
-
});
|
|
1566
|
+
await strapi2.db.query("plugin::strapi-plugin-oidc.audit-log").deleteMany({ where: { createdAt: { $lt: cutoff } } });
|
|
1616
1567
|
}
|
|
1617
1568
|
};
|
|
1618
1569
|
}
|