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
386 stars 70 forks source link

Magic Link Metadata Storage #172

Closed KyleKotowick closed 6 months ago

KyleKotowick commented 6 months ago

The README for magic links recommends storing link metadata in DynamoDB.

Is there any particular reason that DynamoDB is recommended, instead of storing the link metadata in a developer-only attribute on the Cognito user? This would eliminate the need for the DynamoDB table.

ottokruse commented 6 months ago

We use DDB because we want to be able to do:

See https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/main/cdk/custom-auth/magic-link.ts and look for ddbDocClient to see the calls.

Also DDB currently supports virtually unlimited throughput (it's under your control by setting the read/write capacity on the table) while setting user attributes is limited in RPS by Cognito (25 currently, see https://docs.aws.amazon.com/cognito/latest/developerguide/quotas.html#category_operations). For that reason it is actually often recommended to not set user attributes during each sign-in, as it may lead to throttling by Cognito (if your sign-in rate is >25 RPS).

KyleKotowick commented 6 months ago

@ottokruse Thank you for the info! This all makes sense.

I was hoping you might also be able to tell me a bit more about the theory behind the magic link KMS signing. I understand that signing is useful if the link was provided to some external system (OIDC-style), but I'm unclear on the specific value it adds in this setup.

Say you generate a cryptographic random string and include that in the magic link token JSON map. You store that value in DynamoDB as well. You don't include a signature. If the user provides this token (by clicking the magic link), and you verify that the complete token (including username and random string) matches the same value in DynamoDB, you could be certain that the link is legitimate, no?

Perhaps an easier question is, if you didn't use KMS to sign it but included a random string that was verified on login with the link, what security vulnerability would exist that would be prevented by including a signature?

ottokruse commented 6 months ago

With a string you'd have to store it in the DB table to be able to check it later in verify auth. That suddenly makes the DB table pretty sensitive: if you can write to it, you can create codes to sign in on behalf of a user.

With KMS and the current design, the KMS key is the safeguard, and you need to have access to it. You can give admin access away to the DB table even, or dump the table on pastebin for all you care, as the info in it is useless to threat actors. The KMS key is the safeguard.

ottokruse commented 6 months ago

This is of course security in depth! And I can see you could make a judgement call in your context, to be fine without KMS and do it with crypto random strings instead. But we wanted to insist on the highest security bar here, and therefore did it with KMS.

GREAT QUESTION by the way. Thank you for diving in our design as you did. I'd be curious to hear feedback/thoughts on my response.

KyleKotowick commented 6 months ago

@ottokruse That's what I figured was the main benefit (read access to the table isn't a security hole), but I asked because in the Magic Link readme, it says:

Using AWS KMS enables the feature that Magic Links might be requested in one browser, but will still work when actually opened in another browser (e.g. when a mobile e-mail app opens the link in its integrated browser). Even though the auth session would re-initialize in that case, the KMS signature can be verified to make sure it was us who created the Magic Link.

This description is misleading, because (a) you don't need a signature for the link to work in another browser, and (b) you don't need the signature to verify that it was "us" that created the token. Both of these could be accomplished just as easily with an unguessable random string that is included in the link and that we store on the back-end. KMS serves a separate purpose that isn't discussed in this README.

ottokruse commented 6 months ago

Okay fair. Interested to attempt a PR to improve the README?

KyleKotowick commented 6 months ago

@ottokruse Sure, #173

ottokruse commented 6 months ago

Thanks @KyleKotowick !