strapi-plugin-oidc 1.0.4 → 1.0.5
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/dist/admin/{index-C0GkDnGG.js → index-B525UaV3.js} +12 -18
- package/dist/admin/{index-DwpTg1-J.mjs → index-BbD-7Z4N.mjs} +182 -112
- package/dist/admin/{index-BuuCScSN.mjs → index-D3AvxXlB.mjs} +12 -18
- package/dist/admin/{index-D_0jCOLk.js → index-DlQ8NUBY.js} +181 -111
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +118 -87
- package/dist/server/index.mjs +118 -87
- 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 = userResponseData.email;
|
|
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
|
}
|
|
@@ -289,12 +324,10 @@ async function publicSettings(ctx) {
|
|
|
289
324
|
async function register(ctx) {
|
|
290
325
|
const { email, roles: roles2 } = ctx.request.body;
|
|
291
326
|
if (!email) {
|
|
292
|
-
ctx.body = {
|
|
293
|
-
message: "Please enter a valid email address"
|
|
294
|
-
};
|
|
327
|
+
ctx.body = { message: "Please enter a valid email address" };
|
|
295
328
|
return;
|
|
296
329
|
}
|
|
297
|
-
const emailList = Array.isArray(email) ? email : email.split(",").map((e) => e.trim()).filter(
|
|
330
|
+
const emailList = Array.isArray(email) ? email : email.split(",").map((e) => e.trim()).filter(Boolean);
|
|
298
331
|
const existingUsers = await strapi.query("admin::user").findMany({
|
|
299
332
|
where: { email: { $in: emailList } },
|
|
300
333
|
populate: ["roles"]
|
|
@@ -304,8 +337,8 @@ async function register(ctx) {
|
|
|
304
337
|
for (const singleEmail of emailList) {
|
|
305
338
|
const existingUser = existingUsers.find((u) => u.email === singleEmail);
|
|
306
339
|
let finalRoles = roles2;
|
|
307
|
-
if (existingUser
|
|
308
|
-
finalRoles = existingUser.roles.map((r) => r.id
|
|
340
|
+
if (existingUser?.roles) {
|
|
341
|
+
finalRoles = existingUser.roles.map((r) => String(r.id));
|
|
309
342
|
matchedExistingUsersCount++;
|
|
310
343
|
}
|
|
311
344
|
const alreadyWhitelisted = await strapi.query("plugin::strapi-plugin-oidc.whitelists").findOne({
|
|
@@ -342,8 +375,8 @@ async function syncUsers(ctx) {
|
|
|
342
375
|
const existingStrapiUser = existingStrapiUsers.find((u) => u.email === user.email);
|
|
343
376
|
let finalRoles = user.roles;
|
|
344
377
|
const currUser = currentUsers.find((u) => u.email === user.email);
|
|
345
|
-
if (!currUser && existingStrapiUser
|
|
346
|
-
finalRoles = existingStrapiUser.roles.map((r) => r.id
|
|
378
|
+
if (!currUser && existingStrapiUser?.roles) {
|
|
379
|
+
finalRoles = existingStrapiUser.roles.map((r) => String(r.id));
|
|
347
380
|
matchedExistingUsersCount++;
|
|
348
381
|
}
|
|
349
382
|
if (currUser) {
|
|
@@ -493,8 +526,8 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
493
526
|
}
|
|
494
527
|
}
|
|
495
528
|
const createdUser = await userService.create({
|
|
496
|
-
firstname: firstname
|
|
497
|
-
lastname: lastname
|
|
529
|
+
firstname: firstname || "unset",
|
|
530
|
+
lastname: lastname || "",
|
|
498
531
|
email: email.toLocaleLowerCase(),
|
|
499
532
|
roles: roles2,
|
|
500
533
|
preferedLanguage: locale
|
|
@@ -502,8 +535,8 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
502
535
|
return await userService.register({
|
|
503
536
|
registrationToken: createdUser.registrationToken,
|
|
504
537
|
userInfo: {
|
|
505
|
-
firstname: firstname
|
|
506
|
-
lastname: lastname
|
|
538
|
+
firstname: firstname || "unset",
|
|
539
|
+
lastname: lastname || "user",
|
|
507
540
|
password: generator.generate({
|
|
508
541
|
length: 43,
|
|
509
542
|
// 256 bits (https://en.wikipedia.org/wiki/Password_strength#Random_passwords)
|
|
@@ -527,11 +560,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
527
560
|
return `${origin}+${alias}${domain}`;
|
|
528
561
|
},
|
|
529
562
|
localeFindByHeader(headers) {
|
|
530
|
-
|
|
531
|
-
return "ja";
|
|
532
|
-
} else {
|
|
533
|
-
return "en";
|
|
534
|
-
}
|
|
563
|
+
return headers["accept-language"]?.includes("ja") ? "ja" : "en";
|
|
535
564
|
},
|
|
536
565
|
async triggerWebHook(user) {
|
|
537
566
|
let ENTRY_CREATE;
|
|
@@ -554,7 +583,7 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
554
583
|
});
|
|
555
584
|
},
|
|
556
585
|
triggerSignInSuccess(user) {
|
|
557
|
-
delete user
|
|
586
|
+
delete user.password;
|
|
558
587
|
const eventHub = strapi2.serviceMap.get("eventHub");
|
|
559
588
|
eventHub.emit("admin.auth.success", {
|
|
560
589
|
user,
|
|
@@ -654,35 +683,35 @@ function roleService({ strapi: strapi2 }) {
|
|
|
654
683
|
getOidcRoles() {
|
|
655
684
|
return [
|
|
656
685
|
{
|
|
657
|
-
|
|
686
|
+
oauth_type: this.OIDC_TYPE,
|
|
658
687
|
name: "OIDC"
|
|
659
688
|
}
|
|
660
689
|
];
|
|
661
690
|
},
|
|
662
691
|
async oidcRoles() {
|
|
663
|
-
return
|
|
692
|
+
return strapi2.query("plugin::strapi-plugin-oidc.roles").findOne({
|
|
664
693
|
where: {
|
|
665
|
-
|
|
694
|
+
oauth_type: this.OIDC_TYPE
|
|
666
695
|
}
|
|
667
696
|
});
|
|
668
697
|
},
|
|
669
698
|
async find() {
|
|
670
|
-
return
|
|
699
|
+
return strapi2.query("plugin::strapi-plugin-oidc.roles").findMany();
|
|
671
700
|
},
|
|
672
701
|
async update(roles2) {
|
|
673
702
|
const query = strapi2.query("plugin::strapi-plugin-oidc.roles");
|
|
674
703
|
await Promise.all(
|
|
675
704
|
roles2.map(async (role2) => {
|
|
676
|
-
const oidcRole = await query.findOne({ where: {
|
|
705
|
+
const oidcRole = await query.findOne({ where: { oauth_type: role2.oauth_type } });
|
|
677
706
|
if (oidcRole) {
|
|
678
707
|
await query.update({
|
|
679
|
-
where: {
|
|
708
|
+
where: { oauth_type: role2.oauth_type },
|
|
680
709
|
data: { roles: role2.role }
|
|
681
710
|
});
|
|
682
711
|
} else {
|
|
683
712
|
await query.create({
|
|
684
713
|
data: {
|
|
685
|
-
|
|
714
|
+
oauth_type: role2.oauth_type,
|
|
686
715
|
roles: role2.role
|
|
687
716
|
}
|
|
688
717
|
});
|
|
@@ -709,7 +738,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
709
738
|
},
|
|
710
739
|
async getUsers() {
|
|
711
740
|
const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
712
|
-
return
|
|
741
|
+
return query.findMany();
|
|
713
742
|
},
|
|
714
743
|
async registerUser(email, roles2) {
|
|
715
744
|
const query = strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
@@ -730,6 +759,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
730
759
|
},
|
|
731
760
|
async checkWhitelistForEmail(email) {
|
|
732
761
|
const settings = await this.getSettings();
|
|
762
|
+
console.log("checkWhitelistForEmail settings:", settings);
|
|
733
763
|
if (!settings.useWhitelist) {
|
|
734
764
|
return null;
|
|
735
765
|
}
|
|
@@ -739,7 +769,8 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
739
769
|
email
|
|
740
770
|
}
|
|
741
771
|
});
|
|
742
|
-
|
|
772
|
+
console.log("checkWhitelistForEmail result:", result);
|
|
773
|
+
if (!result) {
|
|
743
774
|
throw new Error("Not present in whitelist");
|
|
744
775
|
}
|
|
745
776
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-oidc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"verify": "strapi-plugin verify",
|
|
16
16
|
"test": "vitest run -c vitest.config.e2e.ts",
|
|
17
17
|
"lint": "eslint",
|
|
18
|
+
"fallow:check": "fallow",
|
|
19
|
+
"fallow:fix": "fallow fix --yes",
|
|
18
20
|
"prepare": "husky"
|
|
19
21
|
},
|
|
20
22
|
"lint-staged": {
|
|
@@ -73,7 +75,6 @@
|
|
|
73
75
|
"@eslint/eslintrc": "^3.3.5",
|
|
74
76
|
"@eslint/js": "^10.0.1",
|
|
75
77
|
"@strapi/sdk-plugin": "^6.0.1",
|
|
76
|
-
"@strapi/typescript-utils": "^5.41.1",
|
|
77
78
|
"@types/node": "^25.5.2",
|
|
78
79
|
"@types/supertest": "^7.2.0",
|
|
79
80
|
"@vitest/coverage-v8": "^4.1.2",
|