Topic on Extension talk:OpenID Connect

Summary by Cindy.cicalese

Group synchronization is supported in PluggableAuth 7.0.0/OpenID Connect 7.0.0. See documentation at Extension:PluggableAuth#Group Synchronization.

ShaunONeil (talkcontribs)

Is there any possibility to map oidc roles to wgGroupPermissions? I see I can add roles to the scopes on the request side, but I can't see a good spot to consume them .

Cindy.cicalese (talkcontribs)
ShaunONeil (talkcontribs)

It's difficult to ignore timing like that! Unfortunately I'm not clear on what value is expected to be configured as 'property', especially in relation to what(?) these 'wiki_roles' and 'global_roles' strings are (in populateGroups). They're passed off into a maze of array shifting that loses me like a circus cup game.


Would it be possible to see an example of the usage of this 'property' configuration and the corresponding json from the access code? I've tried setting 'property' to 'groups' and putting my role list in a claim groups, groups.global_roles, groups.wiki_roles, groups.global_roles.wiki_roles .. It looks like it's so close but just not clicking.

Cindy.cicalese (talkcontribs)

I've asked the patch author to comment here. I haven't had a chance to test the patch yet, so I don't know what the configuration is to be.

Heinebold (talkcontribs)

Hello @ShaunONeil, sorry for the weird array destructuring logic. It's due to the fact that I wanted it to work without caring about whatever nested structure the token json contains and if it gets deserialized as php arrays or objects.

Also sorry for how the code snippets below look, either this comment field or I are too dumb for this, but the important parts are formatted as expected.


I defined separate "wiki_roles" and "global_rules" because Keycloak, which I use, produces a token structure like this with both client-specific and global user roles.

For tokens looking like this: {

 "typ": "Bearer",
 // all the other stuff,
 "realm_access": {
   "roles": [
     "admin",
     "jedi_master"
   ]
 },
 "resource_access": {
   "wiki": {
     "roles": [
       "editor",
       "admin"
     ]
   },
   "other.client": {
     "roles": [
       "manage-account",
       "manage-account-links",
       "view-profile"
     ]
   }
 }

} and assuming you're interested in the user's global roles and those specific to the "wiki" client, the OIDC Plugin config for your issuer should contain this:

$wgOpenIDConnect_Config['<your issuer>'] = [

   'clientID' => '...',
   // config as documented,
   'global_roles' => ['property' => ['realm_access', 'roles']],
   'wiki_roles' => ['property' => ['resource_access', 'wiki', 'roles']]





];

As a result, your user will have the following roles: oidc_admin, oidc_jedi_master, oidc_editor.

These roles are prefixed with oidc_ so that when logging in again with a different token, the plugin will know which roles to modify. Since these roles are coming from the token and thus are subject to change from outside your wiki, you might not want to directly assign rights to them, but rather use them in autopromote conditions. This allows you more easily to adapt to potential changes in your OIDC Issuer's roles.

If you want to add an additional prefixes to either global or wiki-specific roles, you can configure it like this:

$wgOpenIDConnect_Config['<your issuer>'] = [

   'clientID' => '...',
   // config as documented,
   'global_roles' => [
       'property' => ['realm_access', 'roles'],
       'prefix' => 'global'
   ],
   'wiki_roles' => ['property' => ['resource_access', 'wiki', 'roles']]





];
, resulting in these roles: oidc_globaladmin, oidc_globaljedi_master, oidc_editor, oidc_admin.

In this example, the user had only one "oidc_admin" role without an extra prefix, but two distinct roles now.


I will make full public documentation for this once the patch has been accepted. Until then, feel free to ask more questions here.

ShaunONeil (talkcontribs)

Thanks both@Heinebold for the help so far!

It seems my configuration was a red herring (although it did need clarification, I had 'property'=>'nameofclaim', not global_roles=....)

However I seem to be hitting an entirely different issue. Many wfDebugLog later, I appear to be hitting line 371 in https://gerrit.wikimedia.org/r/#/c/mediawiki/extensions/OpenIDConnect/+/572199/2/src/OpenIDConnect.php

This causes getAccessToken() to return null, which causes line 314 to throw an exception, and without a sensible $config, we go downhill rapidly from there.

MW is release 1.34.0, PluggableAuth is master-4d68263 (9-feb), and oidc obviously 04dfc44 from this commit. I'm also testing against Keycloak 9.0.0.

Heinebold (talkcontribs)

Oh, yes I should have put a null check before line 314, thanks. Thats what you get with happy path testing, I should really have been more careful.

As for the problem in 371, I might know an explanation and solution:

Could you compare the iss claim of your access token to the issuer you are using in the $wgOpenIDConnect_Config please? They need to match exactly, so you might be having trouble with a trailing slash or something like it, that's what happened to me.
I was thinking of somehow normalizing it, but putting heuristics in a security setting didn't sound wise. You should be able to just copy the issuer from the token into your config, but all users already associated with it will be broken. You can edit the database table, though, to change the issuer there as well.

ShaunONeil (talkcontribs)

Spot on with the trailing slash! My config has one, my access token doesn't. (This is all in a scratch VM at the moment, so my users won't mind!) And now the magic word, "oidc_testrealmrole". Thank you so much - I was expecting advice on which function I would be best to hook into, and instead got 95% of the solution on a sliver platter.

Re the commit, if I had any suggestions at all, it'd be the use of wbDebugLog() .. this has proven valuable to a process that's difficult to snoop. Otherwise, I'm over the moon right now - this is exactly what I needed.

Cindy.cicalese (talkcontribs)

Wow, what more could I ask for? A patch to add a new feature and another user to test it!

I plan to test this myself. Have you tested with other identity providers than Keycloak? Do you anticipate that it would work with others?

ShaunONeil (talkcontribs)

Keeping in mind I'm less than a week into this project, and oidc in general, so I'm not a voice of experience:

I had an unsuccessful attempt with GitLab (community-edition, self-hosted) as I had it handy and I figured it's the least I can do. I did note the documentation on gitlab as an oidc provider states "Only the sub and sub_legacy claims are included in the ID token, all other claims are available from the /oauth/userinfo endpoint used by OIDC clients". I'm not 100% on whether "ID token" and "Access token" are the same thing, but this sounds very relevant. (I could also be way off, I've been unsuccessful in capturing the JWT to inspect.)


So this may be a limitation; it uses only the access token, and not the userinfo endpoint. In Keycloak, emitting claims in the token is a yes/no option for each claim. There's a lot less knobs to twiddle in GitLab.

This does appear to be the only real limitation, however. As long as the user (the one installing, not the end-user) can discover the names & members of the appropriate odic claim, it does seem appropriate.


The only other possible limitation I discovered is that in Keycloak, the "Claim JSON Type" is a configurable option (default string, options json/int/bool/etc). I discovered json is very much not an acceptable option, it results in Pluggable's big red generic Fatal message. I'm not in a position to evaluate whether this is a problem, hurdle, or irrelevant for other providers.

I have to thank you both for your work here; I started down this path on Friday, had Authentication working on Saturday, and Authorization working on Tuesday. This is a fantastic result far beyond expectations for me!

Heinebold (talkcontribs)

I admit I haven't tested with other providers, and that I forgot that the roles might as well be only in the user info. I just needed the feature and thought, hey, I'm probably not alone, let's share it.

For querying the user info during group population, I'll have to have a closer look to the jumbojett lib and whether I can use it for this without restarting the authentication flow.

I'll definitely add that missing null check and some logging, if another change is ok with you @Cindy.cicalese

Cindy.cicalese (talkcontribs)

Of course, please feel free to continue working on it. In the meantime, I will try to find time to test. I usually use Google as my identity provider, so I'd like to see if there is a way to take advantage of this functionality in that environment.

Cindy.cicalese (talkcontribs)

Sorry I didn't have a chance to test this before, but I did just rebase and reformat it and tested the result. It works fine for my case with no roles defined. I'm still planning to poke around and see if I can make it work with another provider. But, in the meantime, are you both happy with the code as it currently exists? If so, I will go ahead and merge it. Then, if you could please add the documentation to the extension page, @Heinebold, that would be great. Thanks!

Heinebold (talkcontribs)

I am definitely happy with the current state, as I tailored it to my needs ;-) but I saw that there have been some changes on master that should be incorporated into this, which are not solved by a simple rebase alone.

I'll do these changes and commit them, so you can just merge it whenever you like.

As for getting the roles from userInfo instead of only the token, I'd say this will better be a separate change so we can get the basic functinality going.

Cindy.cicalese (talkcontribs)

OK, makes sense. Thank you.

Imakarum (talkcontribs)

Hi,

I know this is an old thread... But are there any plans to merge this into master? The functionality would be very helpful for us...

Thanks!

HWorblehat (talkcontribs)

Also bumping this thread as it would be useful for me too.


Is there anything useful I can do to help move things along?

Cindy.cicalese (talkcontribs)

I was waiting to get confirmation that the changes @Heinebold wanted to make to the patch are complete. If that is the case, and considering that several people have tested the patch successfully, I'm inclined to move forward and merge the patch. Can I get somebody to commit to update the extension page with an example of the new functionality once it is merged? Thanks!

Heinebold (talkcontribs)

Sorry for not having worked on this. The processes of "Gerrit" had me so confused that I had originally given up trying to incorporate the upstream changes, as I wasn't even sure if they were really incompatible. I wanted to take a look later on - and then completely forgot...

So if it is not breaking things, I'm more than happy if it is merged.

This post was hidden by Cindy.cicalese (history)
Cindy.cicalese (talkcontribs)

I *finally* had a chance to merge the change. It would be great if one of you all could add documentation for the new feature to the extension page. Thank you!

Kghbln (talkcontribs)

I guess we are talking about this commit. Looks like an exciting feature to me. Yeah, having a docu for this will be cool. After this we are probably ready for a new release. :)

Cindy.cicalese (talkcontribs)

I'm currently working on a new release that will be compatible with an upcoming release of PluggableAuth. I will likely be refactoring the code introduced by the patch, so testing of the feature in the new release would be much appreciated when it is ready. And, of course, documentation.

Anollamh (talkcontribs)

Hi @Cindy.cicalese, are the changes in the newest releases of the plugins? I tried to use the roles with keycloak, no luck.

Specifically, I assigned the role sysop to a dedicated account in keycloak and put the configuration from this thread to LocalSettings.php, but the change doesn't show in preferences.

Or is there anything more to be done to configure it?

Cindy.cicalese (talkcontribs)

Yes, the code exists in version 6.0, but it was not tested since I do not have an environment set up to do so at present. I have plans to do so, but I'm not certain of the timing. It would be great to have some testing and also documentation added for the feature. It is possible that the significant code changes in version 6.0 broke something, and I would definitely accept bug reports and/or patches.

Cindy.cicalese (talkcontribs)

I tested and confirmed that it does work in version 6.0. I did notice, though, that the instructions on the extension page had a "/" at the end of the providerURL, which caused issues. (Keycloak responds in the access token with the issuer without the trailing slash, causing a check in the code to fail.) I fixed the instructions (and may also remove that check in the code if it turns out it is unnecessary). But, regardless, as long as you don't have the trailing "/" on the providerURL, it should work.

Anollamh (talkcontribs)

Thank you! I didn't have the trailing "/", though, it doesn't work for us.

In Keycloak for <admin_user>, we have:

"<wiki_client>": { "roles": [ "sysop", "wikiAccess" ] },

and in LocalSettings.php:

$wgOpenIDConnect_Config['path/to/keycloak'] = [

'clientID' => '<wiki_client>',

'clientsecret' => '<#hash>',

'scope' => [ 'openid', 'profile', 'email' ],

'global_roles' => ['property' => ['realm_access', 'roles']],

'wiki_roles' => ['property' => ['resource_access', '<wiki_client>', 'roles']]

];

After login, the <admin_user> is in these groups (as per Special:Preferences):

Autoconfirmed users, User

From what I understand this should give <admin_user> the right to e. g. delete (which I suppose would be under "more" scroll button, I don't know how else it would be confirmed).

Am I missing anything? Or should there be role "admin" as in the example above?

Cindy.cicalese (talkcontribs)

What version of PluggableAuth and OpenID Connect are you using? You are not using the version 6.0 config, but you mentioned using the newest release.

Anollamh (talkcontribs)

Right, I am actually using the "latest stable" versions for mediawiki 1.37, i. e. 5.*.

Does it mean that this config syntax in docs is for newest version? I was confused about that...

$wgPluggableAuth_Config[] = [

'plugin' => 'OpenIDConnect',

'data' => [...

I'll check the newest plugins and let you know.

Cindy.cicalese (talkcontribs)

Yes, the syntax in the examples has been updated to the newest version. Maybe both versions should be included.

Anollamh (talkcontribs)

I tested the versions 6 and the new configuration style, and I can se the prefixed roles assigned to users, however, even though my admin user has "oidc_sysop" role assigned, which I believe should give him admin rights, it doesn't. (at least I cannot see a delete article option)

Is it supposed to work like that, or is assigning permissions/rights to groups/roles separate task?

Cindy.cicalese (talkcontribs)
CodyRWhite (talkcontribs)

Hi, we are rolling OIDC connections across multiple internal wiki's and I would really love to use this feature with Azure. However when I go to Extension:OpenID Connect - MediaWiki I am not seeing any documentation. The following appears to be a good base, but I am un sure what <wiki_client> is replaced with.


$wgOpenIDConnect_Config['path/to/keycloak'] = [

'clientID' => '<wiki_client>',

'clientsecret' => '<#hash>',

'scope' => [ 'openid', 'profile', 'email' ],

'global_roles' => ['property' => ['realm_access', 'roles']],

'wiki_roles' => ['property' => ['resource_access', '<wiki_client>', 'roles']]

That configuration also looks different from what I am use.

$wgPluggableAuth_Config[] = [

    'plugin' => 'OpenIDConnect',

    'data' => [

        'providerURL' => 'login.microsoftonline.com/<tenantID>/v2.0/',

        'clientID' => '<clientID>',

        'clientsecret' => '<clientSecret>'

    ]

];

My guess is something like this

$wgPluggableAuth_Config[] = [

    'plugin' => 'OpenIDConnect',

    'data' => [

        'providerURL' => 'login.microsoftonline.com/<tenantID>/v2.0/',

        'clientID' => '<clientID>',

        'clientsecret' => '<clientSecret>'

'global_roles' => ['property' => ['realm_access', 'roles']],

'wiki_roles' => ['property' => ['resource_access', '<wiki_client>', 'roles']]

    ]

];


Then in my App registation in azure I would add groups to the application users and groups and assign each group a role that is created to match the following.

Administrators

Users

CustomRole1


Then as long as I had these roles configured with permissions (custom roles) they should be assigned at user creation. Would the roles be added after the fact if the user was already created. Say a user changes groups for more permissions?


Thanks!


CodyRWhite (talkcontribs)

I did some investigation. I think I figured out what I need for Azure but the plugin keeps exiting before it generates the group information.

In OpenIDConnectUserGroupManager.php

$this->authManager->getAuthenticationSessionData( OpenIDConnect::OIDC_ACCESSTOKEN_SESSION_KEY );

keeps returning null

[OpenIDConnect] Found user with matching subject and issuer.

[OpenIDConnect] No access token found for user


Still investigating, but I hope someone know what's missing.

Cindy.cicalese (talkcontribs)

> Would the roles be added after the fact if the user was already created. Say a user changes groups for more permissions?

Yes. The roles are synced at every login.

CodyRWhite (talkcontribs)

@Cindy.cicalese Thanks for the clarification on that.

Would you happen to know why my Session data is returning null?


Authentication and user creation works without errors, but it appears when the script moves to the UserGroupManager something is missing.

I added the following debug on line 217 in OpenIDConnect.php

$payload = $this->authManager->getAuthenticationSessionData( OpenIDConnect::OIDC_ACCESSTOKEN_SESSION_KEY );

$this->logger->debug( 'AccessTokenPayload: ' . $payload . PHP_EOL );


Debug log shows

[OpenIDConnect] Jumbojett\OpenIDConnectClientException: Unable to determine state in /var/www/public_html/extensions/OpenIDConnect/vendor/jumbojett/openid-connect-php/src/OpenIDConnectClient.php:317


Looks like its failing on

// Do an OpenID Connect session check

            if ($_REQUEST['state'] !== $this->getState()) {

                throw new OpenIDConnectClientException('Unable to determine state');

            }


I think I might be missing something in my ODIC configuration or on the azure side but I am not sure which.

[PluggableAuth] In execute()

[PluggableAuth] Getting PluggableAuth instance

[PluggableAuth] Plugin name: OpenIDConnect

[OpenIDConnect] Redirect URL: https:// <site>/wiki/Special:PluggableAuthLogin

[PluggableAuth] In execute()

[PluggableAuth] Getting PluggableAuth instance

[PluggableAuth] Plugin name: OpenIDConnect

[OpenIDConnect] Redirect URL: https:// <site>/wiki/Special:PluggableAuthLogin

[OpenIDConnect] Real name: xxxxx Email: xxxxx, Subject: Hxxxx, Issuer: https://login.microsoftonline.com/xxxx/v2.0/

[OpenIDConnect] Found user with matching subject and issuer.

[PluggableAuth] Authenticated existing user: Cody White

[PluggableAuth] Getting PluggableAuth instance

[PluggableAuth] Plugin name: OpenIDConnect

[PluggableAuth] Instance already exists

[OpenIDConnect] No access token found for user

[PluggableAuth] User is authorized.

CodyRWhite (talkcontribs)

I spent some more time looking into this. The error message was throwing me for a loop. The module is throwing "No access token found for user". I just assumed $accessToken was null.

It looks like, $this->store->findUser( $accessToken->sub, $accessToken->iss ) is not locating a user.

Where can I find documentation on where that store is. When I look at the openid_connect table in the database the oidc_subject does not match what we are getting from $accessToken->sub.

when I var_dump $this->store

OIDC_Store:object(MediaWiki\Extension\OpenIDConnect\OpenIDConnectStore)#536 (0) { }

Cindy.cicalese (talkcontribs)

I'm happy to announce that PluggableAuth version 7.0.0 and OpenID Connect version 7.0.0 have been released with official support for group synchronization. The syntax is slightly different from what is described above, but it should support all of the use cases discussed and more. Please see the PluggableAuth group synchronization documentation for more information.