Skip to content
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

Add universal required email configuration for authentication #3047

Merged
merged 12 commits into from
Jan 12, 2024

Conversation

apedroferreira
Copy link
Member

@apedroferreira apedroferreira commented Jan 3, 2024

Make it possible to add multiple email address patterns to be matched by authenticated users, either for the Github or Google authentication providers.
Also takes in consideration all verified emails in Github.
Also makes the authentication/authorization dialog be tabbed, brought this from #3002.

Untitled.mov

@apedroferreira apedroferreira self-assigned this Jan 3, 2024
@apedroferreira apedroferreira added the new feature New feature or request label Jan 3, 2024
@apedroferreira apedroferreira marked this pull request as ready for review January 3, 2024 17:23
@apedroferreira apedroferreira requested a review from a team January 3, 2024 17:23
profile.email.endsWith(`@${process.env.TOOLPAD_GOOGLE_AUTH_DOMAIN}`)),
(requiredEmails.length === 0 ||
requiredEmails.some(
(requiredEmail) => new RegExp(requiredEmail).test(profile.email!) ?? false,
Copy link
Member

@Janpot Janpot Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan of using regexes for this. Seems a bit like a footgun to me. e.g. users forgetting to add $ on the end, or forgetting to escape .. e.g. someone naively configures .*@foo.com and then me coming in and buying the domain "fooxcom123.com" and getting access to your server.

Wouldn't >95% of the usecases be covered by just restricting the email domain? Can you benchmark other products on how configurable this is?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll give a look into how other tools do it, but last case scenario we can just check if the user email ends with the specified domain preceded by @, instead of regex.

@@ -27,6 +31,42 @@ export function createAuthHandler(base: string): Router {
GithubProvider({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this provider have to be recreated on every request? Would it make sense to put it in its own module?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just following some examples and didn't consider that, it should be better to move it outside, will do!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things didn't work when I moved the provider to a separate variable, I think it should be fine to do it like this as that's how the examples I have seen work too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things didn't work when I moved the provider to a separate variable

Any idea why? This seems like a strange thing. Can you investigate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I'm taking a look at how it works internally, this library has some weird things sometimes though, it seems to be a bit of a work in progress and most things aren't documented at all.

Copy link
Member Author

@apedroferreira apedroferreira Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the environment set with this.envManager.start()? This runs after the auth.ts file is imported, that's why the variables that need to be loaded aren't there for a global variable in that file.

Copy link
Member Author

@apedroferreira apedroferreira Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can load the Toolpad app's environment before any imports when the server starts if you think it's worth it. Sounds like an unrelated thing though, I don't see what's wrong with initializing the providers in createAuthHandler.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can also load the providers in a global function if that's better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can load the Toolpad app's environment before any imports when the server starts if you think it's worth it

Just

import 'dotenv/config'

before anything else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it works if I add that line to toolpadAppServer.ts, as that file seems to run before index.ts - it might be because the worker imports that file first, I can look deeper into it and try in another file if this one doesn't seem ideal for any reason.
07015a0

@apedroferreira
Copy link
Member Author

apedroferreira commented Jan 5, 2024

I've added the change from regex patterns to using domains only, and kept the rest, let me know if this is good to merge!
I've renamed the property in application.yml to requiredDomain too.

@apedroferreira
Copy link
Member Author

Renaming the property to restrictedDomains too, it sounds better.

@@ -0,0 +1,5 @@
export declare module '@auth/core/types' {
interface Profile {
verifiedEmails?: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't public API, should we really rely on it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With your suggestion verifiedEmails shouldn't be needed anymore so I'll remove it, it was only for verifying the restricted domains with the Github provider.


if (githubRes.ok) {
const emails: GitHubEmail[] = await githubRes.json();
profile.email = (emails.find((e) => e.primary) ?? emails[0]).email;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just look for the first verified email that satisfies our restrictedDomains setting and use that as the email? Then set the email_verified boolean to true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the default logic but it would be an improvement, i'll change it and fallback to primary email.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the changes here fd18d7c, let me know if it's good now!

@apedroferreira apedroferreira enabled auto-merge (squash) January 12, 2024 17:16
@apedroferreira apedroferreira merged commit f854106 into mui:master Jan 12, 2024
11 checks passed
@apedroferreira apedroferreira deleted the auth-required-email branch January 12, 2024 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants