Allow logging-in to same account via multiple auth providers #24868
Replies: 28 comments
-
|
@benhaynes It is possible to manually create users with a different provider in the current implementation. But not invite. As for the other features. we need to break out the provider relation to be a many-to-many instead of the current "one user has one provider" relationship. This shouldn't be too technically difficult. We just have to consider how it would affect users where we don't want them to have multiple auth providers (LDAP, Kerberos, etc). That will likely be covered by configuration options as you mentioned. |
Beta Was this translation helpful? Give feedback.
-
|
Also curious if this could be resolved with a flag on the configuration for the auth provider that sets whether or not it's required for the user to be in the provider 🤔 If you were to configure 3 different OAuth providers, that all use "email" as the identifier key, the only thing you'd have to skip is the "if provider == in allowed providers" check. If you could set that on the provider config itself, you could explicitly grant specific providers access to users that aren't necessarily configured to only allow login from the configured providers 🤔 |
Beta Was this translation helpful? Give feedback.
-
|
@rijkvanzanten That would screw with the stored |
Beta Was this translation helpful? Give feedback.
-
|
Ah right, cause that's tied to the user data, and not the session data right? 🤔 We could re-format that user auth_data JSON structure to be of type |
Beta Was this translation helpful? Give feedback.
-
|
Although I can't understand it, it feels great! |
Beta Was this translation helpful? Give feedback.
-
|
Since your are discussing a reworking of the auth here, I think this may be a good place to link a discussion topic that I just started about Anonymous Authentication. #9566 |
Beta Was this translation helpful? Give feedback.
This comment has been minimized.
This comment has been minimized.
-
|
FWIW there's maybe a better solution here. the then make a
one actual relationship here, but 2 paths to data as the current pattern allows. this is how we're currently organizing our schema, but it requires us to add some post signup actions for users to get more data from their provider (such as guilds and roles from Discord) and allow users to connect accounts to their profile. then perhaps i can have a moderator role account connected to my Discord because I have that role in the guild, but when I login with Twitch it's a more standard role, but sharing the same user profile. this way users could even have different email addresses on various accounts etc. the missing piece is a "connect accounts" page allowing users to OAuth/OpenID after already signing in. and a built-in yet non-system for an just allow the user to use the same anyhow it would never hurt users who just wanted a O2O (a valid pattern, even just inside a O2M) with accounts and profiles. but the many option would be on the table at any time. |
Beta Was this translation helpful? Give feedback.
-
|
I understand some systems may want to only authenticate users using the original provider (the one used during registration) for security reasons that I don't really understand, but that is rather an edge case. But I think being able to authenticate using any provider enabled in Directus configuration is a very important feature and is a desired behavior in 9 cases out of 10. I would assume that if a provider was enabled in Directus then it is trusted, and if user can prove through authenticating with any of these providers that they are who they claim to be then I don't see what the security problem is. In any case I think this should be an option configurable through environment variables. If someone points me to files where this logic is implemented I'll be happy to submit a PR. |
Beta Was this translation helpful? Give feedback.
-
|
@adanielyan The security reason is that many people use managed external user providers like Azure AD or LDAP. So if the user is disabled in Azure AD they should also be prevented from logging in to Directus. But if they can log in with any provider, there is nothing to stop them setting a password in Directus and logging in using the default email/password flow to bypass that. There is also the complication that we store provider specific information with the user, such as the OAuth 2.0 "refresh_token" that we use to ensure the users permission hasn't been revoked. We'd need to instead store that for each provider, rather than just the user. With that being said, I would love to see multi-auth users implemented in a good way. The only logic currently blocking users from working with multiple providers is a single "where" in the fetch user query in the authentication service. https://github.com/directus/directus/blob/main/api/src/services/authentication.ts#L71 EDIT: We might also need some way to have multiple "external_identifier" values. I don't know. Maybe it's good enough to always say "If they external identifier matches, return the user" but that's pretty inflexible. |
Beta Was this translation helpful? Give feedback.
-
@aidenfoxx Thank you for explaining the security implications, it is very helpful. That said, I still believe there are as many (if not more) Directus installations where this problem is not applicable. For those who are in the same boat with me and have a default local authentication as well as a couple of OAuth/OpenID methods enabled, being able to authenticate a user by any auth providers is crucial.
I would love to learn more about this. Are you storing a refresh token provided by OAuth provider or is it generated by directus by hashing the provider name along with other user fields? If it's latter, maybe we should exclude the provider name from hash if the corresponding environment variable (e.g.
Please let me know what you think about the approach I described above. I am happy to discuss and try to implement this or a better approach you may have.
We can only check the provider if
If I understand it correctly we already have |
Beta Was this translation helpful? Give feedback.
-
|
I'm not the best authoritative person to weigh in on this, but from a business (less technical) perspective, I agree. It makes sense to me to allow users to register any number of SSO providers, and add an ENV/Setting to toggle this ability so that enterprises can lock down the feature if needed. |
Beta Was this translation helpful? Give feedback.
-
|
@benhaynes I would agree. The ability to have it either way is certainly the optimal solution. But from a technical perspective handling multiple providers is obviously more complicated than just having one. Especially when you add in provider specific data like "refresh_tokens". @adanielyan "refresh_tokens" are provided by the OAuth provider and are a long living token that we need to store indefinitely. The token is usually only provided the first time the user authorizes Directus with their SSO account, and we use it to verify that the user has not revoked Directus access. Personally, my "perfect" implementation would be to separate the provider stuff into it's own "directus_users_providers" table in a O2M relationship so users can have multiple providers. This would even allow users to have different "external_identifiers" linked to one Directus account, which was my concern in the "EDIT" section (for example, you use "username" for Github and "email" for Azure). But even this runs into weird corner cases. Like what do you do with dynamic registration if you get a username conflict between two services? Let's say "User A" has an account in Github with the username "DankMemer123" and "User B" has the Twitter username "DankMemer123". How does dynamic registration handle that? Does it log "User B" into "User A"s account? It's an obscure case, but we have to be able to handle these kinda things in a good way. I'm sure @rijkvanzanten would have some good input on potential solutions. |
Beta Was this translation helpful? Give feedback.
-
Thank you for explanation. In that case it makes a lot of sense to have a
This is an interesting edge case. I agree it is a valid concern. But I know many websites that somehow solved this issue. I think in cases when the identifier cannot be unique by definition (e.g. username) we should only allow logging in with the provider that was used during registration. But when an identifier is unique across all providers, such as email or a phone number, we can be sure that the user authenticating through a provider different from the one used during registration is the same person. I believe a decision on which identifiers should be considered unique for any directus installation must be made by administrator through listing those identifiers in an env var. We can have it default to email though, but admins should be able to override. |
Beta Was this translation helpful? Give feedback.
-
That's kinda hard to know when different providers can use different keys for their fields. "mail" vs "email" or "phone" vs "telephone" vs "phone_number". Hot take: I would personally say it's better to always create new accounts for dynamically registered users, rather than trying to add the provider to an existing user. If they want to link multiple providers to a single account they should set it up manually in the "User Preferences" UI. |
Beta Was this translation helpful? Give feedback.
-
Will this be even an issue? The auth providers are explicitly configured by Directus admin who must know which identifies to choose for each. The config options for this are already in place. The only additional configuration will be to list a subset of these identifiers that admin considers unique in a new env var. In case it is "email" for one provider and "mail" for another, both will be listed.
In this case if a user logs in with Google first time and then tries to login with Facebook next time, the system will try to create a new account with the same email and will fail, because email is a unique field. But even if it succeeds creating a new account, wouldn't it be confusing to have 2 accounts with the same email? And what if the first account was created using local/default method? I think this may be an unnecessary complication. |
Beta Was this translation helpful? Give feedback.
-
I would say it's more intuitive than having to explain "you have to explicitly set your identifier as unique/not unique because of this weird edge case". People have enough trouble understanding the "IDENTIFIER_KEY" config as it is, whereas this solution doesn't require any additional config and avoids many edge cases. We'd fail trying to create with the same email, which I think is fine if we show a "You already have an account. Log in there and link this provider to your account" message. And if a user gets 2 accounts, I'm sure they'll quickly figure it out and delete one and link the other. Also, you'll still get account duplication with your suggestion, so no way is foolproof.
I think it's easy enough to make it clear that if you already have a Directus account, to link other external providers you have to do so through the UI. That's just my two cents. I'm sure @rijkvanzanten has some good input (if he's not super busy already 😁). |
Beta Was this translation helpful? Give feedback.
-
|
@aidenfoxx thank you for your insight, I really appreciate your input to this discussion. With all due respect let me disagree with some of your points.
I don't know, I don't feel like it's too much too ask to add one more configuration in order to enable a multi-provider authentication. After all it may be only an opt-in feature which will be disabled by default. But if an admin wants to enable it then they will have to take this extra step, which objectively will only take 15-20 minutes to figure out, especially if accompanied by a good documentation with a couple of examples.
In this case you are transferring the burden of configuration from one person - the site admin, who by definition should be more technical, to hundreds or thousands of users, who are supposedly less technical and may have difficulties understanding what linking is.
Hmmm, not sure I understand this part. Why will we get account duplication with my suggestion?
It may be easy enough if the authentication is done through directus' interface. However, in many cases people use directus as a headless CMS and do the authentication from their front end application that communicates to directus exclusively through API. It means that we will have to leave the implementation of account linking to every application developer who chose to use directus as their backend. |
Beta Was this translation helpful? Give feedback.
-
It's all good. I noted it's a hot take because it's pretty contentious. I'm just stating my opinion based on dealing with the auth system in the past, and all the edge cases and issues I had to deal with.
You'd still get duplication if a provider doesn't return email. Twitter, for example. Twitter only returns a username, and if all your other providers use email, you wouldn't be able to merge it to an existing user. Buy yeah, don't take anything I say as gospel. I'm just thinking aloud for the most part. I've said all I want to say, and I look forward to seeing what you come up with! 😄 |
Beta Was this translation helpful? Give feedback.
-
Thank you for taking it easy :)
Technically it's not a duplicate as two accounts will not have the same email. I hope we continue this discussion and finally come up with a feasible solution that can be implemented. Would love to know what others think about it. |
Beta Was this translation helpful? Give feedback.
-
|
Just popping by to say that I love seeing such cooperative and friendly discussion. We'll get this one figured out... not an easy topic! ❤️ |
Beta Was this translation helpful? Give feedback.
-
|
In my company we've started using Keycloak as a SSO provider, and Keycloak indeed does support multiple auth providers. In a setup with multiple auth providers you will constantly have an issue where people don't remember which service they logged in through originally. I saw this in a project I worked on, so it is worth solving this issue even though it is tricky to get the usability/user flow "perfect". In Keycloak, if you use email as username and log in with an email which is already "taken", it will ask you to log in with your existing account (if it is a local email/password account) or (I assume, haven't tested) the auth provider you used previously. From an end user perspective it would be very convenient to be able to log in with any provider since you might be logged in through different providers in different browsers/computers and some might not be available due to 2FA methods not being available (forgot the phone at home?). From a the perspective of a team using directus it would be convenient in cases where you're migrating from one provider to another one, just as an example. |
Beta Was this translation helpful? Give feedback.
-
|
@badrange This PR should at least make your life easier in the mean time #13184 |
Beta Was this translation helpful? Give feedback.
-
|
Linear: ENG-289 |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for the discussion, it looks like a good deal of the technical aspects have been discussed. I would add we'd love to see multiple auth providers implemented for our organization (we use Google Workspace for workforce and want to roll out CIAM for external users):
At the very minimum, we'd need the fallback to Directus' username/password login for workforce. Perhaps it could be helpful to split this issue into 2 categories to manage the security concerns:
Let me know what you think. |
Beta Was this translation helpful? Give feedback.
-
This feels like a good move to me. The biggest concern here is that we can't really be too opinionated on whether or not you allow or trust different providers to be compatible. If you use Google and Apple as providers, there's an expectation that the third party verifies email addresses and prevents impersonation. However we can't make that assumption about any and all SSO providers. Whatever solution we end up with here, there has to be a way to create an account through one of the login methods, and then connect additional methods after you've logged in through that first method. That way we at least have some trust that the user is opting-in to those accounts being linked. If we were to allow you to login straight through a different provider without first manually connecting the accounts, there's a high risk of impersonation through identifiers (like email) that may or may not be impersonated on the third party service |
Beta Was this translation helpful? Give feedback.
-
👏 |
Beta Was this translation helpful? Give feedback.
-
|
any update on this? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I know there are login security concerns based on how a Directus user account is created via an auth provider. However, there is also the precedent of using SSO as a convenience feature for logging in (not for registration).
I would like to discuss and agree upon the ideal configuration options to balance all security and convenience.
Account Creation
Account Authentication
I would like to support all combinations of the above (if at all possible) and offer configuration options to limit for security purposes. For instance, there should be options to individually disable local registration and login, and for limiting login to the service that created the account. We'll also need a way for authenticated users to "connect" multiple different installed auth providers (if admins have configured this to be allowed), so they can be used for login.
Let's use this ticket as a place to gather information and track what needs to be done.
CC: @rijkvanzanten @aidenfoxx @cdwmhcc and maybe @dorianim
Beta Was this translation helpful? Give feedback.
All reactions