strapi-plugin-oidc 1.6.1 → 1.6.2

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 CHANGED
@@ -121,7 +121,12 @@ Role names are the **display names** shown in **Settings → Roles** (e.g. `"Edi
121
121
  1. **User's OIDC groups match `OIDC_GROUP_ROLE_MAP`** → use the mapped Strapi roles
122
122
  2. **No group match or no mapping configured** → use the default OIDC roles
123
123
 
124
- > **Note:** Existing users' roles are updated on every login to reflect current group membership.
124
+ ### Role updates on subsequent logins
125
+
126
+ - **New users** — OIDC roles are always assigned on first login.
127
+ - **Existing users with manually unchanged roles** — If a user's current roles still match the roles assigned by OIDC on their previous login (i.e., an administrator has not manually changed their roles), their roles are updated to reflect the current group mapping. This ensures that when the group-to-role mapping changes, returning users pick up the new roles automatically.
128
+ - **Existing users with manually changed roles** — If an administrator has manually assigned the user different roles since their last OIDC login, the user's roles are left unchanged. OIDC will not overwrite a manual role assignment.
129
+ - **Mapping removed or user's groups don't map** — If the `OIDC_GROUP_ROLE_MAP` is removed, a user's groups no longer match any mapping, or there are no default OIDC roles configured, the user keeps their last known roles.
125
130
 
126
131
  ## Whitelist API
127
132
 
@@ -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();
@@ -506,40 +530,26 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
506
530
  const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
507
531
  let userCreated = false;
508
532
  let rolesUpdated = false;
509
- let activateUser = await userService.findOneByEmail(email);
510
- if (!activateUser) {
511
- activateUser = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
533
+ let user = await userService.findOneByEmail(email, ["roles"]);
534
+ if (!user) {
535
+ user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
512
536
  userCreated = true;
537
+ rolesUpdated = true;
513
538
  } else if (roles2.length > 0) {
514
- const currentRoleIds = new Set((activateUser.roles ?? []).map((r) => String(r.id)));
539
+ const defaultRoleIds = new Set(user.roles.map((r) => String(r.id)));
540
+ const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
515
541
  const newRoleIds = new Set(roles2);
516
- const rolesChanged = currentRoleIds.size !== newRoleIds.size || [...newRoleIds].some((id) => !currentRoleIds.has(id));
517
- if (rolesChanged) {
518
- try {
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
- });
542
+ if (rolesChanged(currentRoleIds, newRoleIds)) {
543
+ const isOnDefaultRoles = currentRoleIds.size === defaultRoleIds.size && [...currentRoleIds].every((id) => defaultRoleIds.has(id));
544
+ if (isOnDefaultRoles) {
545
+ await updateUserRoles(user, currentRoleIds, roles2);
526
546
  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
547
  }
538
548
  }
539
549
  }
540
- const jwtToken = await oauthService2.generateToken(activateUser, ctx);
541
- oauthService2.triggerSignInSuccess(activateUser);
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 strapi2.db.query("plugin::strapi-plugin-oidc.whitelists").deleteMany({
1538
+ await getWhitelistQuery().deleteMany({
1529
1539
  where: { email }
1530
1540
  });
1531
1541
  },
@@ -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();
@@ -500,40 +524,26 @@ async function handleUserAuthentication(userService, oauthService2, roleService2
500
524
  const resolvedRoleNames = allRoles.filter((r) => roles2.includes(String(r.id))).map((r) => r.name);
501
525
  let userCreated = false;
502
526
  let rolesUpdated = false;
503
- let activateUser = await userService.findOneByEmail(email);
504
- if (!activateUser) {
505
- activateUser = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
527
+ let user = await userService.findOneByEmail(email, ["roles"]);
528
+ if (!user) {
529
+ user = await registerNewUser(oauthService2, email, userResponseData, config2, ctx, roles2);
506
530
  userCreated = true;
531
+ rolesUpdated = true;
507
532
  } else if (roles2.length > 0) {
508
- const currentRoleIds = new Set((activateUser.roles ?? []).map((r) => String(r.id)));
533
+ const defaultRoleIds = new Set(user.roles.map((r) => String(r.id)));
534
+ const currentRoleIds = new Set(user.roles.map((r) => String(r.id)));
509
535
  const newRoleIds = new Set(roles2);
510
- const rolesChanged = currentRoleIds.size !== newRoleIds.size || [...newRoleIds].some((id) => !currentRoleIds.has(id));
511
- if (rolesChanged) {
512
- try {
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
- });
536
+ if (rolesChanged(currentRoleIds, newRoleIds)) {
537
+ const isOnDefaultRoles = currentRoleIds.size === defaultRoleIds.size && [...currentRoleIds].every((id) => defaultRoleIds.has(id));
538
+ if (isOnDefaultRoles) {
539
+ await updateUserRoles(user, currentRoleIds, roles2);
520
540
  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
541
  }
532
542
  }
533
543
  }
534
- const jwtToken = await oauthService2.generateToken(activateUser, ctx);
535
- oauthService2.triggerSignInSuccess(activateUser);
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 strapi2.db.query("plugin::strapi-plugin-oidc.whitelists").deleteMany({
1532
+ await getWhitelistQuery().deleteMany({
1523
1533
  where: { email }
1524
1534
  });
1525
1535
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-oidc",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
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",