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