-
Notifications
You must be signed in to change notification settings - Fork 606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to openid client #5856
Migrate to openid client #5856
Conversation
Have you verified this works with 1.0.21? |
Yes, this was updated to 1.1.21 and then tested as described above.
This should be a drop in replacement for existing users as described in the documentation.
Technically I didn't remove any items, they were just re-arranged and modified. Existing configs will work and are handled correctly with no user intervention required. |
Can this be merged? I want to test this with Azure and if necessary build upon this. |
Feel free to test this from my repository directly (migrate_to_openid-client branch). There is also a docker available at mstrhakr/meshcentral:dev |
@mstrhakr I am already building on top of that, I have Azure groups working by reading them from graph api. Btw, there is some bad merge/rebase: + const strategy = domain.authstrategies[req.user.strategy];
const groups = { 'enabled': typeof strategy.groups == 'object' }
if ((req.user != null) && (req.user.sid != null) && (req.user.strategy != null)) {
- const strategy = domain.authstrategies[req.user.strategy]; Strategy is used before it is defined. I had to move that line. |
After this is oked and accepted, I will make pull with for Azure groups. |
I already had that as a feature of this build.
Thanks! I somehow missed that. Correct me if I'm wrong but I think this would make more sense. - const groups = { 'enabled': typeof strategy.groups == 'object' }
if ((req.user != null) && (req.user.sid != null) && (req.user.strategy != null)) {
const strategy = domain.authstrategies[req.user.strategy];
+ const groups = { 'enabled': typeof strategy.groups == 'object' } |
@mstrhakr Sure, that works as well. My issue is just that strategy is used before it is defined. |
Otherwise, everything worked for me. For groups in Azure, I basically just appropriately fill |
I am testing it with my Azure changes, and so far everything seems ok. |
What changes are you making? I only ask because groups are already supported with this branch. |
Not for Azure SSO. You support everything, but you don't actually read groups from Azure AD. |
This is the Azure code: // Azure
if ((typeof domain.authstrategies.azure == 'object') && (typeof domain.authstrategies.azure.clientid == 'string') && (typeof domain.authstrategies.azure.clientsecret == 'string')) {
const AzureOAuth2Strategy = require('passport-azure-oauth2');
let options = { clientID: domain.authstrategies.azure.clientid, clientSecret: domain.authstrategies.azure.clientsecret, tenant: domain.authstrategies.azure.tenantid };
if (typeof domain.authstrategies.azure.callbackurl == 'string') { options.callbackURL = domain.authstrategies.azure.callbackurl; } else { options.callbackURL = url + 'auth-azure-callback'; }
parent.authLog('setupDomainAuthStrategy', 'Adding Azure SSO with options: ' + JSON.stringify(options));
passport.use('azure-' + domain.id, new AzureOAuth2Strategy(options,
function (accessToken, refreshtoken, params, profile, done) {
var userex = null;
try { userex = require('jwt-simple').decode(params.id_token, '', true); } catch (ex) { }
parent.authLog('setupDomainAuthStrategy', 'Azure profile: ' + JSON.stringify(userex));
var user = null;
if (userex != null) {
var user = { sid: '~azure:' + userex.unique_name, name: userex.name, strategy: 'azure' };
if (typeof userex.email == 'string') { user.email = userex.email; }
}
return done(null, user);
}
));
authStrategyFlags |= domainAuthStrategyConsts.azure;
} As you can see, there is no groups handling - no groups are read from Azure AD, what is actually needed is: - let options = { clientID: domain.authstrategies.azure.clientid, clientSecret: domain.authstrategies.azure.clientsecret, tenant: domain.authstrategies.azure.tenantid };
+ let options = { clientID: domain.authstrategies.azure.clientid, clientSecret: domain.authstrategies.azure.clientsecret, tenant: domain.authstrategies.azure.tenantid, resource: 'https://graph.microsoft.com' }; Implement a function that accesses graph API using the acceesToken to get - function (accessToken, refreshtoken, params, profile, done) {
+ async function (accessToken, refreshtoken, params, profile, done) {
+ async function fetchUserGroups(accessToken) {
+ const url = 'https://graph.microsoft.com/v1.0/me/memberOf';
+ try {
+ const response = await require('axios').get(url, {
+ headers: {
+ 'Authorization': `Bearer ${accessToken}`,
+ 'Content-Type': 'application/json'
+ }
+ });
+ const groups = response.data.value.map((g) => g.displayName);
+ return groups;
+ } catch (error) {
+ parent.authLog('setupDomainAuthStrategy', 'Error: ' + error)
+ return [];
+ }
+ } Call the function: - parent.authLog('setupDomainAuthStrategy', 'Azure profile: ' + JSON.stringify(userex));
+ const groups = await fetchUserGroups(accessToken)
+ parent.authLog('setupDomainAuthStrategy', 'Azure profile: ' + JSON.stringify(userex) + ' ' + JSON.stringify(groups)); Fill groups in user struct with the result: - var user = { sid: '~azure:' + userex.unique_name, name: userex.name, strategy: 'azure' };
+ var user = { sid: '~azure:' + userex.unique_name, name: userex.name, strategy: 'azure', groups: groups }; It has new axios dependency (something else can be used as well). @mstrhakr I hope this makes sense. |
Yea that's probably easier than how I accomplished it. Check out my getGroups function. We may be able to reuse it for the Azure strategy. However this version of the OIDC strategy includes an Azure preset that does already pull groups from the Graph API, although I manually made the request instead of using another module. It would not work for existing azure users though as it uses a different identifier for the users and there is currently no code to migrate the users to the new identifier. |
I see, but does this work, is it enabled? I am using your PR, but doesn't fetch groups for me. I can debug to see what is wrong. |
Maybe I just don't know how to set it up, that code is never called if I use Azure Strategy:
|
} else if ((typeof strategy.issuer.issuer == 'string') && (typeof strategy.custom.preset == 'string')) {
let error = new Error(`OIDC: PRESET: ${strategy.custom.preset.toUpperCase()}: PRESET OVERRIDDEN: CONFIG ISSUER: ${strategy.issuer.issuer} PRESET ISSUER: ${presetIssuer}`);
parent.authLog('setupDomainAuthStrategy', error.message);
console.warn(error)
} This code is erroneous, produces |
Check out the documentation I made for the presets. I didn't modify the code for the Azure strategy, I added a preset configuration to the oidc strategy for azure that does what you want already. Its possible we could modify that getGroups function to work for the Azure strategy as well but currently it wouldn't be a drop in. Basically you'd need to use the oidc strategy and enable the azure preset within it to use groups as is. |
Trying to get it to work. This code: // Discover additional information if available, use endpoints from config if present
let issuer
try {
issuer = await strategy.obj.openidClient.Issuer.discover(strategy.issuer.issuer);
} catch(err) {
let error = new Error('OIDC: Discovery failed.', { cause: err });
parent.authLog('setupDomainAuthStrategy', `ERROR: ${JSON.stringify(error)} ISSUER_URI: ${strategy.issuer.issuer}`);
throw error
} finally {
if (Object.keys(strategy.issuer).length > 1) {
parent.authLog('setupDomainAuthStrategy', `OIDC: Adding Issuer Metadata: ${JSON.stringify(strategy.issuer)}`);
issuer = new strategy.obj.openidClient.Issuer(Object.assign(issuer.metadata, strategy.issuer));
}
strategy.issuer = issuer.metadata
strategy.obj.issuer = issuer
} Can't work, finally accesses |
In "oidc": {
"issuer": {
"issuer": "https://sso.server.com",
"end_session_endpoint": "https://sso.server.com/logout"
},
"client": {
"clientid": "00000000-0000-0000-0000-000000000000",
"clientsecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"groups": {
"required": [ "groupA", "groupB", "groupC" ],
"siteadmin": [ "groupA" ],
"sync": {
"enable": true,
"filter": [ "groupB", "groupC" ]
}
},
"newAccounts": true
} |
And this from docs: "oidc": {
"client": {
"client_id": "a1gkl04i-40g8-2h74-6v41-2jm2o2x0x27r",
"client_secret": "AxT6U5K4QtcyS6gF48gndL7Ys22BL15BWJImuq1O"
},
"custom": {
"preset": "azure",
"tenant_id": "46a6022g-4h33-1451-h1rc-08102ga3b5e4"
}
} Will not work as it fails on missing
|
So, no it will actually work like this: "oidc": {
"client": {
"client_id": "...",
"client_secret": "..."
},
"groups": {
"required": [ "...", "..." ],
"siteadmin": [ "..." ],
"sync": {
"enable": true,
"filter": [ "...", "..." ]
}
},
"custom": {
"preset": "azure",
"tenant_id": "..."
},
"newAccounts": true,
"callbackURL": "..."
}, |
I added notes in the PR code, see that. Testing it. Looks good. So the actual idea is to phase auth old auth strategies and use oidc. |
…t#5697 Signed-off-by: si458 <simonsmith5521@gmail.com>
6d1174d
to
1af71b2
Compare
Ok. I am looking into this today. Lots of changes, but the PR quality looks very good. This may break existing installations, however, it's a good update. I need to read this line-by-line and will approve soon if no issues. |
Thank you very much, I will be testing it after it is merged and if anything out of ordinary is found, I will sure report it. |
Yes basically, I'd like to do the same with saml via node-saml eventually. That should cover 99% of use cases between the two of them. |
This has basically been rewritten since you saw it the first time. Almost all of the code for the other strategies is essentially untouched to hopefully ensure no issues with the other strategies. I had to make web server startup asynchronously to get the discovery to work correctly but I think it is worth it for be able to get groups from the API's and such. I've said it before but I'm not a real programmer, I'm just learning and working on things that I find interesting so hopefully there aren't too many major oversights in this. Anyway, I really appreciate you taking another look at this after all this time. |
* Create forksync.yml * update oidc to use openid-client * update oidc module requirements * working oidc+ includes all oauth2 clients automatically migrated. azure will need some kind of fix for the uid * update openid-client install checks * created overarching schema for OIDC * bug fixs for azure login * update schema prepare schema for unified oidc module * update 'oidc' to strategy variable * working azure+ groups groups from azure are in, you can use memberOf or transitiveMemberOf in config (Graphs API) * clean up old config import + working google oidc previous config map was recursive nonsense, changed to multiple IFs * added convertStrArray * de-expanded scope put all other auth strategies back to normal and fixed oidc strategy * swap back to using authlog debugger * Update meshcentral-config-schema.json * working google oidc + groups * working azure+groups (again) * init oidc docs very incomplete but basic config is present * add oidc * more work on docs * add scope and claim options plus fixed a few bugs and faults in my logic used logs correctly * further cleanup debug * more debug cleanup * continue documentation push fixed minor debug bugs also * more work on docs missing links, need to get azure preset docs, probably more. * done with docs its good enough for now * minor fix + presets get correct icon * fix google oidc not visible at login * fix bug with emailVerified property * fix logout bug + debug cleanup * fix strategy logout bug +cleanup * fixed preset login icon * fix alert + fix schema * terminate lines * Dutch language update 1.0.85 line up polish translation * Fixed guest web relay session revocation (Ylianst#4667) * Updated French translation. * Add hook to allow adding custom api endpoints to Express routing * Updated German translation. * Update meshcentral-config-schema.json (change formatting) This way it is easier to edit and maintain * Fixed schema. * fix meshcentral-config-schema.json * add language selector to login (Ylianst#5648) * add language selector to login * add showLanguageSelect to pick top or bottom boxe * remove additionalProperties: false in schema to allow comments Ylianst#5697 Signed-off-by: si458 <simonsmith5521@gmail.com> * fix notes in docs * Fix web relay session handling and redirection due to bad merge * Added option to check HTTP origin. * add links and fix typo * move groups after strategy * Update version split in docs * Fix preset issuer URL in OIDC strategy * Update clientid and clientsecret to client_id and client_secret * Update meshcentral-config-schema.json and fix bad rebase * Update meshcentral-config-schema.json * fix bad rebase * fix bad rebase * Add 'connect-flash' to passport dependencies * Remove unnecessary passport dependencies - fix bad rebase * Fix auth strategy bug and remove console.log statement * Set groupType to the preset name if it exists, otherwise use the strategy name * remove finally block from * Refactor authentication logging in handleStrategyLogin to include strategy name --------- Signed-off-by: si458 <simonsmith5521@gmail.com> Co-authored-by: petervanv <58996467+petervanv@users.noreply.github.com> Co-authored-by: Ylian Saint-Hilaire <ysainthilaire@hotmail.com> Co-authored-by: Martin Mädler <martin.maedler@gmail.com> Co-authored-by: Fausto Gutierrez <28719096+faustogut@users.noreply.github.com> Co-authored-by: Simon Smith <simonsmith5521@gmail.com>
This is merge ready. I updated this to 1.1.21 then I tested it with Authelia (custom setup and with old configs) as well as Azure using the preset. This doesn't interfere with any old configuration and adds a bunch of cool features. I have again published the latest version of this on docker hub (
mstrhakr/MeshCentral:dev
) and tested that as well.@Ylianst if you have any questions about this, please let me know. Although I wrote some documentation that you can reference as well.
Fixes #4545
Fixes #5179
Fixes #4531
Fixes #5725