Problem/Motivation
When editing and saving a user, the hook simplesamlphp_auth_user_form_submit() calls $externalauth->linkExistingAccount() in order to ensure that an entry in the authmap table exists, and passes the Drupal user's username as $authname by default.
This makes sense in most cases, but after #2910506: Usernames (authnames) do not update in authmap table changed the behavior of the linkExistingAccount() method in External Authentication v2.0.3, you might lose sync between SAML and Drupal users if you don't use the default settings.
Steps to reproduce
- Configure "uid" for
unique_identifieras and "displayName"usernamefor . - Log in with with SAML User "John Doe"; a Drupal user "John Doe" will get created; the corresponding authname is the UID "john_doe".
- Edit and save that user in Drupal, e.g. because you want to assign some roles.
- The authname is now "John Doe", and further login attempts will result in a second Drupal user created, since no user with authname "john_doe" is found.
Proposed resolution
Not sure yet.
The described behavior is being caused by the following code in simplesamlphp_auth_user_form_submit():
// Link an authmap entry to this account, if not yet existing.
// By default, we use the username as authname.
// This can be altered if needed. See simplesamlphp_auth.api.php for
// details.
$authname = $account->getAccountName();
\Drupal::modulehandler()->alter('simplesamlphp_auth_account_authname', $authname, $account);
$externalauth->linkExistingAccount($authname, 'simplesamlphp_auth', $account);
A possible workaround is to provide an alter hook:
/**
* Implements hook_simplesamlphp_auth_account_authname_alter().
*/
function mymodule_simplesamlphp_auth_account_authname_alter(&$authname, UserInterface $account): void {
$authmap = \Drupal::service('externalauth.authmap');
// Check whether an entry for the account already exists in the authmap table.
// If so, change the authname to the value already stored there
$current_authname = $authmap->get($account->id(), 'simplesamlphp_auth');
if ($current_authname) {
$authname = $current_authname;
}
}
But of course, this is not the real solution.
The above logic in simplesamlphp_auth_user_form_submit() works as expected when you keep the default value "eduPersonPrincipalName" for both user_name and unique_id. Maybe we should call $externalauth->linkExistingAccount() only if these settings have the same value, indicating that the Drupal user's username and the authmap's authname should be equal?
Remaining tasks
- Agree on a solution
- Write patch
- Commit.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 3332611-4.diff | 845 bytes | jrearick |
Issue fork simplesamlphp_auth-3332611
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
jrearickWe're running into the same issue. Just to get things moving, I created a MR and attached a patch file. It's hard to test without a proper environment, so any help testing would be appreciated. I assume we'll want to add tests for this, but I suppose that can wait until we know this is the solution to go with.
Comment #5
gocaps commentedWe use a custom Unique Identifier field and so we were running into this issue, where the authname would update in the authmap table each time the user was edited. On next login attempts, Drupal would create a new user prefixed with "simplesamlphp_auth_" and log an error:
Seems related to https://www.drupal.org/project/simplesamlphp_auth/issues/3347716
Patch #4 is so far working.
Comment #6
giuseppe87 commentedI'm facing the same bug updating simplesamlphp_auth on a existing project from 3.2 to 4.x-dev, and #4 works.
Comment #7
caesius commented@Giuseppe87 could you confirm that you ran into this issue by updating from 3.2 to 4.x-dev without also updating the externalauth module? The original ticket description seems to indicate that this issue would come up after updating externalauth (since it introduces new behavior) regardless of the version of the simplesamlphp_auth module.
Also, the latest 3.x version is 3.3, so check to make sure that the issue doesn't come up when going from 3.2 -> 3.3 instead. The 4.x version should be unlikely to cause any issues that don't also occur in 3.3.
One last thing -- since multiple people have indicated #4 works, perhaps this should be marked RTBC by someone who has confirmed that it does work?
Comment #8
giuseppe87 commented@caesius you're right, I mixed the two modules responsibilities.
I've done the tests you asked:
Therefore as you stated isn't something strictly due to 4.x-dev upgrade.
However, I'd point out that this issue could be implicitly linked to the 4.x.x release, for dependency reasons.
If 4.x.x is gonna the branch with D10 support, being 2.x.x the only branch with D10 support for externalauth, when upgrading core and thus contribute modules, everyone using simplesamlphp_auth will face this issue.
Comment #9
giuseppe87 commentedComment #10
caesius commentedRe-ran the 3.x tests and it seems good now. 4.x failed due to the same Composer plugin error that 3.x originally ran into, but it's obviously not related to this patch.
Comment #13
berdirCommitted, thanks.
Comment #14
bbombachiniI know the issue has been resolved, hence closed. But is there any suggestions about how to fix the users that have already been affected?
Comment #15
giuseppe87 commentedI don't know if there's a better way, but from the debug I did to identify this bug switching to 4.x.x, I suppose the following method method should work - please be aware you have to test it, I haven't actually tried it, so maybe I'm missing something in externalauth module flow.
In the end, you have to update the database table
authmapfor the the user whose externalauthnamehave been overriden with the local\Drupal one.In my case, the external authname is a string like
00556409645g0050SJQww2while the drupal one is an email, so if I needed I could query the table to list all the elements with an email as authname, and then manually update them.You obviously need some way to find the external authnames for the broken records - e.g. a backup db.
A direct database query should be good enough, but to be 100% sure all the externalauth module flow is followed, you could write a batch function which use
externalauth/src/ExternalAuth.php::linkExistingAccount()to update the $users with this problem.