-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Injected auth models #1583
Injected auth models #1583
Conversation
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
waspc/data/Generator/templates/server/src/core/auth/prismaMiddleware.js
Outdated
Show resolved
Hide resolved
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another round of review done!
waspc/data/Generator/templates/server/src/auth/providers/oauth/createRouter.ts
Outdated
Show resolved
Hide resolved
* if given middleware returns promise, reject of that promise will be correctly handled, | ||
* meaning that error will be forwarded to next(). | ||
*/ | ||
type RequestWithExtraFields = Request & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I get it better now.
Yeah, you need this to be able to say req.user
later.
So far we used no types, now you started using express.Request
, but that one won't let you do req.user
. And we need that in many places.
The thing is, we don't need it in all places. And if I am correct, we know in which places we need it, and in which places we don't right? So I wonder, can we make it more precise? Instead of saying user
could be in any request, should we go step further and specify it per request?
We could make handleRejection<T extends Request>
and then call it with handleRejection<WithUser<Request>>(...
where appropriate.
I am still learning Typescript so I maybe made up some things here or they don't make fully sense, but I think the direction should make sense. Probably the bigger question is, is it worth doing this, I will let you decide. I see 12 different call sites of handleRejection
, and I think if there is a single one there that for sure doesn't need user, then it is worth doing it. Even if not, if there is a chance there will be such usage, I think it is worth doing it. But if you think that doesn't make sense let me know! I am also not sure if this is indeed easily doable with Express's current typings, if that could make it harder.
EDIT: there is certainly at least one such place: handleRejection in server/src/core/auth.js, the first one, const auth = handleRejection
, that one doesn't have user on it for sure, right?
* if given middleware returns promise, reject of that promise will be correctly handled, | ||
* meaning that error will be forwarded to next(). | ||
*/ | ||
type RequestWithExtraFields = Request & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for the name: if we are using that one Request always instead of Express's Request, then I would probably go with just Request
even, since it is virtually a replacement for Express one. Or maybe WaspRequest
.
But if we sometimes have user in request, sometimes don't, and similar, then I would instead go for type level operators, something like WithUser
and then construct it in place as needed. Maybe have some aliases for common combos.
@@ -0,0 +1,27 @@ | |||
// Since we can't deduplicate these helper functions in the server and the client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could though create a template file under templates/universal/src/auth/user.ts, right, and then just inject this very first line with imports into it, couldn't we? At the moment. And then once we have restructuring done, we might even have some other options open, as it being part of SDK means we can more easily share some code maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already commented on that option here: #1583 (comment)
As I said, I've considered it and went with a simpler solution of duplication. This can be the temporary solution if it's going to change with the restructuring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I commented again because you said in the comment in the code (the one I commented on here) that we can't deduplicate this, which is not correct, we can, but you decided we rather shouldn't as it is not worth it. So maybe let's update comment with that info?
waspc/data/Generator/templates/server/src/auth/providers/email/requestPasswordReset.ts
Outdated
Show resolved
Hide resolved
// ProviderId represents one user account in a specific provider. | ||
// We are packing it into a single object to make it easier to | ||
// make sure that the providerUserId is always lowercased. | ||
type ProviderId = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, if you wanted to do actual nominal typing in Typescript, check this: https://byby.dev/ts-nominal-typing .
waspc/data/Generator/templates/server/src/auth/providers/email/signup.ts
Outdated
Show resolved
Hide resolved
waspc/data/Generator/templates/server/src/auth/providers/email/signup.ts
Outdated
Show resolved
Hide resolved
waspc/data/Generator/templates/server/src/auth/providers/email/signup.ts
Outdated
Show resolved
Hide resolved
if (providerData.isEmailVerified) { | ||
await doFakeWork(); | ||
return res.json({ success: true }); | ||
} | ||
|
||
|
||
// TODO: we are still leaking information here since when we are faking work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We agreed to improve upon the timing attack prevention by improving the fake work mechanism:
- make sure that real and fake flows take the same time
- handle the "try again in 1 minute" case better
|
||
} else if (existingAuthIdentity && allowUnverifiedLogin) { | ||
// 2. User already exists **and we allow unverified login** | ||
throw new HttpError(422, "User with that email already exists.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Ah yeah, makes sense -> so we can't pretend sign up was successful if we allow unverified email. How about we just forbid unverified email?
- Example of an auth system -> well what do they do, how do they handle it? I thought I remember getting such an email but I can't remember for sure. If I go into research, it will take me much longer than just reviewing, but ideally you would do the research here and know what others do, with examples and stuff. Why do you think it is out of scope? You think we are being too pedantic? I don't know but it sounds weird that I try to sign up, and web app says I succeeded and it sent me an email, but then I get no email, I would conclude something is wrong and give up, not that I already maybe have an account. Right?
- (and 4 and 5) -> yeah ok I brainfarted a bit there, but then what we can do is send them an email saying that somebody already created an account with their email and they have to wait a bit before they can claim it, or they can click on password reset and we give them link. How about that?
waspc/data/Generator/templates/server/src/auth/providers/email/resetPassword.ts
Outdated
Show resolved
Hide resolved
rethrowPossibleAuthError(e); | ||
} | ||
|
||
} else if (existingAuthIdentity && allowUnverifiedLogin) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's reafctor it.
I get the point with nesting, but what you loose is obvious branching -> instead of reader being very clear about all cases being handled, I have to think quite a bit here to figure out these two really cover all the cases. And it can also be easier in the future to introduce a mistake where these conditions don't perfectly match each other and suddenly they don't cover all the cases any more.
waspc/data/Generator/templates/server/src/auth/providers/email/utils.ts
Outdated
Show resolved
Hide resolved
|
||
export type OAuthProviderData = {} | ||
|
||
// This type is used to map provider names to their data types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I expressed myself clumsily, but what I meant to say is that this type is not useful to describe values, but only to describe other types (type level programming). I don't think that is clear by itself, and would mention that in the comment.
…/signup.ts Co-authored-by: Martin Šošić <Martinsos@users.noreply.github.com>
Co-authored-by: Martin Šošić <Martinsos@users.noreply.github.com>
Closes #1080
Qucik recap of things that happen in this PR:
Instead of asking from users to define the auth related entities, we inject them in the Prisma file
The auth entities changed to fit all auth methods into one uniform shape and allow for easier extension later on
Since some of the information is now stored in
providerData
JSON field, a bit of work was done to serialize and deserialize the field properlyAll of the auth methods were updated to use the new structure
Unit, e2e and headless tests were updated
What is left