0

I want to allow a user to enter their username/password in a field. Upon continuing, I want to run a check to see if that user already exists in the user pool. If they do, log them in and continue with app, if they do not, move to account creation flow where they will be instructed to add name, phone number, email etc.

I cannot find documentation on how to log a user in using AWS Cognito. I should be able to pass username/passcode in a call and get a response back that says User Exists/User does not exist or whatever! Am I missing something here?

Any help would be greatly appreciated. I've scoured the documentation...

palla
  • 3
  • 1
  • 2
  • Are you using a social login (i.e. Facebook, Google, etc.)? What version of the SDK are you using? How many users do you have? Passing in a username/password to determine if an account exists is a giant security hole as it allows anyone to figure out valid user names. – stdunbar Oct 21 '20 at 19:51
  • No not social login. we have an application where users login by providing username and password which is now being authenticated from mySQL database. But now we wanted to move the users from mySQL database to Cognito User pool. However every user will have an associated email linked to their account. we have around 20k users as of now. So in order not to ask every user to reset their password. – palla Oct 22 '20 at 18:23
  • I wanted to follow this one at a time migrate use case where user will try to login to our app, and then we try to authenticate the user from user pool. If there is no such user in user pool, then it authenticates against mySQl and if it is success then it migrates that user to userpool – palla Oct 22 '20 at 18:23

3 Answers3

2

To check if the user exists or not, all you need is username.

So for your scenario, trigger the myMethod() below after user enters username and password. That will

  1. Check if the username is already in user
  2. If username exists, perform sign in
  3. If username does not exists, create account

/**
* let's say you call this method when user enters username and password
* @param context context
* @param identityProvider cognito client
* @param username user entered username
* @param password user entered password
* @return
*/
private void myMethod(Context context, AWSCognitoIdentityProvider identityProvider, String username, String password) {
    
    boolean userExists = userExists(context, identityProvider, username);
    
    if(userExists) {
        // perform sign in with provided password
    } else {
        // create account
    }
}


/**
* @param context context
* @param identityProvider cognito client
* @param username user entered username
* @return true if username is already in use, false otherwise
*/
private boolean userExists(Context context, AWSCognitoIdentityProvider identityProvider, String username) {
    LambdaLogger logger = context.getLogger();

    try {
        AdminGetUserRequest getUserRequest = new AdminGetUserRequest();
        getUserRequest.setUserPoolId("cognitoPoolId");
        getUserRequest.setUsername(username);

        AdminGetUserResult getUserResult = identityProvider.adminGetUser(getUserRequest);

        return true;
    } catch (UserNotFoundException userNotFoundException) {
        logger.log("UserNotFoundException! " + userNotFoundException.toString());
        return false;
    } catch (Exception e) {
        return false;
    }
}
ᴛʜᴇᴘᴀᴛᴇʟ
  • 4,466
  • 5
  • 39
  • 73
0

Instead of having to do a full scan of your Cognito user pool every time, I'd use the ability of Cognito to trigger an event. For your use case Cognito can run a Lambda. You're interested in the Migrate User trigger. Basically what happens is that when the user tries to log into your system through Cognito and the user doesn't exist in the pool, a trigger is fired to let you log the user in and migrate them to Cognito.

The data coming in looks like:

{
    "version": "1",
    "triggerSource": "UserMigration_Authentication",
    "region": "us-west-2",
    "userPoolId": "us-west-2_abcdef",
    "userName": "theusername@example.com",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "yourclientid"
    },
    "request": {
        "password": "theuserpassword",
        "validationData": null,
        "userAttributes": null
    },
    "response": {
        "userAttributes": null,
        "forceAliasCreation": null,
        "finalUserStatus": null,
        "messageAction": null,
        "desiredDeliveryMediums": null
    }
}

Your Lambda will consume this and ultimately take the username and password and determine if it is valid. If it is, you will pass back information in the response.userAttributes field along with things like if you want to send a Cognito welcome email (messageAction) and some other values. For example, you may send back:

{
    "version": "1",
    "triggerSource": "UserMigration_Authentication",
    "region": "us-west-2",
    "userPoolId": "us-west-2_abcdef",
    "userName": "theusername@example.com",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "yourclientid"
    },
    "request": {
        "password": "theuserpassword",
        "validationData": null,
        "userAttributes": null
    },
    "response": {
        "userAttributes": { "email":"theusername@example.com",
                            "email_verified": "true" }
        "forceAliasCreation": null,
        "finalUserStatus": "CONFIRMED",
        "messageAction": "SUPPRESS",
        "desiredDeliveryMediums": null
    }
}

Your Lambda will look something like this in Java:

public class MigrateUserLambda implements RequestStreamHandler {

    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        LambdaLogger logger = context.getLogger();

        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(inputStream);

        logger.log("input is " + objectMapper.writeValueAsString(rootNode));

        String email = rootNode.path("email").asText();
        String password = rootNode.path("request").path("password").asText();

        // verify user name and password in MySQL.  If ok...

        String triggerSource = rootNode.path("triggerSource").asText();

        if( triggerSource.equals("UserMigration_Authentication")) {
            JsonNode responseNode = rootNode.path("response");
            if (responseNode != null) {
                ((ObjectNode) responseNode).with("userAttributes").put("username", "theusername@example.com" );
                ((ObjectNode) responseNode).with("userAttributes").put("email_verified", "true" );
                ((ObjectNode) responseNode).put("messageAction", "SUPPRESS");
                ((ObjectNode) responseNode).put("finalUserStatus", "CONFIRMED");
            }
        }

        String output = objectMapper.writeValueAsString(rootNode);

        OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
        writer.write(output);
        logger.log("sending back " + output);

        writer.close();
    }
}
stdunbar
  • 16,263
  • 11
  • 31
  • 53
-1

To list users you can use AWS Java SDK:

public static void list() {
    AwsBasicCredentials awsCreds = AwsBasicCredentials.create(AWS_KEY,
            AWS_SECRET);

    CognitoIdentityProviderClient identityProviderClient =
            CognitoIdentityProviderClient.builder()
                    .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                    .region(Region.of(REGION))
                    .build();

    final ListUsersRequest listUsersRequest = ListUsersRequest.builder()
            .userPoolId(POOL_ID)
            .build();

    ListUsersResponse result = identityProviderClient.listUsers(listUsersRequest);

    System.out.println("Has users:"+result.hasUsers());
    result.users().stream().map(u->u.username()).forEach(System.out::println);
}

it requires next dependecies (please use latest versions):

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>aws-core</artifactId>
  <version>2.13.57</version>
</dependency>

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>cognitoidentityprovider</artifactId>
  <version>2.13.57</version>
</dependency>

Here is a code sample of how to login user from Java.

Yuriy P
  • 1,330
  • 9
  • 16
  • 1
    This solution will NOT work - `ListUsersRequest` can only return 60 users at a time. If your cognito has hundreds or thousands of users, you will first have to do for loop (possibly hundreds of times) get all users first. – ᴛʜᴇᴘᴀᴛᴇʟ Oct 24 '20 at 02:02
  • @ᴛʜᴇᴘᴀᴛᴇʟ you are right, I didn't think about the number of users, hence suggested a bad solution. – Yuriy P Oct 26 '20 at 13:53