From cff56fe2aacd850467ce53520fefbf43cfb5ebc7 Mon Sep 17 00:00:00 2001 From: shubhamkapoor01 <66124414+shubhamkapoor01@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:04:20 +0530 Subject: [PATCH 1/2] Treat a missing SAML group claim as no groups when other teams are allowed --- forge/ee/lib/sso/index.js | 5 +++++ test/unit/forge/ee/lib/sso/index_spec.js | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/forge/ee/lib/sso/index.js b/forge/ee/lib/sso/index.js index bbb8d38b1f..bea0ff1ac1 100644 --- a/forge/ee/lib/sso/index.js +++ b/forge/ee/lib/sso/index.js @@ -454,6 +454,11 @@ module.exports.init = async function (app) { // This is an array of groups the user belongs to. We expect them to be // of the form 'ff-SLUG-ROLE' - anything else is ignored let groupAssertions = samlUser[providerOpts.groupAssertionName] + if (!groupAssertions && providerOpts.groupOtherTeams) { + // Some IdPs (e.g. Azure) omit the claim when the user is in no groups + app.log.info(`SAML response missing ${providerOpts.groupAssertionName} assertion for ${user.username}, treating as no groups`) + groupAssertions = [] + } if (groupAssertions) { const promises = [] if (!Array.isArray(groupAssertions)) { diff --git a/test/unit/forge/ee/lib/sso/index_spec.js b/test/unit/forge/ee/lib/sso/index_spec.js index 90777bd491..7507fe1614 100644 --- a/test/unit/forge/ee/lib/sso/index_spec.js +++ b/test/unit/forge/ee/lib/sso/index_spec.js @@ -291,6 +291,18 @@ NOY6Z1oJnpttQ9gwyV8euQ3C0Wcjf3+OVQ== }) }) + it('treats missing group assertions as no groups when other teams are allowed', async function () { + // An IdP can omit the claim when the user is in no groups (seen with Azure) + await app.sso.updateTeamMembership({}, app.user, { + groupAssertionName: 'ff-roles', + groupTeams: ['ateam', 'bteam'], + groupOtherTeams: true + }) + should.not.exist(await app.db.models.TeamMember.getTeamMembership(app.user.id, teams.ATeam.id)) + should.not.exist(await app.db.models.TeamMember.getTeamMembership(app.user.id, teams.BTeam.id)) + ;(await app.db.models.TeamMember.getTeamMembership(app.user.id, teams.DTeam.id)).should.have.property('role', Roles.Viewer) + }) + it('applies team membership changes - all teams', async function () { // Apply changes to all teams // Covers: From c17481eec4d8b44d413f6d19245d3461f63b4717 Mon Sep 17 00:00:00 2001 From: shubhamkapoor01 <66124414+shubhamkapoor01@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:50:28 +0530 Subject: [PATCH 2/2] Test that a missing SAML group claim removes admin when groupAdmin is set --- test/unit/forge/ee/lib/sso/index_spec.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/unit/forge/ee/lib/sso/index_spec.js b/test/unit/forge/ee/lib/sso/index_spec.js index 7507fe1614..6a91df7009 100644 --- a/test/unit/forge/ee/lib/sso/index_spec.js +++ b/test/unit/forge/ee/lib/sso/index_spec.js @@ -303,6 +303,25 @@ NOY6Z1oJnpttQ9gwyV8euQ3C0Wcjf3+OVQ== ;(await app.db.models.TeamMember.getTeamMembership(app.user.id, teams.DTeam.id)).should.have.property('role', Roles.Viewer) }) + it('removes admin when group assertions are missing and groupAdmin is set', async function () { + // a second admin, so Alice is not the last one + const otherAdmin = await app.factory.createUser({ admin: true, username: 'sso-admin2', name: 'Other Admin', email: 'sso-admin2@example.com', password: 'ccPassword' }) + try { + await app.sso.updateTeamMembership({}, app.user, { + groupAssertionName: 'ff-roles', + groupTeams: ['ateam', 'bteam'], + groupOtherTeams: true, + groupAdmin: true, + groupAdminName: 'ff-admins' + }) + ;(await app.db.models.User.byId(app.user.id)).should.have.property('admin', false) + } finally { + app.user.admin = true + await app.user.save() + await otherAdmin.destroy() + } + }) + it('applies team membership changes - all teams', async function () { // Apply changes to all teams // Covers: