strapi-plugin-oidc 1.3.2 → 1.4.1
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 +92 -44
- package/dist/admin/{index-BqyGGX8X.js → index-BnFRueNv.js} +163 -33
- package/dist/admin/{index-CFmg9Kxl.mjs → index-CY4s-vtv.mjs} +167 -37
- package/dist/admin/{index-Cse9ex24.js → index-RMgj1w0B.js} +15 -2
- package/dist/admin/{index-D1ypRUlq.mjs → index-ZRaWWFUL.mjs} +15 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +257 -219
- package/dist/server/index.mjs +257 -219
- package/package.json +11 -4
package/dist/server/index.mjs
CHANGED
|
@@ -158,7 +158,6 @@ const config = {
|
|
|
158
158
|
OIDC_AUTHORIZATION_ENDPOINT: "",
|
|
159
159
|
OIDC_TOKEN_ENDPOINT: "",
|
|
160
160
|
OIDC_USER_INFO_ENDPOINT: "",
|
|
161
|
-
OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER: false,
|
|
162
161
|
OIDC_GRANT_TYPE: "authorization_code",
|
|
163
162
|
OIDC_FAMILY_NAME_FIELD: "family_name",
|
|
164
163
|
OIDC_GIVEN_NAME_FIELD: "given_name",
|
|
@@ -217,45 +216,43 @@ function clearAuthCookies(strapi2, ctx) {
|
|
|
217
216
|
ctx.cookies.set("strapi_admin_refresh", "", options2);
|
|
218
217
|
ctx.cookies.set("oidc_authenticated", "", { ...options2, path: "/" });
|
|
219
218
|
}
|
|
219
|
+
const REQUIRED_CONFIG_KEYS = [
|
|
220
|
+
"OIDC_CLIENT_ID",
|
|
221
|
+
"OIDC_CLIENT_SECRET",
|
|
222
|
+
"OIDC_REDIRECT_URI",
|
|
223
|
+
"OIDC_SCOPES",
|
|
224
|
+
"OIDC_TOKEN_ENDPOINT",
|
|
225
|
+
"OIDC_USER_INFO_ENDPOINT",
|
|
226
|
+
"OIDC_GRANT_TYPE",
|
|
227
|
+
"OIDC_FAMILY_NAME_FIELD",
|
|
228
|
+
"OIDC_GIVEN_NAME_FIELD",
|
|
229
|
+
"OIDC_AUTHORIZATION_ENDPOINT"
|
|
230
|
+
];
|
|
220
231
|
function configValidation() {
|
|
221
232
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
222
|
-
|
|
223
|
-
"OIDC_CLIENT_ID",
|
|
224
|
-
"OIDC_CLIENT_SECRET",
|
|
225
|
-
"OIDC_REDIRECT_URI",
|
|
226
|
-
"OIDC_SCOPES",
|
|
227
|
-
"OIDC_TOKEN_ENDPOINT",
|
|
228
|
-
"OIDC_USER_INFO_ENDPOINT",
|
|
229
|
-
"OIDC_GRANT_TYPE",
|
|
230
|
-
"OIDC_FAMILY_NAME_FIELD",
|
|
231
|
-
"OIDC_GIVEN_NAME_FIELD",
|
|
232
|
-
"OIDC_AUTHORIZATION_ENDPOINT"
|
|
233
|
-
];
|
|
234
|
-
if (requiredKeys.every((key) => config2[key])) {
|
|
233
|
+
if (REQUIRED_CONFIG_KEYS.every((key) => config2[key])) {
|
|
235
234
|
return config2;
|
|
236
235
|
}
|
|
237
|
-
throw new Error(
|
|
236
|
+
throw new Error(
|
|
237
|
+
`The following configuration keys are required: ${REQUIRED_CONFIG_KEYS.join(", ")}`
|
|
238
|
+
);
|
|
238
239
|
}
|
|
239
240
|
async function oidcSignIn(ctx) {
|
|
240
|
-
let { state } = ctx.query;
|
|
241
241
|
const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPES, OIDC_AUTHORIZATION_ENDPOINT } = configValidation();
|
|
242
242
|
const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge();
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
243
|
+
const state = randomBytes(32).toString("base64url");
|
|
244
|
+
const nonce = randomBytes(32).toString("base64url");
|
|
246
245
|
const isProduction = strapi.config.get("environment") === "production";
|
|
247
|
-
|
|
246
|
+
const cookieOptions = {
|
|
248
247
|
httpOnly: true,
|
|
249
248
|
maxAge: 6e5,
|
|
249
|
+
// 10 minutes
|
|
250
250
|
secure: isProduction && ctx.request.secure,
|
|
251
251
|
sameSite: "lax"
|
|
252
|
-
}
|
|
253
|
-
ctx.cookies.set("
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
secure: isProduction && ctx.request.secure,
|
|
257
|
-
sameSite: "lax"
|
|
258
|
-
});
|
|
252
|
+
};
|
|
253
|
+
ctx.cookies.set("oidc_code_verifier", codeVerifier, cookieOptions);
|
|
254
|
+
ctx.cookies.set("oidc_state", state, cookieOptions);
|
|
255
|
+
ctx.cookies.set("oidc_nonce", nonce, cookieOptions);
|
|
259
256
|
const params = new URLSearchParams();
|
|
260
257
|
params.append("response_type", "code");
|
|
261
258
|
params.append("client_id", OIDC_CLIENT_ID);
|
|
@@ -264,11 +261,12 @@ async function oidcSignIn(ctx) {
|
|
|
264
261
|
params.append("code_challenge", codeChallenge);
|
|
265
262
|
params.append("code_challenge_method", "S256");
|
|
266
263
|
params.append("state", state);
|
|
264
|
+
params.append("nonce", nonce);
|
|
267
265
|
const authorizationUrl = `${OIDC_AUTHORIZATION_ENDPOINT}?${params.toString()}`;
|
|
268
266
|
ctx.set("Location", authorizationUrl);
|
|
269
267
|
return ctx.send({}, 302);
|
|
270
268
|
}
|
|
271
|
-
async function exchangeTokenAndFetchUserInfo(config2, params) {
|
|
269
|
+
async function exchangeTokenAndFetchUserInfo(config2, params, expectedNonce) {
|
|
272
270
|
const response = await fetch(config2.OIDC_TOKEN_ENDPOINT, {
|
|
273
271
|
method: "POST",
|
|
274
272
|
body: params,
|
|
@@ -277,31 +275,28 @@ async function exchangeTokenAndFetchUserInfo(config2, params) {
|
|
|
277
275
|
}
|
|
278
276
|
});
|
|
279
277
|
if (!response.ok) {
|
|
280
|
-
|
|
281
|
-
throw new Error(
|
|
282
|
-
`Failed to exchange token: ${response.status} ${response.statusText} - ${errText}`
|
|
283
|
-
);
|
|
278
|
+
throw new Error("Token exchange failed");
|
|
284
279
|
}
|
|
285
280
|
const tokenData = await response.json();
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
281
|
+
if (tokenData.id_token) {
|
|
282
|
+
try {
|
|
283
|
+
const payloadB64 = tokenData.id_token.split(".")[1];
|
|
284
|
+
const idTokenPayload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
|
|
285
|
+
if (idTokenPayload.nonce !== expectedNonce) {
|
|
286
|
+
throw new Error("Nonce mismatch");
|
|
287
|
+
}
|
|
288
|
+
} catch (e) {
|
|
289
|
+
if (e.message === "Nonce mismatch") throw e;
|
|
290
|
+
throw new Error("Failed to parse ID token");
|
|
291
|
+
}
|
|
293
292
|
}
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
headers: userInfoEndpointHeaders
|
|
293
|
+
const userResponse = await fetch(config2.OIDC_USER_INFO_ENDPOINT, {
|
|
294
|
+
headers: { Authorization: `Bearer ${tokenData.access_token}` }
|
|
297
295
|
});
|
|
298
296
|
if (!userResponse.ok) {
|
|
299
|
-
|
|
300
|
-
throw new Error(
|
|
301
|
-
`Failed to fetch user info: ${userResponse.status} ${userResponse.statusText} - ${errText}`
|
|
302
|
-
);
|
|
297
|
+
throw new Error("Failed to fetch user info");
|
|
303
298
|
}
|
|
304
|
-
return
|
|
299
|
+
return userResponse.json();
|
|
305
300
|
}
|
|
306
301
|
async function registerNewUser(userService, oauthService2, roleService2, email, userResponseData, whitelistUser, config2, ctx) {
|
|
307
302
|
let roles2 = [];
|
|
@@ -325,22 +320,16 @@ async function registerNewUser(userService, oauthService2, roleService2, email,
|
|
|
325
320
|
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
326
321
|
const email = String(userResponseData.email).toLowerCase();
|
|
327
322
|
const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
userResponseData,
|
|
339
|
-
whitelistUser,
|
|
340
|
-
config2,
|
|
341
|
-
ctx
|
|
342
|
-
);
|
|
343
|
-
}
|
|
323
|
+
const activateUser = await userService.findOneByEmail(email) ?? await registerNewUser(
|
|
324
|
+
userService,
|
|
325
|
+
oauthService2,
|
|
326
|
+
roleService2,
|
|
327
|
+
email,
|
|
328
|
+
userResponseData,
|
|
329
|
+
whitelistUser,
|
|
330
|
+
config2,
|
|
331
|
+
ctx
|
|
332
|
+
);
|
|
344
333
|
const jwtToken = await oauthService2.generateToken(activateUser, ctx);
|
|
345
334
|
oauthService2.triggerSignInSuccess(activateUser);
|
|
346
335
|
return { activateUser, jwtToken };
|
|
@@ -356,8 +345,10 @@ async function oidcSignInCallback(ctx) {
|
|
|
356
345
|
}
|
|
357
346
|
const oidcState = ctx.cookies.get("oidc_state");
|
|
358
347
|
const codeVerifier = ctx.cookies.get("oidc_code_verifier");
|
|
348
|
+
const oidcNonce = ctx.cookies.get("oidc_nonce");
|
|
359
349
|
ctx.cookies.set("oidc_state", null);
|
|
360
350
|
ctx.cookies.set("oidc_code_verifier", null);
|
|
351
|
+
ctx.cookies.set("oidc_nonce", null);
|
|
361
352
|
if (!ctx.query.state || ctx.query.state !== oidcState) {
|
|
362
353
|
return ctx.send(oauthService2.renderSignUpError("Invalid state"));
|
|
363
354
|
}
|
|
@@ -369,7 +360,7 @@ async function oidcSignInCallback(ctx) {
|
|
|
369
360
|
params.append("grant_type", config2.OIDC_GRANT_TYPE);
|
|
370
361
|
params.append("code_verifier", codeVerifier);
|
|
371
362
|
try {
|
|
372
|
-
const userResponseData = await exchangeTokenAndFetchUserInfo(config2, params);
|
|
363
|
+
const userResponseData = await exchangeTokenAndFetchUserInfo(config2, params, oidcNonce);
|
|
373
364
|
const { activateUser, jwtToken } = await handleUserAuthentication(
|
|
374
365
|
userService,
|
|
375
366
|
oauthService2,
|
|
@@ -385,7 +376,7 @@ async function oidcSignInCallback(ctx) {
|
|
|
385
376
|
ctx.send(html);
|
|
386
377
|
} catch (e) {
|
|
387
378
|
console.error("ERROR CAUGHT IN OIDC SIGNIN:", e);
|
|
388
|
-
ctx.send(oauthService2.renderSignUpError(
|
|
379
|
+
ctx.send(oauthService2.renderSignUpError("Authentication failed. Please try again."));
|
|
389
380
|
}
|
|
390
381
|
}
|
|
391
382
|
async function logout(ctx) {
|
|
@@ -432,8 +423,11 @@ const role = {
|
|
|
432
423
|
find,
|
|
433
424
|
update
|
|
434
425
|
};
|
|
426
|
+
function getWhitelistService() {
|
|
427
|
+
return strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
428
|
+
}
|
|
435
429
|
async function info(ctx) {
|
|
436
|
-
const whitelistService2 =
|
|
430
|
+
const whitelistService2 = getWhitelistService();
|
|
437
431
|
const settings = await whitelistService2.getSettings();
|
|
438
432
|
const whitelistUsers = await whitelistService2.getUsers();
|
|
439
433
|
ctx.body = {
|
|
@@ -444,8 +438,9 @@ async function info(ctx) {
|
|
|
444
438
|
};
|
|
445
439
|
}
|
|
446
440
|
async function updateSettings(ctx) {
|
|
447
|
-
|
|
448
|
-
|
|
441
|
+
const { useWhitelist } = ctx.request.body;
|
|
442
|
+
let { enforceOIDC } = ctx.request.body;
|
|
443
|
+
const whitelistService2 = getWhitelistService();
|
|
449
444
|
if (useWhitelist && enforceOIDC) {
|
|
450
445
|
const users = await whitelistService2.getUsers();
|
|
451
446
|
if (users.length === 0) {
|
|
@@ -456,7 +451,7 @@ async function updateSettings(ctx) {
|
|
|
456
451
|
ctx.body = { useWhitelist, enforceOIDC };
|
|
457
452
|
}
|
|
458
453
|
async function publicSettings(ctx) {
|
|
459
|
-
const whitelistService2 =
|
|
454
|
+
const whitelistService2 = getWhitelistService();
|
|
460
455
|
const settings = await whitelistService2.getSettings();
|
|
461
456
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
462
457
|
ctx.body = {
|
|
@@ -476,10 +471,11 @@ async function register(ctx) {
|
|
|
476
471
|
where: { email: { $in: emailList } },
|
|
477
472
|
populate: ["roles"]
|
|
478
473
|
});
|
|
479
|
-
const
|
|
474
|
+
const existingUsersByEmail = new Map(existingUsers.map((u) => [u.email, u]));
|
|
475
|
+
const whitelistService2 = getWhitelistService();
|
|
480
476
|
let matchedExistingUsersCount = 0;
|
|
481
477
|
for (const singleEmail of emailList) {
|
|
482
|
-
const existingUser =
|
|
478
|
+
const existingUser = existingUsersByEmail.get(singleEmail);
|
|
483
479
|
let finalRoles = roles2;
|
|
484
480
|
if (existingUser?.roles) {
|
|
485
481
|
finalRoles = existingUser.roles.map((r) => String(r.id));
|
|
@@ -496,14 +492,56 @@ async function register(ctx) {
|
|
|
496
492
|
}
|
|
497
493
|
async function removeEmail(ctx) {
|
|
498
494
|
const { id } = ctx.params;
|
|
499
|
-
const whitelistService2 =
|
|
495
|
+
const whitelistService2 = getWhitelistService();
|
|
500
496
|
await whitelistService2.removeUser(id);
|
|
501
497
|
ctx.body = {};
|
|
502
498
|
}
|
|
499
|
+
async function deleteAll(ctx) {
|
|
500
|
+
await strapi.query("plugin::strapi-plugin-oidc.whitelists").deleteMany({});
|
|
501
|
+
ctx.body = {};
|
|
502
|
+
}
|
|
503
|
+
async function importUsers(ctx) {
|
|
504
|
+
const { users } = ctx.request.body;
|
|
505
|
+
if (!Array.isArray(users)) {
|
|
506
|
+
ctx.status = 400;
|
|
507
|
+
ctx.body = { error: "Expected { users: [{email, roles}] }" };
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const allRoles = await strapi.query("admin::role").findMany({});
|
|
511
|
+
const roleNameToId = new Map(allRoles.map((r) => [r.name, String(r.id)]));
|
|
512
|
+
const resolveRole = (nameOrId) => roleNameToId.get(nameOrId) ?? nameOrId;
|
|
513
|
+
const normalized = users.filter((u) => u?.email).map((u) => ({
|
|
514
|
+
email: String(u.email).trim().toLowerCase(),
|
|
515
|
+
roles: (Array.isArray(u.roles) ? u.roles : []).map(resolveRole)
|
|
516
|
+
}));
|
|
517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
518
|
+
const deduped = normalized.filter((u) => {
|
|
519
|
+
if (seen.has(u.email)) return false;
|
|
520
|
+
seen.add(u.email);
|
|
521
|
+
return true;
|
|
522
|
+
});
|
|
523
|
+
const strapiUsers = await strapi.query("admin::user").findMany({
|
|
524
|
+
where: { email: { $in: deduped.map((u) => u.email) } },
|
|
525
|
+
populate: ["roles"]
|
|
526
|
+
});
|
|
527
|
+
const strapiUserMap = new Map(strapiUsers.map((u) => [u.email, u]));
|
|
528
|
+
const whitelistService2 = getWhitelistService();
|
|
529
|
+
const existing = await whitelistService2.getUsers();
|
|
530
|
+
const existingEmails = new Set(existing.map((u) => u.email));
|
|
531
|
+
let importedCount = 0;
|
|
532
|
+
for (const user of deduped) {
|
|
533
|
+
if (existingEmails.has(user.email)) continue;
|
|
534
|
+
const strapiUser = strapiUserMap.get(user.email);
|
|
535
|
+
const finalRoles = strapiUser?.roles?.length ? strapiUser.roles.map((r) => String(r.id)) : user.roles;
|
|
536
|
+
await whitelistService2.registerUser(user.email, finalRoles);
|
|
537
|
+
importedCount++;
|
|
538
|
+
}
|
|
539
|
+
ctx.body = { importedCount };
|
|
540
|
+
}
|
|
503
541
|
async function syncUsers(ctx) {
|
|
504
|
-
|
|
505
|
-
users =
|
|
506
|
-
const whitelistService2 =
|
|
542
|
+
const { users: rawUsers } = ctx.request.body;
|
|
543
|
+
const users = rawUsers.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
|
|
544
|
+
const whitelistService2 = getWhitelistService();
|
|
507
545
|
const currentUsers = await whitelistService2.getUsers();
|
|
508
546
|
let matchedExistingUsersCount = 0;
|
|
509
547
|
const emailsToSync = users.map((u) => u.email);
|
|
@@ -511,26 +549,29 @@ async function syncUsers(ctx) {
|
|
|
511
549
|
where: { email: { $in: emailsToSync } },
|
|
512
550
|
populate: ["roles"]
|
|
513
551
|
});
|
|
552
|
+
const syncEmailSet = new Set(emailsToSync);
|
|
553
|
+
const currentUsersByEmail = new Map(currentUsers.map((u) => [u.email, u]));
|
|
554
|
+
const strapiUsersByEmail = new Map(existingStrapiUsers.map((u) => [u.email, u]));
|
|
514
555
|
for (const currUser of currentUsers) {
|
|
515
|
-
if (!
|
|
556
|
+
if (!syncEmailSet.has(currUser.email)) {
|
|
516
557
|
await whitelistService2.removeUser(currUser.id);
|
|
517
558
|
}
|
|
518
559
|
}
|
|
519
560
|
for (const user of users) {
|
|
520
|
-
const
|
|
561
|
+
const currUser = currentUsersByEmail.get(user.email);
|
|
521
562
|
let finalRoles = user.roles;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
563
|
+
if (!currUser) {
|
|
564
|
+
const existingStrapiUser = strapiUsersByEmail.get(user.email);
|
|
565
|
+
if (existingStrapiUser?.roles) {
|
|
566
|
+
finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
|
|
567
|
+
matchedExistingUsersCount++;
|
|
568
|
+
}
|
|
569
|
+
await whitelistService2.registerUser(user.email, finalRoles);
|
|
570
|
+
} else {
|
|
528
571
|
await strapi.query("plugin::strapi-plugin-oidc.whitelists").update({
|
|
529
572
|
where: { id: currUser.id },
|
|
530
573
|
data: { roles: finalRoles }
|
|
531
574
|
});
|
|
532
|
-
} else {
|
|
533
|
-
await whitelistService2.registerUser(user.email, finalRoles);
|
|
534
575
|
}
|
|
535
576
|
}
|
|
536
577
|
ctx.body = { matchedExistingUsersCount };
|
|
@@ -541,7 +582,9 @@ const whitelist = {
|
|
|
541
582
|
publicSettings,
|
|
542
583
|
register,
|
|
543
584
|
removeEmail,
|
|
544
|
-
|
|
585
|
+
deleteAll,
|
|
586
|
+
syncUsers,
|
|
587
|
+
importUsers
|
|
545
588
|
};
|
|
546
589
|
const controllers = {
|
|
547
590
|
oidc,
|
|
@@ -565,134 +608,133 @@ const rateLimitMiddleware = async (ctx, next) => {
|
|
|
565
608
|
rateLimitMap.set(ip, requestStamps);
|
|
566
609
|
await next();
|
|
567
610
|
};
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
policies: [
|
|
575
|
-
"admin::isAuthenticatedAdmin",
|
|
576
|
-
{ name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
|
|
577
|
-
]
|
|
578
|
-
}
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
method: "PUT",
|
|
582
|
-
path: "/oidc-roles",
|
|
583
|
-
handler: "role.update",
|
|
584
|
-
config: {
|
|
585
|
-
policies: [
|
|
586
|
-
"admin::isAuthenticatedAdmin",
|
|
587
|
-
{
|
|
588
|
-
name: "admin::hasPermissions",
|
|
589
|
-
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
590
|
-
}
|
|
591
|
-
]
|
|
592
|
-
}
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
method: "GET",
|
|
596
|
-
path: "/oidc",
|
|
597
|
-
handler: "oidc.oidcSignIn",
|
|
598
|
-
config: {
|
|
599
|
-
auth: false,
|
|
600
|
-
middlewares: [rateLimitMiddleware]
|
|
601
|
-
}
|
|
602
|
-
},
|
|
603
|
-
{
|
|
604
|
-
method: "GET",
|
|
605
|
-
path: "/oidc/callback",
|
|
606
|
-
handler: "oidc.oidcSignInCallback",
|
|
607
|
-
config: {
|
|
608
|
-
auth: false,
|
|
609
|
-
middlewares: [rateLimitMiddleware]
|
|
610
|
-
}
|
|
611
|
-
},
|
|
612
|
-
{
|
|
613
|
-
method: "GET",
|
|
614
|
-
path: "/logout",
|
|
615
|
-
handler: "oidc.logout",
|
|
616
|
-
config: {
|
|
617
|
-
auth: false
|
|
618
|
-
}
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
method: "GET",
|
|
622
|
-
path: "/whitelist",
|
|
623
|
-
handler: "whitelist.info",
|
|
624
|
-
config: {
|
|
625
|
-
policies: [
|
|
626
|
-
"admin::isAuthenticatedAdmin",
|
|
627
|
-
{ name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
|
|
628
|
-
]
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
{
|
|
632
|
-
method: "PUT",
|
|
633
|
-
path: "/whitelist/settings",
|
|
634
|
-
handler: "whitelist.updateSettings",
|
|
635
|
-
config: {
|
|
636
|
-
policies: [
|
|
637
|
-
"admin::isAuthenticatedAdmin",
|
|
638
|
-
{
|
|
639
|
-
name: "admin::hasPermissions",
|
|
640
|
-
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
641
|
-
}
|
|
642
|
-
]
|
|
643
|
-
}
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
method: "GET",
|
|
647
|
-
path: "/settings/public",
|
|
648
|
-
handler: "whitelist.publicSettings",
|
|
649
|
-
config: {
|
|
650
|
-
auth: false
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
{
|
|
654
|
-
method: "PUT",
|
|
655
|
-
path: "/whitelist/sync",
|
|
656
|
-
handler: "whitelist.syncUsers",
|
|
657
|
-
config: {
|
|
658
|
-
policies: [
|
|
659
|
-
"admin::isAuthenticatedAdmin",
|
|
660
|
-
{
|
|
661
|
-
name: "admin::hasPermissions",
|
|
662
|
-
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
663
|
-
}
|
|
664
|
-
]
|
|
665
|
-
}
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
method: "POST",
|
|
669
|
-
path: "/whitelist",
|
|
670
|
-
handler: "whitelist.register",
|
|
671
|
-
config: {
|
|
672
|
-
policies: [
|
|
673
|
-
"admin::isAuthenticatedAdmin",
|
|
674
|
-
{
|
|
675
|
-
name: "admin::hasPermissions",
|
|
676
|
-
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
677
|
-
}
|
|
678
|
-
]
|
|
611
|
+
const adminPolicies = (action) => ({
|
|
612
|
+
policies: [
|
|
613
|
+
"admin::isAuthenticatedAdmin",
|
|
614
|
+
{
|
|
615
|
+
name: "admin::hasPermissions",
|
|
616
|
+
config: { actions: [`plugin::strapi-plugin-oidc.${action}`] }
|
|
679
617
|
}
|
|
618
|
+
]
|
|
619
|
+
});
|
|
620
|
+
const routes = {
|
|
621
|
+
admin: {
|
|
622
|
+
type: "admin",
|
|
623
|
+
routes: [
|
|
624
|
+
{
|
|
625
|
+
method: "GET",
|
|
626
|
+
path: "/oidc-roles",
|
|
627
|
+
handler: "role.find",
|
|
628
|
+
config: adminPolicies("read")
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
method: "PUT",
|
|
632
|
+
path: "/oidc-roles",
|
|
633
|
+
handler: "role.update",
|
|
634
|
+
config: adminPolicies("update")
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
method: "GET",
|
|
638
|
+
path: "/oidc",
|
|
639
|
+
handler: "oidc.oidcSignIn",
|
|
640
|
+
config: { auth: false, middlewares: [rateLimitMiddleware] }
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
method: "GET",
|
|
644
|
+
path: "/oidc/callback",
|
|
645
|
+
handler: "oidc.oidcSignInCallback",
|
|
646
|
+
config: { auth: false, middlewares: [rateLimitMiddleware] }
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
method: "GET",
|
|
650
|
+
path: "/logout",
|
|
651
|
+
handler: "oidc.logout",
|
|
652
|
+
config: { auth: false }
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
method: "GET",
|
|
656
|
+
path: "/whitelist",
|
|
657
|
+
handler: "whitelist.info",
|
|
658
|
+
config: adminPolicies("read")
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
method: "PUT",
|
|
662
|
+
path: "/whitelist/settings",
|
|
663
|
+
handler: "whitelist.updateSettings",
|
|
664
|
+
config: adminPolicies("update")
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
method: "GET",
|
|
668
|
+
path: "/settings/public",
|
|
669
|
+
handler: "whitelist.publicSettings",
|
|
670
|
+
config: { auth: false }
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
method: "PUT",
|
|
674
|
+
path: "/whitelist/sync",
|
|
675
|
+
handler: "whitelist.syncUsers",
|
|
676
|
+
config: adminPolicies("update")
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
method: "POST",
|
|
680
|
+
path: "/whitelist/import",
|
|
681
|
+
handler: "whitelist.importUsers",
|
|
682
|
+
config: adminPolicies("update")
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
method: "POST",
|
|
686
|
+
path: "/whitelist",
|
|
687
|
+
handler: "whitelist.register",
|
|
688
|
+
config: adminPolicies("update")
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
method: "DELETE",
|
|
692
|
+
path: "/whitelist/:id",
|
|
693
|
+
handler: "whitelist.removeEmail",
|
|
694
|
+
config: adminPolicies("update")
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
method: "DELETE",
|
|
698
|
+
path: "/whitelist",
|
|
699
|
+
handler: "whitelist.deleteAll",
|
|
700
|
+
config: adminPolicies("update")
|
|
701
|
+
}
|
|
702
|
+
]
|
|
680
703
|
},
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
704
|
+
// API-token-authenticated routes for programmatic whitelist management.
|
|
705
|
+
// Accessible at /strapi-plugin-oidc/... using a Strapi API token
|
|
706
|
+
// (full-access or custom) in the Authorization: Bearer <token> header.
|
|
707
|
+
"content-api": {
|
|
708
|
+
type: "content-api",
|
|
709
|
+
routes: [
|
|
710
|
+
{
|
|
711
|
+
method: "GET",
|
|
712
|
+
path: "/whitelist",
|
|
713
|
+
handler: "whitelist.info"
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
method: "POST",
|
|
717
|
+
path: "/whitelist",
|
|
718
|
+
handler: "whitelist.register"
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
method: "POST",
|
|
722
|
+
path: "/whitelist/import",
|
|
723
|
+
handler: "whitelist.importUsers"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
method: "DELETE",
|
|
727
|
+
path: "/whitelist/:id",
|
|
728
|
+
handler: "whitelist.removeEmail"
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
method: "DELETE",
|
|
732
|
+
path: "/whitelist",
|
|
733
|
+
handler: "whitelist.deleteAll"
|
|
734
|
+
}
|
|
735
|
+
]
|
|
694
736
|
}
|
|
695
|
-
|
|
737
|
+
};
|
|
696
738
|
const policies = {};
|
|
697
739
|
function renderHtmlTemplate(title, content) {
|
|
698
740
|
return `
|
|
@@ -828,7 +870,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
828
870
|
roles: roles2,
|
|
829
871
|
preferedLanguage: locale
|
|
830
872
|
});
|
|
831
|
-
return
|
|
873
|
+
return userService.register({
|
|
832
874
|
registrationToken: createdUser.registrationToken,
|
|
833
875
|
userInfo: {
|
|
834
876
|
firstname: firstname || "unset",
|
|
@@ -849,7 +891,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
849
891
|
if (!baseAlias) {
|
|
850
892
|
return baseEmail;
|
|
851
893
|
}
|
|
852
|
-
const alias = baseAlias.replace(
|
|
894
|
+
const alias = baseAlias.replace(/\+/g, "");
|
|
853
895
|
const beforePosition = baseEmail.indexOf("@");
|
|
854
896
|
const origin = baseEmail.substring(0, beforePosition);
|
|
855
897
|
const domain = baseEmail.substring(beforePosition);
|
|
@@ -886,11 +928,9 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
886
928
|
provider: "strapi-plugin-oidc"
|
|
887
929
|
});
|
|
888
930
|
},
|
|
889
|
-
// Sign In Success
|
|
890
931
|
renderSignUpSuccess(jwtToken, user, nonce) {
|
|
891
932
|
const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
|
|
892
|
-
const
|
|
893
|
-
const isRememberMe = !!REMEMBER_ME;
|
|
933
|
+
const isRememberMe = !!config2["REMEMBER_ME"];
|
|
894
934
|
const content = `
|
|
895
935
|
<noscript>
|
|
896
936
|
<div class="card">
|
|
@@ -916,7 +956,6 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
916
956
|
<\/script>`;
|
|
917
957
|
return renderHtmlTemplate("Authenticating...", content);
|
|
918
958
|
},
|
|
919
|
-
// Sign In Error
|
|
920
959
|
renderSignUpError(message) {
|
|
921
960
|
const safeMessage = String(message).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
922
961
|
const content = `
|
|
@@ -944,8 +983,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
944
983
|
const userId = String(user.id);
|
|
945
984
|
const deviceId = randomUUID();
|
|
946
985
|
const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
|
|
947
|
-
const
|
|
948
|
-
const rememberMe = !!REMEMBER_ME;
|
|
986
|
+
const rememberMe = !!config2["REMEMBER_ME"];
|
|
949
987
|
const { token: refreshToken, absoluteExpiresAt } = await sessionManager(
|
|
950
988
|
"admin"
|
|
951
989
|
).generateRefreshToken(userId, deviceId, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-oidc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
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",
|
|
@@ -26,10 +26,17 @@
|
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"strapi",
|
|
29
|
-
"plugin",
|
|
29
|
+
"strapi-plugin",
|
|
30
|
+
"oidc",
|
|
30
31
|
"oauth",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
32
|
+
"sso",
|
|
33
|
+
"authentication",
|
|
34
|
+
"keycloak",
|
|
35
|
+
"auth0",
|
|
36
|
+
"okta",
|
|
37
|
+
"azure-ad",
|
|
38
|
+
"authentik",
|
|
39
|
+
"authelia"
|
|
33
40
|
],
|
|
34
41
|
"peerDependencies": {
|
|
35
42
|
"@strapi/strapi": "^5.24.1",
|