aws-samples / amazon-cognito-passwordless-auth

Passwordless authentication with Amazon Cognito: FIDO2 (WebAuthn, support for Passkeys), Magic Link, SMS OTP Step Up
Apache License 2.0
370 stars 65 forks source link

Prominently mention JWT/localStorage issues in FAQ -- Avoid JWT? #165

Open cmawhorter opened 5 months ago

cmawhorter commented 5 months ago

I read this answer in the FAQ and I was pretty far down the road before I finally did 2+2 and realized this significant caveat regarding JWT/localStorage. Even being well aware of this pitfall with JWT I assumed it was a non-issue based on the FAQ. To help dummies like me in the future, I'd recommend adding that link to the amplify issue into the FAQ to drive the point home.

Totally my fault, but that leaves me at the point of either having to start over or keep moving forward and figure out a solution to avoid storing tokens in the browser.

I'm wondering: Has any thought/effort been given to using cloudfront instead of api gateway? I don't have a complete grasp of how everything is working here, but it seems like doing that would eliminate CORS entirely and make it pretty simple to support secure session cookies instead of JWT.

As it stands now, I think my plan is to hack together a lambda@edge proxy to the api gateway that stores tokens in DDB with an opaque session ID, and issues a secure cookie. It seems like that's my best worst option at this point, unless there is a better idea out there?

That'll mean forking the client side stuff, but that feels like a better option than abandoning all the super helpful server things in cdk.

ottokruse commented 5 months ago

Thanks for filing the issue!

This is a hard topic. If you want to avoid JWT, the first question is, why use Cognito User Pools then––you could say its primary purpose is to hand out JWTs. You can build something yourself to map those JWTs to signed session cookies, but it's considerable plumbing on your hands now. And one could argue it defeats the purpose of JWT, as being stateless is one of its key selling points.

But using an encrypted session cookie approach is certainly a good option, and a tried and tested approach. With HTTP-only cookies the XSS concern goes away, but now you need to protect against CSRF. I do want to mention that using LocalStorage is not insecure per se (take this as a personal opinion please), because XSS can also be protected against (one important part would be a good CSP).

But this is a highly debated topic. And right now I think most people tend to say that HTTP only cookies are the most secure option (with eg an encrypted session token, but it could also be JWTs), but there's pros and cons to both sides.

Should you want to store JWTs in HTTP-only cookies, have a look at https://github.com/aws-samples/cloudfront-authorization-at-edge. Downside: that solution uses hosted UI and so does not work with the Passwordless flows from the repo here (that have been built with custom auth flows).

cmawhorter commented 5 months ago

Understood. My intent with opening the issue was to suggest calling out the JWT requirement in the readme, and I'm definitely not looking to rehash the pro/con of jwt generally.

Why cognito? Short answer is because identity and especially MFA is hard to get right and disastrous to get wrong.

Longer answer is that it has been many years (pre user pools) since I've looked at cognito, and I was giving it another shot. I'm essentially coming to cognito new again, and I was blindly assuming (hoping) things would be a lot more drop-in in the context of an app user auth scenario.

Re. state: I'm not sure I'm following how swapping in an opaque session id in place of jwt would change things. It just moves that sensitive state from the browser to the server, but is otherwise identical?

I'm guessing the underlying reason for leaving jwt here was because cognito/IAM can be used to auth* calls to aws resources? That is something I've stayed away from because it just feels like a bad idea, and I probably will never be convinced otherwise.

I donno. This all kinda took the wind out of my sails and I've moved on to other projects. I'll come back to the project at some point, but I don't know what I'm going to do.

ottokruse commented 5 months ago

Hi mate. Cognito can be a fine choice and worth your look again.

I think the issue you uncovered is, how do I store JWTs securely in the browser?

And the gist of it is, there is no definite answer on this, and "it depends". This is not particular for Cognito, all IDPs have this concern.

How can we get the wind back in your sails?

cmawhorter commented 5 months ago

how do I store JWTs securely in the browser

We're wading into jwt debate territory, but I'm in the camp that you can't. Sure, from the context of "the browser" you can, but there is all sorts of malware out there that scoops up jwt (and session cookies), but jwt contain PII and are designed to be passed around and can more easily be leveraged by an attacker.

[...] not particular for Cognito, all IDPs have this concern

True, but google, etc. have tons of resources and teams of people working to make sure things are safe and secure. Cognito offloads a lot of that responsibility onto me, which makes me ask... what's the point of cognito? If I have to give serious thought to all this then rolling my own is starting to look much more attractive as I'd have complete control and understanding of everything. This definitely feels like a least bad option situation.

How can we get the wind back in your sails?

A lot of my issues are turning out to be with cognito and not exactly related to this repo, so I'm not sure you can. Everything here worked great and was easy to get running. Maybe pass up the AWS chain to stop shipping MVPs lol. I don't know.

ottokruse commented 5 months ago

Everything here worked great and was easy to get running.

Thanks that's great to hear.

If you can provide more details on your exact scenario I can think along with you better. I realize I've been shooting from the hip a bit in this discussion, without understanding your scenario well.

I'd also be curious what features from the likes of Google you're referring to exactly, that you miss in Cognito in context of your current use case.

Otherwise, thank you for your interest in this solution here so far.

cmawhorter commented 5 months ago

My particular scenario is pretty straightforward: It's a dashboard web app with an email/password login. No social/oauth or anything like that. I'd like to stop using my own solution and switch to something else as part of a larger rewrite. I found this repo while researching MFA and it seemed like a good starting point.

what features from the likes of Google you're referring to exactly

I was speaking within the context of google themselves consuming their jwt. The point I was trying to make was that me having to think about the security implications of using jwt is something I'd prefer not to do.

FWIW I came across this randomly and it seems to be exactly what I'm after. A drop-in solution that has server session management, mfa, etc etc. I wasn't looking for it and didn't dive too deep beyond checking if it can do cookies instead of jwt, which it seems like it can (by storing tokens server side in db). I'd still prefer to use cognito though because I trust the inner workings of aws a lot more.