OIDC - Azure roles (#7551)
stephannn committed
Mar 9, 2026 at 11:33 UTC
9d8849ebdf4356511bc69c246a5b5bfb5edab41c
3 files changed
+62
-20
meshcentral-config-schema.json
+15
-1
@@ -3994,7 +3994,17 @@
3994
"customer_id": {
3995
"type": "string",
3996
"description": "REQUIRED IF USING GROUPS: Customer ID from Google Workspace Admin Console (https://admin.google.com/ac/accountsettings/profile)"
3997
- }
3997
+ },
3998
+ "authorities": {
3999
+ "type": [
4000
+ "array"
4001
+ ],
4002
+ "description": "Default value is groups. Roles can be used in azure to assign app permissons",
4003
+ "enum": [
4004
+ "groups",
4005
+ "roles"
4006
+ ]
4007
+ }
4008
}
4009
},
4010
"groups": {
@@ -4049,6 +4059,10 @@
4059
"description": "Custom claim to use."
4060
}
4061
}
4062
+ },
4063
+ "options": {
4064
+ "type": "object",
4065
+ "description": "Custom options to pass to the OpenID Client"
4066
}
4067
}
4068
}
sample-config-advanced.json
+5
@@ -582,6 +582,11 @@
582
"filter": [ "groupB", "groupC" ]
583
}
584
},
585
+ "options" : {
586
+ "params": {
587
+ "prompt": "select_account"
588
+ }
589
+ },
590
"newAccounts": true,
591
"_newAccountsRights": [ "nonewgroups", "notools" ],
592
"_newAccountsUserGroups": [ "ugrp//xxxxxxxxxxxxxxxxx" ]
webserver.js
+42
-19
@@ -8074,13 +8074,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8074
strategy.options.params = Object.assign(strategy.options.params || {}, { 'scope': ['openid', 'profile', 'email'] });
8075
}
8076
if (typeof strategy.groups == 'object') {
8077
- let groupScope = strategy.groups.scope || null
8078
- if (groupScope == null) {
8079
- if (preset == 'azure') { groupScope = 'Group.Read.All' }
8080
- if (preset == 'google') { groupScope = 'https://www.googleapis.com/auth/cloud-identity.groups.readonly' }
8081
- if (typeof preset != 'string') { groupScope = 'groups' }
8077
+ strategy.custom.authorities = obj.common.convertStrArray(strategy.custom.authorities, ' ')
8078
+ // Check if authorities does not exist or includes groups
8079
+ if((Array.isArray(strategy.custom.authorities) && strategy.custom.authorities.filter(x => x.trim().length > 0).length > 0) == false || strategy.custom.authorities.includes('groups')) {
8080
+ let groupScope = strategy.groups.scope || null
8081
+ if (groupScope == null) {
8082
+ if (preset == 'azure') { groupScope = 'Group.Read.All' }
8083
+ if (preset == 'google') { groupScope = 'https://www.googleapis.com/auth/cloud-identity.groups.readonly' }
8084
+ if (typeof preset != 'string') { groupScope = 'groups' }
8085
+ }
8086
+ strategy.options.params.scope.push(groupScope)
8087
+ parent.authLog('setupDomainAuthStrategy', `OIDC: Groups sync enabled, added group scope to request: ${strategy.options.params.scope}`);
8088
}
8083
- strategy.options.params.scope.push(groupScope)
8089
}
8090
strategy.options.params.scope = strategy.options.params.scope.join(' ')
8091
@@ -8228,8 +8233,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8233
}
8234
}
8235
8231
- // If profile is null/undefined, extract user info from the tokenset
8232
- if (!profile && tokenset && tokenset.id_token) {
8236
+ // If profile is null/undefined or roles are requested, extract user info from the tokenset
8237
+ if ((!profile || (strategy.custom.authorities.includes('roles')) && !profile.roles) && tokenset && tokenset.id_token) {
8238
try {
8239
// Simple JWT decoder to extract user claims from id_token
8240
const parts = tokenset.id_token.split('.');
@@ -8238,7 +8243,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8243
const paddedPayload = payload + '='.repeat((4 - payload.length % 4) % 4);
8244
const decoded = JSON.parse(Buffer.from(paddedPayload, 'base64').toString());
8245
if (decoded) {
8241
- profile = decoded;
8246
+ if(!profile){
8247
+ profile = decoded;
8248
+ } else {
8249
+ profile.roles = decoded.roles;
8250
+ }
8251
}
8252
}
8253
} catch (err) {
@@ -8277,6 +8286,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8286
8287
user.emailVerified = profile && profile.email_verified ? profile.email_verified : obj.common.validateEmail(user.email);
8288
user.groups = profile && obj.common.validateStrArray(profile.groups, 1) ? profile.groups : null;
8289
+ user.roles = profile && obj.common.validateStrArray(profile.roles, 1) ? profile.roles : null;
8290
user.preset = obj.common.validateString(strategy.custom.preset) ? strategy.custom.preset : null;
8291
if (strategy.groups && obj.common.validateString(strategy.groups.claim)) {
8292
user.groups = profile && obj.common.validateStrArray(profile[strategy.groups.claim], 1) ? profile[strategy.groups.claim] : null
@@ -8295,16 +8305,29 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8305
8306
// Setup presets and groups, get groups from API if needed then return
8307
if (strategy.groups && typeof user.preset == 'string') {
8298
- getGroups(user.preset, tokenset).then((groups) => {
8299
- user = Object.assign(user, { 'groups': groups });
8300
- done(null, user);
8301
- }).catch((err) => {
8302
- let error = new Error('OIDC: GROUPS: No groups found due to error:', { cause: err });
8303
- parent.debug('error', `${JSON.stringify(error)}`);
8304
- parent.authLog('oidcCallback', error.message);
8305
- user.groups = [];
8306
- done(null, user);
8307
- });
8308
+ if((Array.isArray(strategy.custom.authorities) && strategy.custom.authorities.filter(x => x.trim().length > 0).length > 0) == false || strategy.custom.authorities.includes('groups')) {
8309
+ getGroups(user.preset, tokenset).then((groups) => {
8310
+ user = Object.assign(user, { 'groups': groups });
8311
+ //done(null, user);
8312
+ }).catch((err) => {
8313
+ let error = new Error('OIDC: GROUPS: No groups found due to error:', { cause: err });
8314
+ parent.debug('error', `${JSON.stringify(error)}`);
8315
+ parent.authLog('oidcCallback', error.message);
8316
+ user.groups = [];
8317
+ //done(null, user);
8318
+ });
8319
+ }
8320
+ if(strategy.custom.authorities.includes('roles')){
8321
+ if(user.roles){
8322
+ if(!strategy.custom.authorities.includes('groups')){
8323
+ user.groups = user.roles;
8324
+ } else {
8325
+ user.groups = (user.groups || []).concat(user.roles);
8326
+ }
8327
+ }
8328
+ }
8329
+ parent.authLog('OIDC: USER GROUPS/ROLES:', user);
8330
+ done(null, user);
8331
} else {
8332
done(null, user);
8333
}