I have been digging through the documentation and related topics to figure out how to improve our current auth0 setup. There is one aspect which I cannot readily find an answer on. To start our current setup is as follows:
We have a pretty standard SaaS application with a ExpressJS backend and React frontend. Right now if a client invites a new user to our platform we create a user in auth0 using the management API. We store this user_id auth0|… in our db together with the client id so we know who this user is and to which client it belongs. After creating the user we send a custom email to them with the password reset link[1]. This e-mail also includes an option to sign in with SSO which if used, we link the accounts if the e-mail is the same using a post login action. Clients also have the option to enforce SSO and if used we again link accounts based on e-mail.
The issue we are facing right now is that with Azure AD users have a upn that’s different from their e-mail which causes not to be able to link their accounts and we basically do not know who these people are. You can imagine someone being invited and getting a user account in auth0 with auth0|… and then using the mail to login with SSO this creates another account in auth0 with a user id waad|… and some upn that we cannot match to anyone.
From my investigation I can deduce there are a couple things wrong with this flow. First and foremost we should not be linking accounts based on e-mail without authenticating those accounts. From my understanding a normal flow would be create user in auth0 → invite with e-mail → have the user create a password → let them login to the application → link an SSO account with user initiated account linking [2]. This prevents linking accounts that shouldn’t be linked. It also let’s people link accounts with different upn/email/etc.
The one aspect which I cannot find much info on is what needs to be done if we want users to only and immediately make an account using SSO and skip the user + password flow. What I came up with was to add a query param with this users id which we can then send in the redirect callback so we know who the user was that clicked on the link and signed in with SSO. So the e-mail would have a ?token= on the SSO button. We pass this token to handleRedirectCallback({ authorizationParams: { token: url.searchParams.get(‘token’) } })now inside post login action we can access this token and find the user inside auth0 and link them.
So my question is what is the recommended way to allow only SSO signup and invitation by e-mail?
If you already have an external email solution and don’t want to configure an SMTP provider on your tenant, you can use the Management API to generate the password change ticket and email the ticket link to the user yourself instead of using Auth0’s email template and workflow.
It sounds like the real issue here isn’t Azure AD or UPNs so much as the fact that identity is being stitched together after the user has authenticated. Pre-creating a database user and then trying to link whatever SSO identity appears later (especially by email) is inherently fragile, and Entra just makes that more visible because UPN and email don’t have to match. If the intent is SSO-only onboarding via email, I’d avoid creating users up front entirely. Instead, invite the user into the tenant/org and let their first successful SSO login be what creates the Auth0 user and establishes which customer they belong to. That removes the need to match on email or UPN, cuts down technical debt, and gives you a much cleaner identity model. From a UX point of view it’s also simpler: accept invite → sign in with your company account → done.
On the Auth0 side, this is something you can support cleanly. You can use Organizations invitations to onboard users and, for customers that enforce SSO, disable or avoid the database connection for that application so there’s no password-based signup path at all. In Universal Login, that effectively removes the “sign up with email/password” option and only presents the allowed enterprise SSO connection(s) for that org. The invitation link carries the org (and optionally the connection), so Auth0 knows this is an invite-only flow and will create the user on first SSO login.
From a security and identity perspective, I’d also avoid silent account linking in Actions. If someone later needs multiple login methods, make that an explicit, user-initiated linking flow where both identities are authenticated. Your idea of passing a custom token in the invite link can work, but it’s essentially re-implementing invitation and binding logic that Auth0 already provides, which adds risk and ongoing maintenance. Treat email as a profile attribute for communication, not as an identity key, and use org/tenant membership as the stable source of truth.
Thank you for your fast and detailed reply. I fully agree that the issue is stitching together accounts. I understand what you are trying to say and it does look like organizations would be a good solution for us. However one thing I am still unclear on which kinda relates to the token solution. In our application when a client invites a member they can immediately select a bunch of options for this user. We store these options in our database. If we do not create the user upfront in auth0 how would we know who is who? Right now we create some user object like this in our db:
normally we would put the auth0 user id here as well but since we do not have that, how can we relate this back to the person who gets the invite, we can’t use the e-mail because that isn’t safe? It seems like we could use app_metadata property when creating the invite and put our id inside metadata so we know who it is?.(Welcome to Auth0 Docs - Auth0 Docs)
And if we wanted to not use organizations then such a flow is not possible to send invite urls without creating an account?
Thanks again for your reply it was already helpful
@JFoxUK has provided a good point regarding the implementation and my personal suggestion would also be to rely only on the SSO login without having to create an Auth0 user then link these identities.
If it is necessary for you to retrieve some kind of user_id in order to keep track of these invitations, you can create dummy Auth0 users which will be blocked (in order for this identity to not log in) and use that specific user_id. You can store these dummy users in a new database so that they do not get mixed with normal users using Email+PW.
Alternatively, you can try to generate an custom invitation token which you can send inside the state constructed by your backend in order for the SSO login to point to the dummy created account and link these identities using a PostLogin action as long as the token is valid and present.
And if we wanted to not use organizations then such a flow is not possible to send invite urls without creating an account?
This flow is basically possible, however, you will not have a dedicated organization invitation email. You will need to customize either the welcome email template or email verification one for the invitation flow (since you will be sending these emails with a custom password change ticket attached to it in order for the users to create their password). This involves a lot more customization steps for your tenant rather than using the simple organization flow already available.
Since you have not replied back regarding the topic, I will be marking my previous reply as the Solution. Feel free to let us know if you experience any further issues. You can always post again on the community if you ever need help!
Apologies I haven’t had time to look into the issue any further. To get back to your proposed solution I am not sure the dummy auth0 account would work. It seems that would run into the same issue. If I create a dummy auth0 account I will get some id `auth0|1234` but then after this I would still need to send this id to the SSO flow to understand that the person logging in with SSO is that user. This seems like it would be the same as making a custom token except the user_id is the token.
From what I can tell the best solution is probably using the “Invite Organization Members“ flow. According to the docs you can add app_metadata during the creation of an invite using the management api ( Welcome to Auth0 Docs - Auth0 Docs ). I could supply an id from our end to the app_metadata and use that to keep track of who is who. To me this seems like a good way to go and to keep complexity to a minimum. Could anyone confirm this is an okay use of app_metadata or is this unsafe.
Indeed, the organization invite flow would accomplish the same outcome and would decrease the complexity of handling the dummy account(user_id) generated during the flow.
Since app_metadata is only modifiable by the application itself, it is safe to use it since the user does not have control over it and it would require someone to have admin access to the tenant or application in order to inject/change values within it. Also, it would be quite hard to intercept the request to the Auth0 server since it would require somebody to have access to the M2M token provided during the exchange.
In other words, the flow should be perfectly safe for your use case.