AWS Cognito custom auth - sending metadata to a challenge lambda functions
Asked Answered
P

2

12

I'm developing a custom passwordless auth to sign into a Cognito user pool. I will describe what I'm trying to implement in case anything is silly. I want a user to enter their email address, then receive a magic login link via email, and when they click on that be taken back to the site and be logged in.

This uses custom auth lambda functions to define/create a challenge with a time based password and send it to the user in an email. I am having a couple of problems:

Problem 1)

When the user returns with the code they might not be in the same browser/device and certainly won't be in the same tab so they don't have the session, meaning I need to call cognitoUser.initiateAuth again. This goes through the define/create challenge lambdas again so a second email gets sent even though at this point the user is coming from the email link so already has the code. Note: the session id is not available in the event object when the challenge is created, also I've read these sessions only last 3 minutes and my time based passwords will last ~15minutes, so I don't think I can include the session id in the email.

Problem 2)

You can login from a few places (browser, android app, etc) and I would like to be able to include the url or at least protocol as a parameter to control what gets sent in the email, e.g. if you entered your email address in the android app then the email you get would be myapp://login?code=xxx and if you did it on the web it would be https://example.com/login?code=xxx

It seems like I would be able to implement both of these to work properly if only I could find some way to send custom metadata through to the DefineChallenge and CreateChallenge lambda such that it would appear in the event object. I thought adding ValidationData to the AuthenticationDetails object would do this, but that information doesn't appear in the event object in the Lambda fns.

The workaround I've found is to create a new client id for every situation - one for initiating auth, one for redeeming token, and repeat for each different protocol. But that is a lot of client ids quickly - a pain to mantain and clumsy.

So tl;dr is: I want to send custom metadata from my cognitoUser.initiateAuth(...) call in JS and have it available in my Define/Create Challenge lambda fns.

Percolator answered 4/6, 2018 at 12:33 Comment(3)
Did you ever solve this? My scenario is pretty much exactly the same.Travesty
Having the same issue. 'clientMetadata' parameter in lambda is not there by either passing it as 'validationData' or 'clientMetadata' using amplify.Auth.signIn.Sexagenary
Unfortunately AWS Cognito does not pass clientMetadata to DefineAuthChallenge or CreateAuthChallenge, see the InitiateAuth docs here, under Request Parameters. It is misleading that clientMetadata is mentioned on the createAuthChallenge docs though.Mennonite
R
3

You can split the authentication process into multiple custom auth challenges. This allows custom auth state to be supplied via the challenge response as client metadata.

Auth session state must be persisted in a database in order to be shared between devices.

Your custom login flow will probably have two challenge steps: the first prompts for auth type and the second prompts for the secret code. The action taken by the "Create Auth Challenge" Lambda will depend on the auth type. If the auth type is "Email" then the secret code and magic link are generated, stored in DynamoDB and emailed. If the auth type is "MagicLink" then the secret is loaded from DynamoDB. Clicking on a Magic link will initiate a new auth session and automatically supply all the challenge answers.

There are a few other things to consider:

  1. Your magic link needs to encapsulate the Cognito username as well as the one-time secret and probably some other session id that is used as a key in dynamodb.
  2. You probably should not put app-specific links into your emails. Instead associate your domain with your app and/or leverage the redirect URI parameter of your web-based login page.
  3. You can also access custom Cognito user attributes from the Lambda function which can be used to indicate user login preferences (eg Email vs SMS for login codes).
Rolf answered 12/10, 2020 at 4:1 Comment(0)
V
2

Building on what @nathan said the cognito flow is pretty confusing but it is doable to sort of pass metadata via multiple steps. The flow is basically -

  1. Initiate the login request from the UI.
  2. The first thing that gets hit is the define auth challenge lambda. This looks at past request.session items to figure out what has or has not been done yet. On first go around the session list is empty so you just ask for a challenge. The important part here is you aren't specifying which challenge, just that a challenge is required. The define step doesn't know about the custom flow/metadata yet.
  3. Next in the flow is create auth challenge which is where you can set metadata on the flow and if you need to collect more information back from the user this is your chance. You can set the challengeMetadata field to be a string representing the "asking user for more data" and in the publicChallengeParameters just set some data being like "i need more".
  4. Now it hits your UI, your ui can look at the response of the initial auth request and should see data in the user challengeParam field which should map to the public params you set in create auth. Your ui should submit a challenge response which is all the extra metadata you might need to do the task.
  5. Now it goes to verifyAuthChallenge. Here you will look at the session and see that the session metadata is the "asking user for more data" string you set earlier and you can just auto verify it (or do custom verification,etc). At this point you will probably want to store the data you collected in dynamo or something keyed off the username that is trying to log in. It's been verified, you have the user name, etc.
  6. Now it goes back to "define auth challenge". Here you now can look at past sessions and see that the "get more data" step is done and you can either ask for more data (a 2fa code for example) or go ahead and say they are complete. If you do another round and need the original metadata at this point you can load it from dynamo and then do whatever you need to do.

In this silly way you can now collect and store metadata and persist it across the different custom auth modes.

I wish clientMetadata was just passable and we didn't have to jump through these hoops but work with what ya got I guess.

Vittoria answered 22/3, 2023 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.