strapi-plugin-oidc 1.0.4 → 1.0.6
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 +16 -2
- package/dist/admin/{index-C0GkDnGG.js → index-BITZIRCD.js} +12 -18
- package/dist/admin/{index-D_0jCOLk.js → index-CLDWnBI9.js} +191 -113
- package/dist/admin/{index-BuuCScSN.mjs → index-YOG9buUz.mjs} +12 -18
- package/dist/admin/{index-DwpTg1-J.mjs → index-p9ncVp1G.mjs} +192 -114
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +340 -149
- package/dist/server/index.mjs +340 -149
- package/package.json +3 -2
package/dist/server/index.js
CHANGED
|
@@ -125,10 +125,22 @@ const contentTypes = {
|
|
|
125
125
|
};
|
|
126
126
|
function configValidation() {
|
|
127
127
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
128
|
-
|
|
128
|
+
const requiredKeys = [
|
|
129
|
+
"OIDC_CLIENT_ID",
|
|
130
|
+
"OIDC_CLIENT_SECRET",
|
|
131
|
+
"OIDC_REDIRECT_URI",
|
|
132
|
+
"OIDC_SCOPES",
|
|
133
|
+
"OIDC_TOKEN_ENDPOINT",
|
|
134
|
+
"OIDC_USER_INFO_ENDPOINT",
|
|
135
|
+
"OIDC_GRANT_TYPE",
|
|
136
|
+
"OIDC_FAMILY_NAME_FIELD",
|
|
137
|
+
"OIDC_GIVEN_NAME_FIELD",
|
|
138
|
+
"OIDC_AUTHORIZATION_ENDPOINT"
|
|
139
|
+
];
|
|
140
|
+
if (requiredKeys.every((key) => config2[key])) {
|
|
129
141
|
return config2;
|
|
130
142
|
}
|
|
131
|
-
throw new Error(
|
|
143
|
+
throw new Error(`The following configuration keys are required: ${requiredKeys.join(", ")}`);
|
|
132
144
|
}
|
|
133
145
|
async function oidcSignIn(ctx) {
|
|
134
146
|
let { state } = ctx.query;
|
|
@@ -151,6 +163,66 @@ async function oidcSignIn(ctx) {
|
|
|
151
163
|
ctx.set("Location", authorizationUrl);
|
|
152
164
|
return ctx.send({}, 302);
|
|
153
165
|
}
|
|
166
|
+
async function exchangeTokenAndFetchUserInfo(httpClient, config2, params) {
|
|
167
|
+
const response = await httpClient.post(config2.OIDC_TOKEN_ENDPOINT, params, {
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
let userInfoEndpointHeaders = {};
|
|
173
|
+
let userInfoEndpointParameters = `?access_token=${response.data.access_token}`;
|
|
174
|
+
if (config2.OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER) {
|
|
175
|
+
userInfoEndpointHeaders = {
|
|
176
|
+
headers: { Authorization: `Bearer ${response.data.access_token}` }
|
|
177
|
+
};
|
|
178
|
+
userInfoEndpointParameters = "";
|
|
179
|
+
}
|
|
180
|
+
const userInfoEndpoint = `${config2.OIDC_USER_INFO_ENDPOINT}${userInfoEndpointParameters}`;
|
|
181
|
+
const userResponse = await httpClient.get(userInfoEndpoint, userInfoEndpointHeaders);
|
|
182
|
+
return userResponse.data;
|
|
183
|
+
}
|
|
184
|
+
async function registerNewUser(userService, oauthService2, roleService2, email, userResponseData, whitelistUser, config2, ctx) {
|
|
185
|
+
let roles2 = [];
|
|
186
|
+
if (whitelistUser?.roles?.length > 0) {
|
|
187
|
+
roles2 = whitelistUser.roles;
|
|
188
|
+
} else {
|
|
189
|
+
const oidcRoles = await roleService2.oidcRoles();
|
|
190
|
+
roles2 = oidcRoles?.roles || [];
|
|
191
|
+
}
|
|
192
|
+
const defaultLocale = oauthService2.localeFindByHeader(ctx.request.headers);
|
|
193
|
+
const activateUser = await oauthService2.createUser(
|
|
194
|
+
email,
|
|
195
|
+
userResponseData[config2.OIDC_FAMILY_NAME_FIELD],
|
|
196
|
+
userResponseData[config2.OIDC_GIVEN_NAME_FIELD],
|
|
197
|
+
defaultLocale,
|
|
198
|
+
roles2
|
|
199
|
+
);
|
|
200
|
+
await oauthService2.triggerWebHook(activateUser);
|
|
201
|
+
return activateUser;
|
|
202
|
+
}
|
|
203
|
+
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
204
|
+
const email = String(userResponseData.email).toLowerCase();
|
|
205
|
+
const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
|
|
206
|
+
const dbUser = await userService.findOneByEmail(email);
|
|
207
|
+
let activateUser;
|
|
208
|
+
if (dbUser) {
|
|
209
|
+
activateUser = dbUser;
|
|
210
|
+
} else {
|
|
211
|
+
activateUser = await registerNewUser(
|
|
212
|
+
userService,
|
|
213
|
+
oauthService2,
|
|
214
|
+
roleService2,
|
|
215
|
+
email,
|
|
216
|
+
userResponseData,
|
|
217
|
+
whitelistUser,
|
|
218
|
+
config2,
|
|
219
|
+
ctx
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const jwtToken = await oauthService2.generateToken(activateUser, ctx);
|
|
223
|
+
oauthService2.triggerSignInSuccess(activateUser);
|
|
224
|
+
return { activateUser, jwtToken };
|
|
225
|
+
}
|
|
154
226
|
async function oidcSignInCallback(ctx) {
|
|
155
227
|
const config2 = configValidation();
|
|
156
228
|
const httpClient = axios__default.default.create();
|
|
@@ -159,77 +231,41 @@ async function oidcSignInCallback(ctx) {
|
|
|
159
231
|
const roleService2 = strapi.plugin("strapi-plugin-oidc").service("role");
|
|
160
232
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
161
233
|
if (!ctx.query.code) {
|
|
162
|
-
return ctx.send(oauthService2.renderSignUpError(
|
|
234
|
+
return ctx.send(oauthService2.renderSignUpError("code Not Found"));
|
|
163
235
|
}
|
|
164
236
|
if (!ctx.query.state || ctx.query.state !== ctx.session.oidcState) {
|
|
165
|
-
return ctx.send(oauthService2.renderSignUpError(
|
|
237
|
+
return ctx.send(oauthService2.renderSignUpError("Invalid state"));
|
|
166
238
|
}
|
|
167
239
|
const params = new URLSearchParams();
|
|
168
240
|
params.append("code", ctx.query.code);
|
|
169
|
-
params.append("client_id", config2
|
|
170
|
-
params.append("client_secret", config2
|
|
171
|
-
params.append("redirect_uri", config2
|
|
172
|
-
params.append("grant_type", config2
|
|
241
|
+
params.append("client_id", config2.OIDC_CLIENT_ID);
|
|
242
|
+
params.append("client_secret", config2.OIDC_CLIENT_SECRET);
|
|
243
|
+
params.append("redirect_uri", config2.OIDC_REDIRECT_URI);
|
|
244
|
+
params.append("grant_type", config2.OIDC_GRANT_TYPE);
|
|
173
245
|
params.append("code_verifier", ctx.session.codeVerifier);
|
|
174
246
|
try {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
headers: { Authorization: `Bearer ${response.data.access_token}` }
|
|
185
|
-
};
|
|
186
|
-
userInfoEndpointParameters = "";
|
|
187
|
-
}
|
|
188
|
-
const userInfoEndpoint = `${config2["OIDC_USER_INFO_ENDPOINT"]}${userInfoEndpointParameters}`;
|
|
189
|
-
const userResponse = await httpClient.get(
|
|
190
|
-
userInfoEndpoint,
|
|
191
|
-
userInfoEndpointHeaders
|
|
247
|
+
const userResponseData = await exchangeTokenAndFetchUserInfo(httpClient, config2, params);
|
|
248
|
+
const { activateUser, jwtToken } = await handleUserAuthentication(
|
|
249
|
+
userService,
|
|
250
|
+
oauthService2,
|
|
251
|
+
roleService2,
|
|
252
|
+
whitelistService2,
|
|
253
|
+
userResponseData,
|
|
254
|
+
config2,
|
|
255
|
+
ctx
|
|
192
256
|
);
|
|
193
|
-
const email = userResponse.data.email;
|
|
194
|
-
const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
|
|
195
|
-
const dbUser = await userService.findOneByEmail(email);
|
|
196
|
-
let activateUser;
|
|
197
|
-
let jwtToken;
|
|
198
|
-
if (dbUser) {
|
|
199
|
-
activateUser = dbUser;
|
|
200
|
-
jwtToken = await oauthService2.generateToken(dbUser, ctx);
|
|
201
|
-
} else {
|
|
202
|
-
let roles2 = [];
|
|
203
|
-
if (whitelistUser && whitelistUser.roles && whitelistUser.roles.length > 0) {
|
|
204
|
-
roles2 = whitelistUser.roles;
|
|
205
|
-
} else {
|
|
206
|
-
const oidcRoles = await roleService2.oidcRoles();
|
|
207
|
-
roles2 = oidcRoles && oidcRoles["roles"] ? oidcRoles["roles"] : [];
|
|
208
|
-
}
|
|
209
|
-
const defaultLocale = oauthService2.localeFindByHeader(ctx.request.headers);
|
|
210
|
-
activateUser = await oauthService2.createUser(
|
|
211
|
-
email,
|
|
212
|
-
userResponse.data[config2["OIDC_FAMILY_NAME_FIELD"]],
|
|
213
|
-
userResponse.data[config2["OIDC_GIVEN_NAME_FIELD"]],
|
|
214
|
-
defaultLocale,
|
|
215
|
-
roles2
|
|
216
|
-
);
|
|
217
|
-
jwtToken = await oauthService2.generateToken(activateUser, ctx);
|
|
218
|
-
await oauthService2.triggerWebHook(activateUser);
|
|
219
|
-
}
|
|
220
|
-
oauthService2.triggerSignInSuccess(activateUser);
|
|
221
257
|
const nonce = node_crypto.randomUUID();
|
|
222
258
|
const html = oauthService2.renderSignUpSuccess(jwtToken, activateUser, nonce);
|
|
223
259
|
ctx.set("Content-Security-Policy", `script-src 'nonce-${nonce}'`);
|
|
224
260
|
ctx.send(html);
|
|
225
261
|
} catch (e) {
|
|
226
|
-
console.error(e);
|
|
262
|
+
console.error("ERROR CAUGHT IN OIDC SIGNIN:", e);
|
|
227
263
|
ctx.send(oauthService2.renderSignUpError(e.message));
|
|
228
264
|
}
|
|
229
265
|
}
|
|
230
266
|
async function logout(ctx) {
|
|
231
267
|
const config2 = strapi.config.get("plugin::strapi-plugin-oidc");
|
|
232
|
-
const logoutUrl = config2
|
|
268
|
+
const logoutUrl = config2.OIDC_LOGOUT_URL;
|
|
233
269
|
if (logoutUrl) {
|
|
234
270
|
ctx.redirect(logoutUrl);
|
|
235
271
|
} else {
|
|
@@ -247,10 +283,9 @@ async function find(ctx) {
|
|
|
247
283
|
const roles2 = await roleService2.find();
|
|
248
284
|
const oidcConstants = roleService2.getOidcRoles();
|
|
249
285
|
for (const oidc2 of oidcConstants) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
286
|
+
const matchedRole = roles2.find((r) => r.oauth_type === oidc2.oauth_type);
|
|
287
|
+
if (matchedRole) {
|
|
288
|
+
oidc2.role = matchedRole.roles;
|
|
254
289
|
}
|
|
255
290
|
}
|
|
256
291
|
ctx.send(oidcConstants);
|
|
@@ -262,7 +297,7 @@ async function update(ctx) {
|
|
|
262
297
|
await roleService2.update(roles2);
|
|
263
298
|
ctx.send({}, 204);
|
|
264
299
|
} catch (e) {
|
|
265
|
-
console.
|
|
300
|
+
console.error(e);
|
|
266
301
|
ctx.send({}, 400);
|
|
267
302
|
}
|
|
268
303
|
}
|
|
@@ -281,8 +316,14 @@ async function info(ctx) {
|
|
|
281
316
|
};
|
|
282
317
|
}
|
|
283
318
|
async function updateSettings(ctx) {
|
|
284
|
-
|
|
319
|
+
let { useWhitelist, enforceOIDC } = ctx.request.body;
|
|
285
320
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
321
|
+
if (useWhitelist && enforceOIDC) {
|
|
322
|
+
const users = await whitelistService2.getUsers();
|
|
323
|
+
if (users.length === 0) {
|
|
324
|
+
enforceOIDC = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
286
327
|
await whitelistService2.setSettings({ useWhitelist, enforceOIDC });
|
|
287
328
|
ctx.body = { useWhitelist, enforceOIDC };
|
|
288
329
|
}
|
|
@@ -296,12 +337,11 @@ async function publicSettings(ctx) {
|
|
|
296
337
|
async function register(ctx) {
|
|
297
338
|
const { email, roles: roles2 } = ctx.request.body;
|
|
298
339
|
if (!email) {
|
|
299
|
-
ctx.body = {
|
|
300
|
-
message: "Please enter a valid email address"
|
|
301
|
-
};
|
|
340
|
+
ctx.body = { message: "Please enter a valid email address" };
|
|
302
341
|
return;
|
|
303
342
|
}
|
|
304
|
-
const
|
|
343
|
+
const rawEmails = Array.isArray(email) ? email : email.split(",");
|
|
344
|
+
const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
305
345
|
const existingUsers = await strapi.query("admin::user").findMany({
|
|
306
346
|
where: { email: { $in: emailList } },
|
|
307
347
|
populate: ["roles"]
|
|
@@ -311,8 +351,8 @@ async function register(ctx) {
|
|
|
311
351
|
for (const singleEmail of emailList) {
|
|
312
352
|
const existingUser = existingUsers.find((u) => u.email === singleEmail);
|
|
313
353
|
let finalRoles = roles2;
|
|
314
|
-
if (existingUser
|
|
315
|
-
finalRoles = existingUser.roles.map((r) => r.id
|
|
354
|
+
if (existingUser?.roles) {
|
|
355
|
+
finalRoles = existingUser.roles.map((r) => String(r.id));
|
|
316
356
|
matchedExistingUsersCount++;
|
|
317
357
|
}
|
|
318
358
|
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({
|
|
@@ -331,7 +371,8 @@ async function removeEmail(ctx) {
|
|
|
331
371
|
ctx.body = {};
|
|
332
372
|
}
|
|
333
373
|
async function syncUsers(ctx) {
|
|
334
|
-
|
|
374
|
+
let { users } = ctx.request.body;
|
|
375
|
+
users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
|
|
335
376
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
336
377
|
const currentUsers = await whitelistService2.getUsers();
|
|
337
378
|
let matchedExistingUsersCount = 0;
|
|
@@ -349,8 +390,8 @@ async function syncUsers(ctx) {
|
|
|
349
390
|
const existingStrapiUser = existingStrapiUsers.find((u) => u.email === user.email);
|
|
350
391
|
let finalRoles = user.roles;
|
|
351
392
|
const currUser = currentUsers.find((u) => u.email === user.email);
|
|
352
|
-
if (!currUser && existingStrapiUser
|
|
353
|
-
finalRoles = existingStrapiUser.roles.map((r) => r.id
|
|
393
|
+
if (!currUser && existingStrapiUser?.roles) {
|
|
394
|
+
finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
|
|
354
395
|
matchedExistingUsersCount++;
|
|
355
396
|
}
|
|
356
397
|
if (currUser) {
|
|
@@ -377,6 +418,23 @@ const controllers = {
|
|
|
377
418
|
role,
|
|
378
419
|
whitelist
|
|
379
420
|
};
|
|
421
|
+
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
422
|
+
const RATE_LIMIT_WINDOW = 6e4;
|
|
423
|
+
const MAX_REQUESTS = 20;
|
|
424
|
+
const rateLimitMiddleware = async (ctx, next) => {
|
|
425
|
+
const ip = ctx.request.ip;
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
const windowStart = now - RATE_LIMIT_WINDOW;
|
|
428
|
+
const requestStamps = (rateLimitMap.get(ip) || []).filter((timestamp) => timestamp > windowStart);
|
|
429
|
+
if (requestStamps.length >= MAX_REQUESTS) {
|
|
430
|
+
ctx.status = 429;
|
|
431
|
+
ctx.body = "Too Many Requests";
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
requestStamps.push(now);
|
|
435
|
+
rateLimitMap.set(ip, requestStamps);
|
|
436
|
+
await next();
|
|
437
|
+
};
|
|
380
438
|
const routes = [
|
|
381
439
|
{
|
|
382
440
|
method: "GET",
|
|
@@ -396,7 +454,10 @@ const routes = [
|
|
|
396
454
|
config: {
|
|
397
455
|
policies: [
|
|
398
456
|
"admin::isAuthenticatedAdmin",
|
|
399
|
-
{
|
|
457
|
+
{
|
|
458
|
+
name: "admin::hasPermissions",
|
|
459
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
460
|
+
}
|
|
400
461
|
]
|
|
401
462
|
}
|
|
402
463
|
},
|
|
@@ -405,7 +466,8 @@ const routes = [
|
|
|
405
466
|
path: "/oidc",
|
|
406
467
|
handler: "oidc.oidcSignIn",
|
|
407
468
|
config: {
|
|
408
|
-
auth: false
|
|
469
|
+
auth: false,
|
|
470
|
+
middlewares: [rateLimitMiddleware]
|
|
409
471
|
}
|
|
410
472
|
},
|
|
411
473
|
{
|
|
@@ -413,7 +475,8 @@ const routes = [
|
|
|
413
475
|
path: "/oidc/callback",
|
|
414
476
|
handler: "oidc.oidcSignInCallback",
|
|
415
477
|
config: {
|
|
416
|
-
auth: false
|
|
478
|
+
auth: false,
|
|
479
|
+
middlewares: [rateLimitMiddleware]
|
|
417
480
|
}
|
|
418
481
|
},
|
|
419
482
|
{
|
|
@@ -442,7 +505,10 @@ const routes = [
|
|
|
442
505
|
config: {
|
|
443
506
|
policies: [
|
|
444
507
|
"admin::isAuthenticatedAdmin",
|
|
445
|
-
{
|
|
508
|
+
{
|
|
509
|
+
name: "admin::hasPermissions",
|
|
510
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
511
|
+
}
|
|
446
512
|
]
|
|
447
513
|
}
|
|
448
514
|
},
|
|
@@ -461,7 +527,10 @@ const routes = [
|
|
|
461
527
|
config: {
|
|
462
528
|
policies: [
|
|
463
529
|
"admin::isAuthenticatedAdmin",
|
|
464
|
-
{
|
|
530
|
+
{
|
|
531
|
+
name: "admin::hasPermissions",
|
|
532
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
533
|
+
}
|
|
465
534
|
]
|
|
466
535
|
}
|
|
467
536
|
},
|
|
@@ -472,7 +541,10 @@ const routes = [
|
|
|
472
541
|
config: {
|
|
473
542
|
policies: [
|
|
474
543
|
"admin::isAuthenticatedAdmin",
|
|
475
|
-
{
|
|
544
|
+
{
|
|
545
|
+
name: "admin::hasPermissions",
|
|
546
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
547
|
+
}
|
|
476
548
|
]
|
|
477
549
|
}
|
|
478
550
|
},
|
|
@@ -483,12 +555,132 @@ const routes = [
|
|
|
483
555
|
config: {
|
|
484
556
|
policies: [
|
|
485
557
|
"admin::isAuthenticatedAdmin",
|
|
486
|
-
{
|
|
558
|
+
{
|
|
559
|
+
name: "admin::hasPermissions",
|
|
560
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
561
|
+
}
|
|
487
562
|
]
|
|
488
563
|
}
|
|
489
564
|
}
|
|
490
565
|
];
|
|
491
566
|
const policies = {};
|
|
567
|
+
function renderHtmlTemplate(title, content) {
|
|
568
|
+
return `
|
|
569
|
+
<!doctype html>
|
|
570
|
+
<html lang="en">
|
|
571
|
+
<head>
|
|
572
|
+
<meta charset="utf-8">
|
|
573
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
574
|
+
<title>${title}</title>
|
|
575
|
+
<style>
|
|
576
|
+
:root {
|
|
577
|
+
--bg-color: #f6f6f9;
|
|
578
|
+
--card-bg: #ffffff;
|
|
579
|
+
--text-color: #32324d;
|
|
580
|
+
--text-muted: #666687;
|
|
581
|
+
--btn-bg: #4945ff;
|
|
582
|
+
--btn-hover: #271fe0;
|
|
583
|
+
--btn-text: #ffffff;
|
|
584
|
+
--icon-bg: #fcecea;
|
|
585
|
+
--icon-color: #d02b20;
|
|
586
|
+
--success-bg: #eafbe7;
|
|
587
|
+
--success-color: #328048;
|
|
588
|
+
--shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
|
|
589
|
+
}
|
|
590
|
+
@media (prefers-color-scheme: dark) {
|
|
591
|
+
:root {
|
|
592
|
+
--bg-color: #181826;
|
|
593
|
+
--card-bg: #212134;
|
|
594
|
+
--text-color: #ffffff;
|
|
595
|
+
--text-muted: #a5a5ba;
|
|
596
|
+
--btn-bg: #4945ff;
|
|
597
|
+
--btn-hover: #7b79ff;
|
|
598
|
+
--btn-text: #ffffff;
|
|
599
|
+
--icon-bg: #4a2123;
|
|
600
|
+
--icon-color: #f23628;
|
|
601
|
+
--success-bg: #1c3523;
|
|
602
|
+
--success-color: #55ca76;
|
|
603
|
+
--shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
body {
|
|
607
|
+
margin: 0;
|
|
608
|
+
padding: 0;
|
|
609
|
+
display: flex;
|
|
610
|
+
justify-content: center;
|
|
611
|
+
align-items: center;
|
|
612
|
+
height: 100vh;
|
|
613
|
+
background-color: var(--bg-color);
|
|
614
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
615
|
+
color: var(--text-color);
|
|
616
|
+
}
|
|
617
|
+
.card {
|
|
618
|
+
background: var(--card-bg);
|
|
619
|
+
padding: 32px 40px;
|
|
620
|
+
border-radius: 8px;
|
|
621
|
+
box-shadow: var(--shadow);
|
|
622
|
+
max-width: 400px;
|
|
623
|
+
width: 100%;
|
|
624
|
+
text-align: center;
|
|
625
|
+
box-sizing: border-box;
|
|
626
|
+
}
|
|
627
|
+
.icon {
|
|
628
|
+
width: 48px;
|
|
629
|
+
height: 48px;
|
|
630
|
+
background-color: var(--icon-bg);
|
|
631
|
+
color: var(--icon-color);
|
|
632
|
+
border-radius: 50%;
|
|
633
|
+
display: inline-flex;
|
|
634
|
+
justify-content: center;
|
|
635
|
+
align-items: center;
|
|
636
|
+
margin-bottom: 24px;
|
|
637
|
+
}
|
|
638
|
+
.icon.success {
|
|
639
|
+
background-color: var(--success-bg);
|
|
640
|
+
color: var(--success-color);
|
|
641
|
+
}
|
|
642
|
+
.icon svg {
|
|
643
|
+
width: 24px;
|
|
644
|
+
height: 24px;
|
|
645
|
+
stroke: currentColor;
|
|
646
|
+
stroke-width: 2;
|
|
647
|
+
stroke-linecap: round;
|
|
648
|
+
stroke-linejoin: round;
|
|
649
|
+
fill: none;
|
|
650
|
+
}
|
|
651
|
+
h1 {
|
|
652
|
+
margin: 0 0 12px 0;
|
|
653
|
+
font-size: 20px;
|
|
654
|
+
font-weight: 600;
|
|
655
|
+
color: var(--text-color);
|
|
656
|
+
}
|
|
657
|
+
p {
|
|
658
|
+
margin: 0 0 32px 0;
|
|
659
|
+
font-size: 14px;
|
|
660
|
+
line-height: 1.5;
|
|
661
|
+
color: var(--text-muted);
|
|
662
|
+
}
|
|
663
|
+
.btn {
|
|
664
|
+
display: inline-block;
|
|
665
|
+
background-color: var(--btn-bg);
|
|
666
|
+
color: var(--btn-text);
|
|
667
|
+
padding: 10px 16px;
|
|
668
|
+
border-radius: 4px;
|
|
669
|
+
text-decoration: none;
|
|
670
|
+
font-size: 14px;
|
|
671
|
+
font-weight: 500;
|
|
672
|
+
transition: background-color 0.2s;
|
|
673
|
+
}
|
|
674
|
+
.btn:hover {
|
|
675
|
+
background-color: var(--btn-hover);
|
|
676
|
+
}
|
|
677
|
+
</style>
|
|
678
|
+
</head>
|
|
679
|
+
<body>
|
|
680
|
+
${content}
|
|
681
|
+
</body>
|
|
682
|
+
</html>`;
|
|
683
|
+
}
|
|
492
684
|
function oauthService({ strapi: strapi2 }) {
|
|
493
685
|
return {
|
|
494
686
|
async createUser(email, lastname, firstname, locale, roles2 = []) {
|
|
@@ -500,8 +692,8 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
500
692
|
}
|
|
501
693
|
}
|
|
502
694
|
const createdUser = await userService.create({
|
|
503
|
-
firstname: firstname
|
|
504
|
-
lastname: lastname
|
|
695
|
+
firstname: firstname || "unset",
|
|
696
|
+
lastname: lastname || "",
|
|
505
697
|
email: email.toLocaleLowerCase(),
|
|
506
698
|
roles: roles2,
|
|
507
699
|
preferedLanguage: locale
|
|
@@ -509,8 +701,8 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
509
701
|
return await userService.register({
|
|
510
702
|
registrationToken: createdUser.registrationToken,
|
|
511
703
|
userInfo: {
|
|
512
|
-
firstname: firstname
|
|
513
|
-
lastname: lastname
|
|
704
|
+
firstname: firstname || "unset",
|
|
705
|
+
lastname: lastname || "user",
|
|
514
706
|
password: generator__default.default.generate({
|
|
515
707
|
length: 43,
|
|
516
708
|
// 256 bits (https://en.wikipedia.org/wiki/Password_strength#Random_passwords)
|
|
@@ -534,11 +726,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
534
726
|
return `${origin}+${alias}${domain}`;
|
|
535
727
|
},
|
|
536
728
|
localeFindByHeader(headers) {
|
|
537
|
-
|
|
538
|
-
return "ja";
|
|
539
|
-
} else {
|
|
540
|
-
return "en";
|
|
541
|
-
}
|
|
729
|
+
return headers["accept-language"]?.includes("ja") ? "ja" : "en";
|
|
542
730
|
},
|
|
543
731
|
async triggerWebHook(user) {
|
|
544
732
|
let ENTRY_CREATE;
|
|
@@ -561,7 +749,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
561
749
|
});
|
|
562
750
|
},
|
|
563
751
|
triggerSignInSuccess(user) {
|
|
564
|
-
delete user
|
|
752
|
+
delete user.password;
|
|
565
753
|
const eventHub = strapi2.serviceMap.get("eventHub");
|
|
566
754
|
eventHub.emit("admin.auth.success", {
|
|
567
755
|
user,
|
|
@@ -573,40 +761,48 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
573
761
|
const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
|
|
574
762
|
const REMEMBER_ME = config2["REMEMBER_ME"];
|
|
575
763
|
const isRememberMe = !!REMEMBER_ME;
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<
|
|
579
|
-
<
|
|
580
|
-
<
|
|
581
|
-
<
|
|
582
|
-
</
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
764
|
+
const content = `
|
|
765
|
+
<noscript>
|
|
766
|
+
<div class="card">
|
|
767
|
+
<div class="icon success">
|
|
768
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check">
|
|
769
|
+
<path d="M20 6 9 17l-5-5"/>
|
|
770
|
+
</svg>
|
|
771
|
+
</div>
|
|
772
|
+
<h1>JavaScript Required</h1>
|
|
773
|
+
<p>JavaScript must be enabled for authentication to complete.</p>
|
|
774
|
+
</div>
|
|
775
|
+
</noscript>
|
|
776
|
+
<script nonce="${nonce}">
|
|
777
|
+
window.addEventListener('load', function() {
|
|
778
|
+
if(${isRememberMe}){
|
|
779
|
+
localStorage.setItem('jwtToken', '"${jwtToken}"');
|
|
780
|
+
}else{
|
|
781
|
+
document.cookie = 'jwtToken=${encodeURIComponent(jwtToken)}; Path=/';
|
|
782
|
+
}
|
|
783
|
+
localStorage.setItem('isLoggedIn', 'true');
|
|
784
|
+
location.href = '${strapi2.config.admin.url}'
|
|
785
|
+
})
|
|
786
|
+
<\/script>`;
|
|
787
|
+
return renderHtmlTemplate("Authenticating...", content);
|
|
598
788
|
},
|
|
599
789
|
// Sign In Error
|
|
600
790
|
renderSignUpError(message) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
<
|
|
604
|
-
<
|
|
605
|
-
<
|
|
606
|
-
<
|
|
607
|
-
<
|
|
608
|
-
|
|
609
|
-
</
|
|
791
|
+
const safeMessage = String(message).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
792
|
+
const content = `
|
|
793
|
+
<div class="card">
|
|
794
|
+
<div class="icon">
|
|
795
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert">
|
|
796
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/>
|
|
797
|
+
<path d="M12 9v4"/>
|
|
798
|
+
<path d="M12 17h.01"/>
|
|
799
|
+
</svg>
|
|
800
|
+
</div>
|
|
801
|
+
<h1>Authentication Failed</h1>
|
|
802
|
+
<p>${safeMessage}</p>
|
|
803
|
+
<a href="${strapi2.config.admin.url}" class="btn">Return to Login</a>
|
|
804
|
+
</div>`;
|
|
805
|
+
return renderHtmlTemplate("Authentication Failed", content);
|
|
610
806
|
},
|
|
611
807
|
async generateToken(user, ctx) {
|
|
612
808
|
const sessionManager = strapi2.sessionManager;
|
|
@@ -661,35 +857,35 @@ function roleService({ strapi: strapi2 }) {
|
|
|
661
857
|
getOidcRoles() {
|
|
662
858
|
return [
|
|
663
859
|
{
|
|
664
|
-
|
|
860
|
+
oauth_type: this.OIDC_TYPE,
|
|
665
861
|
name: "OIDC"
|
|
666
862
|
}
|
|
667
863
|
];
|
|
668
864
|
},
|
|
669
865
|
async oidcRoles() {
|
|
670
|
-
return
|
|
866
|
+
return strapi2.query("plugin::strapi-plugin-oidc.roles").findOne({
|
|
671
867
|
where: {
|
|
672
|
-
|
|
868
|
+
oauth_type: this.OIDC_TYPE
|
|
673
869
|
}
|
|
674
870
|
});
|
|
675
871
|
},
|
|
676
872
|
async find() {
|
|
677
|
-
return
|
|
873
|
+
return strapi2.query("plugin::strapi-plugin-oidc.roles").findMany();
|
|
678
874
|
},
|
|
679
875
|
async update(roles2) {
|
|
680
876
|
const query = strapi2.query("plugin::strapi-plugin-oidc.roles");
|
|
681
877
|
await Promise.all(
|
|
682
878
|
roles2.map(async (role2) => {
|
|
683
|
-
const oidcRole = await query.findOne({ where: {
|
|
879
|
+
const oidcRole = await query.findOne({ where: { oauth_type: role2.oauth_type } });
|
|
684
880
|
if (oidcRole) {
|
|
685
881
|
await query.update({
|
|
686
|
-
where: {
|
|
882
|
+
where: { oauth_type: role2.oauth_type },
|
|
687
883
|
data: { roles: role2.role }
|
|
688
884
|
});
|
|
689
885
|
} else {
|
|
690
886
|
await query.create({
|
|
691
887
|
data: {
|
|
692
|
-
|
|
888
|
+
oauth_type: role2.oauth_type,
|
|
693
889
|
roles: role2.role
|
|
694
890
|
}
|
|
695
891
|
});
|
|
@@ -700,53 +896,48 @@ function roleService({ strapi: strapi2 }) {
|
|
|
700
896
|
};
|
|
701
897
|
}
|
|
702
898
|
function whitelistService({ strapi: strapi2 }) {
|
|
899
|
+
const getPluginStore = () => strapi2.store({
|
|
900
|
+
environment: "",
|
|
901
|
+
type: "plugin",
|
|
902
|
+
name: "strapi-plugin-oidc"
|
|
903
|
+
});
|
|
904
|
+
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
703
905
|
return {
|
|
704
906
|
async getSettings() {
|
|
705
|
-
|
|
706
|
-
let settings = await pluginStore.get({ key: "settings" });
|
|
907
|
+
let settings = await getPluginStore().get({ key: "settings" });
|
|
707
908
|
if (!settings) {
|
|
708
909
|
settings = { useWhitelist: true, enforceOIDC: false };
|
|
709
|
-
await
|
|
910
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
710
911
|
}
|
|
711
912
|
return settings;
|
|
712
913
|
},
|
|
713
914
|
async setSettings(settings) {
|
|
714
|
-
|
|
715
|
-
await pluginStore.set({ key: "settings", value: settings });
|
|
915
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
716
916
|
},
|
|
717
917
|
async getUsers() {
|
|
718
|
-
|
|
719
|
-
return await query.findMany();
|
|
918
|
+
return getWhitelistQuery().findMany();
|
|
720
919
|
},
|
|
721
920
|
async registerUser(email, roles2) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
data: {
|
|
725
|
-
email,
|
|
726
|
-
roles: roles2
|
|
727
|
-
}
|
|
921
|
+
await getWhitelistQuery().create({
|
|
922
|
+
data: { email, roles: roles2 }
|
|
728
923
|
});
|
|
729
924
|
},
|
|
730
925
|
async removeUser(id) {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
where: {
|
|
734
|
-
id
|
|
735
|
-
}
|
|
926
|
+
await getWhitelistQuery().delete({
|
|
927
|
+
where: { id }
|
|
736
928
|
});
|
|
737
929
|
},
|
|
738
930
|
async checkWhitelistForEmail(email) {
|
|
739
931
|
const settings = await this.getSettings();
|
|
932
|
+
console.log("checkWhitelistForEmail settings:", settings);
|
|
740
933
|
if (!settings.useWhitelist) {
|
|
741
934
|
return null;
|
|
742
935
|
}
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
where: {
|
|
746
|
-
email
|
|
747
|
-
}
|
|
936
|
+
const result = await getWhitelistQuery().findOne({
|
|
937
|
+
where: { email }
|
|
748
938
|
});
|
|
749
|
-
|
|
939
|
+
console.log("checkWhitelistForEmail result:", result);
|
|
940
|
+
if (!result) {
|
|
750
941
|
throw new Error("Not present in whitelist");
|
|
751
942
|
}
|
|
752
943
|
return result;
|