AWS Mobile Blog

User Sign-in and Sign-up for Ionic Mobile Apps with Amazon Cognito

This post was written by Brice Pelle, AWS Technical Account Manager.

In our previous blog post, we described how to secure and deploy RESTful interfaces for the Ionic Framework using the AWS SDK for JavaScript and AWS Mobile Hub, with support for authenticated and unauthenticated users. This allows guest users to use your app, with limited privileges, without having an account. When users are ready, they can sign up for an account and sign in. This gives them access to more features in the app. Sign-up and sign-in functionality in a mobile app can be difficult to implement securely and challenging to manage.

In this post, we take a deeper look at how AWS Mobile Hub along with Amazon Cognito provide a foundation for user sign-up and sign-in. We describe how these services integrate in your Ionic Framework mobile app. We use the same sample app introduced in the previous post to showcase the implementation. Refer to the previous blog post for instructions on how to deploy backend resources with Mobile Hub and how to launch the app.

Amazon Cognito and the User Sign-in Feature in Mobile Hub

Amazon Cognito makes it easy for developers to address authentication and authorization concerns. You can create and maintain your own user directory with an Amazon Cognito User Pool. The sample app integrates this feature to securely implement sign-up with email verification and sign-in. After users sign in, you can give them access to specific AWS resources with Amazon Cognito Federated Identities. The sample app integrates the Federated Identities feature to request temporary credentials for signed in users. Amazon Cognito Federated Identities also supports granting access to unauthenticated users; this functionality is implemented in the sample app. The sample app uses the retrieved credentials to sign the API requests to the backend.

This Amazon Cognito functionality is easily set up using Mobile Hub. The Mobile Hub project that sets up the sample app backend infrastructure uses the User Sign-in feature to provision the Amazon Cognito resources.

The configuration consists of a custom sign-in provider backed by an Amazon Cognito User Pool. This configuration does not require users to sign in to access AWS restricted resources (optional with Amazon Cognito Federated Identities). AWS Mobile Hub creates a user pool, an app client, and an identity pool that enables access to unauthenticated identities. It then adds the user pool and app client as an authentication provider with the identity pool. The user pool, app client, identity pool IDs, and the region are included in the aws-config.js and aws-exports.js files.

const aws_cognito_identity_pool_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const aws_cognito_region = 'xxxxxxxxx';
const aws_user_pools_id = 'xxxxxxxxxxxxxxxxxxx';
const aws_user_pools_web_client_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';

In the sample app, aws-config.js is used and placed in the ./client/src/assets/ folder. The data is made available to the sample app through the AwsConfig class, which is implemented in the ./client/src/app/app.config.ts file.

The authentication service

To support the registration and authentication process, the sample app implements the AuthService in ./client/src/app/auth.service.ts and provides the following functions that interact with the Amazon Cognito APIs:

  • signin: signs in a user and notifies subscribers of a sign-in event
  • signout: signs out a user if they exist, resets credentials (falling back to guest access) and notifies subscribers of a sign-out event
  • register: registers a new user
  • confirm: confirms a user

The service imports the AWS SDK for JavaScript and the Amazon Cognito Identity SDK. The service interacts with a single user pool defined in the service constructor by specifying the user pool ID and the app client ID.

constructor(private config: AppConfig) {
  AWS.config.region = this.config.get('region')
  this.poolData = { 
    UserPoolId: this.config.get('userPoolId'), 
    ClientId: this.config.get('appId') 
  } 
  this.userPool = new CognitoUserPool(this.poolData)
  this.refreshOrResetCreds() 
}

Registration, confirmation, and sign-in are done through interaction with the user pool. Sign-out consists of clearing the user’s session and cached information.

Signing up and Signing in

The sample app allows users to view a summary of tasks on a dashboard but users must sign in to manage their own tasks. Before a user can sign in, they need to register and create an account. The sample app implements a modal component in ./client/src/modal/login/ that presents a view with the option to either sign in (for an existing user), or register (for a new user). This modal implements a set of forms, using Angular’s NgForm, which allows the user to enter their account credential information (captured in the credentials variable).

The user can click the Register button to switch to the “register” page. Here, the form prompts the user to enter username, email, and password information. The component uses the information to create a user in the user pool by calling AuthService.register. If an error occurs and registration is not successful, the error response returned from the backend is displayed.

register () {
  this.auth.register(this.credentials).then((user) => {
    console.log('register: success', user)
    this.page = 'confirm'
  }).catch((err) => {
    console.log('error registering', err)
    this.setError(err.message)
  })
}

Upon successful creation of the account, Amazon Cognito sends a confirmation code to the provided email address to verify the account. The component automatically switches to the “confirm” page where the user can enter the received code that the app then sends to Amazon Cognito by calling AuthService.confirm.

confirm () {
  this.auth.confirm(this.credentials).then((user) => {
    this.page = 'login'
    this.setMessage('You have been confirmed. Please sign in.')
  }).catch((err) => {
    console.log('error confirming', err)
    this.setError(err.message)
  })
}

If the supplied code is correct, Amazon Cognito updates the user account’s state to confirmed. The component switches back to the “login” page where the user can now enter username and password information. The component calls the AuthService.siginin function to sign in the user with Amazon Cognito. The sign-in process is handled by Amazon Cognito using the user pool custom authentication flow with the Secure Remote Password (SRP) protocol.

signin () {
  this.auth.signin(this.credentials).then((user) => {
    this.dismiss()
  }).catch((err) => {
    console.log('error signing in', err)
    this.setError(err.message)
  })
}

Acquiring credentials to sign API requests

Upon successful sign-in, Amazon Cognito returns a set of tokens. One of these tokens is a JSON Web Token (JWT) ID token that can be used to request temporary credentials from the identity pool. In the sample app, the AuthService does this in a lazy fashion: the credentials object is built when the user signs in (by calling AuthService.saveCreds), but the actual keys are not fetched (by calling AuthService.getCredentials) until an actual request to the backend is made.

private saveCreds (session, cognitoUser?): void {
  this.session = session
  if (cognitoUser) { this._cognitoUser = cognitoUser }
  this.setCredentials(this.buildCreds())
}

private setCredentials(newCreds) {
  AWS.config.credentials = newCreds
}

private buildCreds () {
  let json = this.buildLogins(this.session.getIdToken().getJwtToken())
  return new AWS.CognitoIdentityCredentials(json)
}

private buildLogins (token) {
  let key = this.config.get('idpURL') + '/' + this.config.get('userPoolId')
  let json = { IdentityPoolId: this.config.get('identityPool'), Logins: {} }
  json.Logins[key] = token
  return json
}

When the credentials are fetched (AWS.config.credentials.get), first a GetId request is sent to Amazon Cognito to retrieve an identity for the current user. If this is the initial GetId request for the user in the identity pool, Amazon Cognito creates a new identity and associates it with the user. Second, a GetCredentialsForIdentity request for the current identity is made. Amazon Cognito returns an AWS access key ID, a secret access key, and a session token. This set of credentials data can be used to sign requests to AWS services including https requests to the Amazon API Gateway backend. In the sample app, requests are made using the Sigv4Http service (see ./client/src/app/sigv4.service.ts) which signs them using the passed credentials.

Next Steps

Now that you know how to implement registration and authentication with Amazon Cognito, you can use Mobile Hub and some of the described techniques to build your own sign-up and sign-in modules in your Ionic mobile app.

Visit the GitHub repository referenced in this post to download the code and get some additional insight on the application. You can visit ionicframework.com to learn more about building Ionic apps.