strapi-plugin-oidc 1.6.1 → 1.6.3
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 +6 -2
- package/dist/server/index.js +45 -35
- package/dist/server/index.mjs +45 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,9 +119,13 @@ Role names are the **display names** shown in **Settings → Roles** (e.g. `"Edi
|
|
|
119
119
|
### Role assignment precedence
|
|
120
120
|
|
|
121
121
|
1. **User's OIDC groups match `OIDC_GROUP_ROLE_MAP`** → use the mapped Strapi roles
|
|
122
|
-
2. **No group match or no mapping configured** → use the default OIDC roles
|
|
122
|
+
2. **No group match or no mapping configured** → use the default OIDC roles (new users only — see below)
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
### Role updates on subsequent logins
|
|
125
|
+
|
|
126
|
+
- **New users** — Roles are always assigned on first login: group-mapped roles if a match is found, otherwise the configured default OIDC roles.
|
|
127
|
+
- **Existing users with a group mapping match** — Roles are updated to reflect the current mapping. If a user's groups change between logins, their Strapi roles are updated accordingly.
|
|
128
|
+
- **Existing users with no group mapping match** — Roles are left unchanged, regardless of what the default OIDC roles are set to. Manually-assigned roles are never overwritten by a default fallback.
|
|
125
129
|
|
|
126
130
|
## Whitelist API
|
|
127
131
|
|
package/dist/server/index.js
CHANGED
|
@@ -476,9 +476,9 @@ function resolveRolesFromGroups(userInfo, config2, availableRoles) {
|
|
|
476
476
|
}
|
|
477
477
|
async function resolveRoles(userInfo, config2, roleService2, availableRoles) {
|
|
478
478
|
const groupRoles = resolveRolesFromGroups(userInfo, config2, availableRoles);
|
|
479
|
-
if (groupRoles.length > 0) return groupRoles;
|
|
479
|
+
if (groupRoles.length > 0) return { roles: groupRoles, fromGroupMapping: true };
|
|
480
480
|
const oidcRoles = await roleService2.oidcRoles();
|
|
481
|
-
return oidcRoles?.roles || [];
|
|
481
|
+
return { roles: oidcRoles?.roles || [], fromGroupMapping: false };
|
|
482
482
|
}
|
|
483
483
|
async function registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2) {
|
|
484
484
|
const defaultLocale = oauthService2.localeFindByHeader(
|
|
@@ -494,6 +494,30 @@ async function registerNewUser(oauthService2, email, userResponseData, config2,
|
|
|
494
494
|
await oauthService2.triggerWebHook(activateUser);
|
|
495
495
|
return activateUser;
|
|
496
496
|
}
|
|
497
|
+
function rolesChanged(current, next) {
|
|
498
|
+
return current.size !== next.size || [...next].some((id) => !current.has(id));
|
|
499
|
+
}
|
|
500
|
+
async function updateUserRoles(user, currentRoleIds, newRoleIds) {
|
|
501
|
+
try {
|
|
502
|
+
strapi.log.info(
|
|
503
|
+
`[OIDC] Roles updated for user ${user.id}: [${[...currentRoleIds].join(",")}] -> [${newRoleIds.join(",")}]`
|
|
504
|
+
);
|
|
505
|
+
await strapi.db.query("admin::user").update({
|
|
506
|
+
where: { id: user.id },
|
|
507
|
+
data: { roles: newRoleIds }
|
|
508
|
+
});
|
|
509
|
+
} catch (updateErr) {
|
|
510
|
+
strapi.log.error({
|
|
511
|
+
code: errorCodes.ROLE_UPDATE_FAILED,
|
|
512
|
+
userId: user.id,
|
|
513
|
+
detail: getErrorDetail("role_update_failed", {
|
|
514
|
+
userId: user.id,
|
|
515
|
+
error: updateErr.message
|
|
516
|
+
})
|
|
517
|
+
});
|
|
518
|
+
throw updateErr;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
497
521
|
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
498
522
|
const rawEmail = String(userResponseData.email ?? "");
|
|
499
523
|
const email = rawEmail.toLowerCase();
|
|
@@ -502,44 +526,30 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
502
526
|
}
|
|
503
527
|
await whitelistService2.checkWhitelistForEmail(email);
|
|
504
528
|
const allRoles = await strapi.db.query("admin::role").findMany();
|
|
505
|
-
const roles2 = await resolveRoles(
|
|
529
|
+
const { roles: roles2, fromGroupMapping } = await resolveRoles(
|
|
530
|
+
userResponseData,
|
|
531
|
+
config2,
|
|
532
|
+
roleService2,
|
|
533
|
+
allRoles
|
|
534
|
+
);
|
|
506
535
|
const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
|
|
507
536
|
let userCreated = false;
|
|
508
537
|
let rolesUpdated = false;
|
|
509
|
-
let
|
|
510
|
-
if (!
|
|
511
|
-
|
|
538
|
+
let user = await userService.findOneByEmail(email, ["roles"]);
|
|
539
|
+
if (!user) {
|
|
540
|
+
user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
|
|
512
541
|
userCreated = true;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
strapi.log.info(
|
|
520
|
-
`[OIDC] Roles updated for user ${activateUser.id}: [${[...currentRoleIds].join(",")}] -> [${roles2.join(",")}]`
|
|
521
|
-
);
|
|
522
|
-
await strapi.db.query("admin::user").update({
|
|
523
|
-
where: { id: activateUser.id },
|
|
524
|
-
data: { roles: roles2 }
|
|
525
|
-
});
|
|
526
|
-
rolesUpdated = true;
|
|
527
|
-
} catch (updateErr) {
|
|
528
|
-
strapi.log.error({
|
|
529
|
-
code: errorCodes.ROLE_UPDATE_FAILED,
|
|
530
|
-
userId: activateUser.id,
|
|
531
|
-
detail: getErrorDetail("role_update_failed", {
|
|
532
|
-
userId: activateUser.id,
|
|
533
|
-
error: updateErr.message
|
|
534
|
-
})
|
|
535
|
-
});
|
|
536
|
-
throw updateErr;
|
|
537
|
-
}
|
|
542
|
+
rolesUpdated = true;
|
|
543
|
+
} else if (fromGroupMapping && roles2.length > 0) {
|
|
544
|
+
const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
545
|
+
if (rolesChanged(currentRoleIds, new Set(roles2))) {
|
|
546
|
+
await updateUserRoles(user, currentRoleIds, roles2);
|
|
547
|
+
rolesUpdated = true;
|
|
538
548
|
}
|
|
539
549
|
}
|
|
540
|
-
const jwtToken = await oauthService2.generateToken(
|
|
541
|
-
oauthService2.triggerSignInSuccess(
|
|
542
|
-
return { activateUser, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
550
|
+
const jwtToken = await oauthService2.generateToken(user, ctx);
|
|
551
|
+
oauthService2.triggerSignInSuccess(user);
|
|
552
|
+
return { activateUser: user, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
543
553
|
}
|
|
544
554
|
function classifyOidcError(msg, userInfo) {
|
|
545
555
|
const errorMap = [
|
|
@@ -1525,7 +1535,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1525
1535
|
});
|
|
1526
1536
|
},
|
|
1527
1537
|
async removeUser(email) {
|
|
1528
|
-
await
|
|
1538
|
+
await getWhitelistQuery().deleteMany({
|
|
1529
1539
|
where: { email }
|
|
1530
1540
|
});
|
|
1531
1541
|
},
|
package/dist/server/index.mjs
CHANGED
|
@@ -470,9 +470,9 @@ function resolveRolesFromGroups(userInfo, config2, availableRoles) {
|
|
|
470
470
|
}
|
|
471
471
|
async function resolveRoles(userInfo, config2, roleService2, availableRoles) {
|
|
472
472
|
const groupRoles = resolveRolesFromGroups(userInfo, config2, availableRoles);
|
|
473
|
-
if (groupRoles.length > 0) return groupRoles;
|
|
473
|
+
if (groupRoles.length > 0) return { roles: groupRoles, fromGroupMapping: true };
|
|
474
474
|
const oidcRoles = await roleService2.oidcRoles();
|
|
475
|
-
return oidcRoles?.roles || [];
|
|
475
|
+
return { roles: oidcRoles?.roles || [], fromGroupMapping: false };
|
|
476
476
|
}
|
|
477
477
|
async function registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2) {
|
|
478
478
|
const defaultLocale = oauthService2.localeFindByHeader(
|
|
@@ -488,6 +488,30 @@ async function registerNewUser(oauthService2, email, userResponseData, config2,
|
|
|
488
488
|
await oauthService2.triggerWebHook(activateUser);
|
|
489
489
|
return activateUser;
|
|
490
490
|
}
|
|
491
|
+
function rolesChanged(current, next) {
|
|
492
|
+
return current.size !== next.size || [...next].some((id) => !current.has(id));
|
|
493
|
+
}
|
|
494
|
+
async function updateUserRoles(user, currentRoleIds, newRoleIds) {
|
|
495
|
+
try {
|
|
496
|
+
strapi.log.info(
|
|
497
|
+
`[OIDC] Roles updated for user ${user.id}: [${[...currentRoleIds].join(",")}] -> [${newRoleIds.join(",")}]`
|
|
498
|
+
);
|
|
499
|
+
await strapi.db.query("admin::user").update({
|
|
500
|
+
where: { id: user.id },
|
|
501
|
+
data: { roles: newRoleIds }
|
|
502
|
+
});
|
|
503
|
+
} catch (updateErr) {
|
|
504
|
+
strapi.log.error({
|
|
505
|
+
code: errorCodes.ROLE_UPDATE_FAILED,
|
|
506
|
+
userId: user.id,
|
|
507
|
+
detail: getErrorDetail("role_update_failed", {
|
|
508
|
+
userId: user.id,
|
|
509
|
+
error: updateErr.message
|
|
510
|
+
})
|
|
511
|
+
});
|
|
512
|
+
throw updateErr;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
491
515
|
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
492
516
|
const rawEmail = String(userResponseData.email ?? "");
|
|
493
517
|
const email = rawEmail.toLowerCase();
|
|
@@ -496,44 +520,30 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
|
|
|
496
520
|
}
|
|
497
521
|
await whitelistService2.checkWhitelistForEmail(email);
|
|
498
522
|
const allRoles = await strapi.db.query("admin::role").findMany();
|
|
499
|
-
const roles2 = await resolveRoles(
|
|
523
|
+
const { roles: roles2, fromGroupMapping } = await resolveRoles(
|
|
524
|
+
userResponseData,
|
|
525
|
+
config2,
|
|
526
|
+
roleService2,
|
|
527
|
+
allRoles
|
|
528
|
+
);
|
|
500
529
|
const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
|
|
501
530
|
let userCreated = false;
|
|
502
531
|
let rolesUpdated = false;
|
|
503
|
-
let
|
|
504
|
-
if (!
|
|
505
|
-
|
|
532
|
+
let user = await userService.findOneByEmail(email, ["roles"]);
|
|
533
|
+
if (!user) {
|
|
534
|
+
user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
|
|
506
535
|
userCreated = true;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
strapi.log.info(
|
|
514
|
-
`[OIDC] Roles updated for user ${activateUser.id}: [${[...currentRoleIds].join(",")}] -> [${roles2.join(",")}]`
|
|
515
|
-
);
|
|
516
|
-
await strapi.db.query("admin::user").update({
|
|
517
|
-
where: { id: activateUser.id },
|
|
518
|
-
data: { roles: roles2 }
|
|
519
|
-
});
|
|
520
|
-
rolesUpdated = true;
|
|
521
|
-
} catch (updateErr) {
|
|
522
|
-
strapi.log.error({
|
|
523
|
-
code: errorCodes.ROLE_UPDATE_FAILED,
|
|
524
|
-
userId: activateUser.id,
|
|
525
|
-
detail: getErrorDetail("role_update_failed", {
|
|
526
|
-
userId: activateUser.id,
|
|
527
|
-
error: updateErr.message
|
|
528
|
-
})
|
|
529
|
-
});
|
|
530
|
-
throw updateErr;
|
|
531
|
-
}
|
|
536
|
+
rolesUpdated = true;
|
|
537
|
+
} else if (fromGroupMapping && roles2.length > 0) {
|
|
538
|
+
const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
|
|
539
|
+
if (rolesChanged(currentRoleIds, new Set(roles2))) {
|
|
540
|
+
await updateUserRoles(user, currentRoleIds, roles2);
|
|
541
|
+
rolesUpdated = true;
|
|
532
542
|
}
|
|
533
543
|
}
|
|
534
|
-
const jwtToken = await oauthService2.generateToken(
|
|
535
|
-
oauthService2.triggerSignInSuccess(
|
|
536
|
-
return { activateUser, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
544
|
+
const jwtToken = await oauthService2.generateToken(user, ctx);
|
|
545
|
+
oauthService2.triggerSignInSuccess(user);
|
|
546
|
+
return { activateUser: user, jwtToken, userCreated, rolesUpdated, resolvedRoleNames };
|
|
537
547
|
}
|
|
538
548
|
function classifyOidcError(msg, userInfo) {
|
|
539
549
|
const errorMap = [
|
|
@@ -1519,7 +1529,7 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
1519
1529
|
});
|
|
1520
1530
|
},
|
|
1521
1531
|
async removeUser(email) {
|
|
1522
|
-
await
|
|
1532
|
+
await getWhitelistQuery().deleteMany({
|
|
1523
1533
|
where: { email }
|
|
1524
1534
|
});
|
|
1525
1535
|
},
|
package/package.json
CHANGED