AWS Cognito authentication in Ionic Framework

Mobile Reading Time: 12 min.

  • views3207
  • 10comments
Share

When I was looking for some materials about AWS Cognito User Pools and how to use it by JavaScript SDK, I realized that, without building any demo applications, I will not find answers to my questions such as:

  • Is it ready to make a real mobile application?
  • How much effort is needed to use AWS Cognito during the development process?
  • Why do we need such a solution?

Answers to these questions are very important to our team and to me because, after reading a few materials, I realized that we could speed up our development process, mainly during building mobile applications. AWS Cognito simplifies authentication, user management, and data synchronization without writing backend code or managing any infrastructure. That sounds fantastic, but we always want to verify new technologies before production usage. Let’s proof it. Before doing so, I recommend reading the article , AWS Cognito as a backendless user management, to find out what User Pools is and how it works.

Application scenario

Demo application stores a list of tasks to do, and it was made by the following technology stack:

The scenario of application is simple; it could be described by following user stories:

  1. As a mobile user, I want to register an account by entering an email address and password so that I will be able to login to the application.
  2. As a registered user, I want to confirm my email address using a verification code so that no one else can register my email address in the application.
  3. As a user, I want to add new tasks using a title and description text so that I can store tasks on my mobile devices.
  4. As a user, I want to see a list of my tasks, which should be synchronized between my mobile devices so that my data will be free from loss.
  5. As a user, I want the ability to delete a task so that I can remove it from each device from a single place.

The user interface is very simple and looks as follows:

aws-cognito-ionic-full

 

After specifying the user requirements, I would like to describe the most important pieces of code of the demo application that was published on GitHub: aws-cognito-ionic-demo. You can clone it using the following command:

git clone -b hawatel-blog-article https://github.com/Hawatel/aws-cognito-ionic-demo.git

Library dependencies

The file www/index.html includes the library dependencies to AWS Cognito, which were described in this GitHub repository:

<script src="lib/aws/aws-sdk-2.4.9.min.js"></script>
<script src="lib/aws/aws-cognito-sdk.min.js "></script>
<script src="lib/aws/amazon-cognito.min.js"></script>
<script src="lib/aws/amazon-cognito-identity.min.js"></script>
<script src="lib/aws/dependencies/sjcl.js"></script>
<script src="lib/aws/dependencies/moment.min.js"></script>
<script src="lib/aws/dependencies/jsbn.js"></script>
<script src="lib/aws/dependencies/jsbn2.js"></script>

I noticed that after including sjcl.js library, I got an error message as below:

TypeError: undefined is not an object (evaluating 'sjcl.codec.bytes.toBits')

I resolved it by downloading the sjcl.js library, which was included in this discussion. Additionally, I included the angular-messages library, which allows for the verification of input data such as login name, password, and email address. Other libraries include Ionic/Angular components and my custom factories:

<script src="lib/angular/angular-messages.min.js"></script>
<script src="js/factories/awsCognitoIdentity.factory.js"></script>
<script src="js/factories/awsCognitoSync.factory.js"></script>
<script src="js/components/Todo/todo.controller.js"></script>
<script src="js/components/Login/login.controller.js"></script>
<script src="js/components/Register/register.controller.js"></script>
<script src="js/components/Confirmation/confirmation.controller.js"></script>

Configuration CognitoUser Pool and Identity

The demo application is ready to start if you configure three parameters: userPoolId, clientId, and identityId in the file www/js/factories/awsCognitoIdentity.factory.js. How to get these values? First of all, you need to create your Cognito User Pool. You can read about it in the article “Your User Pools for Amazon Cognito.” You should remember two important things during the user pool configuration:

  1. When you create your user pool, select only one required attribute: email. Mark the email attribute as an alias too. It is necessary only for the demo application. Of course, you can choose what you need in your application.aws_cognito_attributes
  2. In the Apps section, note that the “Generate client secret” checkbox must be unmarked because the JavaScript SDK does not support apps that have a client secret key.aws-cognito-app-1

The rest of the User Pool configuration you can leave in the default state. After creating a User Pool, you will be able to get a Pool Id (userPoolID in the file awsCognitoIdentity.factory.js) from the Pool details section and an App client Id (clientID in the file awsCognitoIdentity.factory.js) from the Apps section.

The next step is the creation of a Cognito Identity Pool, which enables users in your user pool to access AWS resources through your client apps. The advantage is that you will get access to the Cognito Sync service, which allows you create up to 20 key-value datasets for each user. Each dataset cannot exceed 1 MB of data, but sometimes it is enough to store, for example, user preferences, application settings, user data. The demo application uses this service to store the task list. To create an Identity Pool, go to the AWS Console, select Cognito, and then select Manage Federated Identities. During creation, do not enable “Unauthenticated identities” because the demo application does not give any content or functionality for unauthenticated users. Of course, your application could have other user requirements. In the Cognito tab, enter the User Pool ID and the App Client ID, which come from the previously-created User Pool.

 

aws-cognito-app-2

 

When you create an Identity Pool, you will be able to get the last needed configuration setting – Identity pool ID.

 

aws-cognito-app-3

 

If you configure three parameters – userPoolId, clientId, and identityId – in the file www/js/factories/awsCognitoIdentity.factory.js, you can run a command to start an ionic development server and test if you can register, confirm an email account, sign in, and finally add and remove tasks to do. Of course, the demo application does not implement all functionalities that are available in Cognito User Pool, Identity Pool, and Sync services. Its primary purpose was touching on a new feature, Cognito User Pools, which was published in April 2016 by the AWS team as a beta version. On 28 July 2016, AWS announced that the User Pools are generally available, and they are also adding a large collection of new features. Read more about it in this article.

Let’s see how configuration in the awsCognitoIdentity.factory.js file should look:

var cognitoUser = null; //CognitoUser object
// http://docs.aws.amazon.com/general/latest/gr/rande.html#cognito_identity_region
var cognitoEndpoint = '<your_endpoint>'; # eg. cognito-idp.us-east-1.amazonaws.com
var region = '<your_region>'; # eg. us-east-1
var userPoolId = '<your_user_pool_id>';
var clientId = '<your_client_id>';
var identityPoolId = '<your_identity_pool_id>';
AWS.config.region = region;
AWS.config.credentials =
    new AWS.CognitoIdentityCredentials({ IdentityPoolId: identityPoolId });
AWSCognito.config.region = region;
AWSCognito.config.credentials =
    new AWS.CognitoIdentityCredentials({ IdentityPoolId: identityPoolId });
AWSCognito.config.update({ accessKeyId: 'anything', secretAccessKey: 'anything' })
var poolData = { UserPoolId: userPoolId, ClientId: clientId };
var userPool =
    new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);

It is worth mentioning accessKeyId and secretAccessKey, which have been set to “anything“. They are always needed if unauthorized user access is disabled for the user pool. You can put any string there.

Registration process with confirmation

You need a simple form and a registration controller, which are located here: www/js/components/Register. Registration controller uses the awsCognitoIdentity factory with two methods:

aws.signUp = function(username, email, password, callback) {
  setupUser(username)
  var attributeList = [];
  var dataEmail = { Name: 'email', Value: email }
  var attributeEmail =
    new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataEmail);
  attributeList.push(attributeEmail);
  return userPool.signUp(username, password, attributeList, null, callback);
}

setupUser = function(username) {
  var userData = { Username : username, Pool : userPool };
  cognitoUser =
    new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
}

In this demo application, I use only one required attribute – email address. It can be changed by modifying the aws.signUp method. In the register.controller.js file, you can see that I use an email address as the username.

$scope.register = function() {
  $ionicLoading.show({template: 'Loading...'});
  awsCognitoIdentityFactory.signUp($scope.user.email, $scope.user.email, $scope.user.password,function(err, result) {
    if(err) {
      errorHandler(err);
      return false;
    }
    $ionicLoading.hide();
    $scope.$apply();
    $scope.user = {}; //clear register form
    $state.go('confirm');
  });
  return true;
}

Why is the email address as the username so important? Somebody caught me up on a chat, which is available on hawatel.com site, and asked me as follows:

In cognito, I want to make the username and email unique, but I didn’t find any option. I mean for cognito to allow users to be signed up with a duplicate email. It just identifies at the time of confirmation, but I need to stop users to sign up as well if their email is duplicated.

I was quite supprised that it works in such a way because I read the documentation and found this:

The values for all selected aliases must be unique across all users in a user pool. If phone numbers are selected as aliases, one user cannot have a phone number that matches the username of another user. To enforce this behavior and to avoid any conflicts, a username that matches a valid phone number pattern will not be accepted by the service for that user pool. Similarly, if email address is selected as an alias, a username cannot match a valid email format. [source]

I verified it, and in fact, it works in the way the chat comment indicated.

shocked-confused-syllvester-stallone

I can register several different usernames with the same email address. Maybe, I had not realized this is true, or it is a bug. The user pool service should raise an exception or return a warning message indicating that the email address is already taken. That is why I used an email address as a username. Additionally, if you want to change the username, you can create a new username in the additional attribute “preferred_username” and mark it as an alias. This was described in the documentation:

If you want to allow users to have the experience of a changeable username, submit their new “username” value as a preferred_username and choose preferred_username as an alias. Then they can log in with the new value they have entered. [source]

Logging process

As in the registration process, you need a simple form and controller, which are located here: www/js/components/Login. The logging controller uses the awsCognitoIdentity factory with the following methods:

aws.signIn = function(username, password, callback) {
  setupUser(username);
  var authenticationData = { Username: username, Password: password };
  var authenticationDetails =
    new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
  cognitoUser.authenticateUser(authenticationDetails, {
    onSuccess: function (result) {
      initConfigCredentials(result.idToken.jwtToken);
        return AWS.config.credentials.get(callback);
      },
      onFailure: function(err) {
        return callback(err);
      },
  });
}

initConfigCredentials = function(jwtToken) {
  var logins = {};
  logins[cognitoEndpoint + "/" + userPoolId] = jwtToken;
  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: identityPoolId,
    Logins: logins
  });
}

aws.signOut = function() {
  if(cognitoUser != null) cognitoUser.signOut();
  cognitoUser = null;
  AWS.config.credentials.clearCachedId();
  return true;
}

It is worth mentioning the aws.signOut method. Just in case, I added the clearCachedId() method, because, in the past, it resolved the problem with logging multiple users to AWS Cognito via the same browser. The following issue will give you more details about it. In the 2.2.11 version of the aws-sdk-js library, the problem should disappear.

You can analyze the rest of the functionality, such as code verification and dataset creation, where to-do tasks are stored separately for each user.

Conclusion

The main purpose of this simple application was to verify whether the AWS Cognito User Pools is ready to use inside mobile applications that we can produce. I would say that it is wonderful that you have some standards for building common functionalities for every application. Sign-in and sign-up processes are so obvious in each application that it makes sense use such solution like AWS Cognito User Pools. Remember not to waste time on database configuration, server setting up, or building any backend to support registration and logging process into your application. That is why we need a solution to our mobile application to speed up our development, but the biggest advantage is standardization of this process and scalability, which you get as a default feature. Do not worry about handling millions of users. Do not forget that feature – AWS Cognito Identity gives you the possibility of using public providers such as Facebook, Google, or Twitter as additional methods of authentication for the same application where you use the User Pools.

The next thing is that the AWS Team still gets feedback from customers and implements new functionalities to the User Pools service. In the recent article, “Amazon Cognito Your User Pools – Now Generally Available,” I saw a new interesting feature – API Gateway Integration. You can configure an API Gateway to accept an id token to authorize users based on their presence in a user pool.

If you analyze the demo application code, you can see that efforts to implement sign-up and sign-in process are not huge. A few methods give us what we need in each application at the beginning stage. In conjunction with AWS Cognito Sync manager, we have 20 datasets, each of which has up to 1MB available for free for each user account in your user pool. It gives you much more than only sign-up and sign-in functionalities; you can store user settings or any other simple data what you want to keep close to your users. In some cases, you can use datasets as a cache to prevent a lot of requests to your complex application.

Tagged with:

Tags: , ,

  • Agha

    very informative …
    Is there any way to integrate ionic app with AWS mobile services . ?

  • Przemyslaw Mantaj

    Nice to see your comment. What kind of services do you mean? I am asking because AWS mobile services consist of several services, such as Cognito (sign-up, sign-in), DynamoDB (NoSQL DB), S3 (Content Delivery), Lambda (backend code to run), Mobile Analytics, SNS (Push notifications). We can discuss real scenario of your application.
    I can answer in a general way that you can integrate Ionic apps with all AWS mobile services. When a mobile user is logged in by Cognito service, you can give him access to AWS resources. For example, your mobile app can send a request to AWS Lambda by API to run any backend code that can do all that you need. One of the easiest service to integrate with Ionic is Mobile Analytics. All that you have to do is putting a few lines of code to the source code of your application. Here is more information about it: https://github.com/aws/aws-sdk-mobile-analytics-js. Thanks to that you can track users activities in your mobile application. Statistics will be presented in AWS Console after 60 minutes.

  • Przemyslaw Mantaj

    Nice to see your comment. What kind of services do you mean? I am asking because AWS mobile services consist of several services, such as Cognito (sign-up, sign-in), DynamoDB (NoSQL DB), S3 (Content Delivery), Lambda (backend code to run), Mobile Analytics, SNS (Push notifications). We can discuss real scenario of your application.
    I can answer in a general way that you can integrate Ionic apps with all AWS mobile services. When a mobile user is logged in by Cognito service, you can give him access to AWS resources. For example, your mobile app can send a request to AWS Lambda by API to run any backend code that can do all that you need. One of the easiest service to integrate with Ionic is Mobile Analytics. All you have to do is put a few lines of code into the source code of your application. Here is more information about it: AWS SDK Mobile Analytics. Thanks to that you can track users activities in your mobile application. Statistics will be presented in AWS Console after 60 minutes.

  • vgzuazo

    I tried to use your app, but when I try to signup there are error “Username cannot be of email format, since user pool is configured for email alias.”, I think is because you are using the email as username.
    I have generated a userName random, and everything is working, thank you very much for your code. It helped me a lot

    • Przemyslaw Mantaj

      You’re welcome.
      You are right. I needed the same username as an email address in my case. I think when I wrote this article it worked correctly. For other readers, if you see the same error you can unmark the email as an alias.

  • f1lt3r

    I am wondering if you tested this app on iPhone and how the login performed for you? Was it slow?

    • Przemyslaw Mantaj

      When it comes to the performance issue, you can find more info here: iPhone performance
      I hope it will help.

      • f1lt3r

        Thanks for the reply. I have been watching that thread also. Did you try the WKWebView?

  • Ricardo

    Take a look at the following starter which might be helpful for your future projects with Ionic and AWS: https://aws.amazon.com/blogs/mobile/deploy-and-secure-rest-based-mobile-apps-with-aws-mobile-hub/

  • Craig

    This is a great post, I’ve been looking for a simple AWS Cognito / User Pool example, and to also use Ionic makes it nice.. I was able to get everything working except for a sync error after I added one ToDo item. The error in red is “Synchronize failed: exceeded maximum retry count”.. Using the AWS dashboard, I can see the new Cognito Identity was created, but no Datasets are ever created.. I’ve looked everywhere for a solution, but can’t find anything.. Do you have any ideas ? Thank you!