Introduction

As a full Stack mobile developer, I’m want to provide value for my clients and make development more efficient. AWS Cognito and Xamarin Forms does this. It is becoming common to have mobile apps with a server component. AWS Cognito provides turnkey authentication and authorization that can be leveraged using Xamarin for both Android, iOS and UWP.

Adding AWS Cognito authentication/authorization to your Xamarin Forms apps, allows access to a wider range of AWS services. Access to services can be controlled on a user-by-user basis, and can be enhanced with groups. These can map to IAMA roles, allowing fine grained access.

This article explains authentication, talks about its complexities and introduces a secure authentication service: Amazon Cognito. By the end of this blog post, you should have the know-how to create a Xamarin App that utilizes AWS Cognito for its authentication system, with a JWT bearer token that you can save/use for other AWS or 3rd party backend services.

Authentication

Authentication is more complicated that seems on the surface. First, security is hard to get right. It is like a chain where only one link needs to be broken for the system to be compromised. Even following current best practices, it is difficult to get right, and often relies on libraries that are black boxes, for things such as encryption, token management and password storage. Second, there is other functionality that is required such as password reset, email or phone number authentication or two factor authentication. This means implementing a good authentication system is not quick nor easy.

There are many turnkey systems that provide authentication services out of the box: Auth0, Google Auth, Azure B2C, Amazon Cognito, and many others. However, each comes with its own share of problems. For example, they can be difficult to use, provide insufficient control over the login process, have too much vendor lock-in, or are not compatible with facilities outside the provider that provider. Some login experiences need to be crafted for specific subcategories of users. End users who don’t have the experience with mobile devices or know what to expect sometimes have difficulty with mobile apps, because there is a high degree of general “know how” that people pick up over time. In these cases, it is better to have a simple UI login experience that guides them through the entire process. Other users may be very savvy, preferring an optimized experience.

For these reasons, when using a turnkey authentication service for authentication is is important to have complete control over the UI. As of this writing, most require embedding webviews into the application that are difficult to customize. Getting consistent look and feel and behavior between my native application and this webpage concerns be problematic, especially if the webpage is hosted by the service providing the authentication facilities. Some provide customization, but can be insufficient because they rely on css, which mostly supports changing the style of existing UI elements.

Amazon Cognito

Amazon Cognito solves a lot of these issues. It is easy to incorporate, provides customization over the login ui process (even allowing custom screens), and provides back-end hooks into the registration and authentication process for specialized authentication needs.

AWS Cognito also supports Java Web Tokens as the token format. JWT (Java Web Tokens) is a standard that can be used with any service that takes follows OpenID. It is even possible to use a token from Amazon Cognito with other cloud services such as Azure or Google.

Amazon Cognito provides many options for tailoring the solution, including optional web pages for login screens. Avoiding web ui is done using an authentication mode called Secure Remote Protocol (SRP). It is a standard, build on top of Password Authenticated Key Agreement (PAKE). It is a robust protocol, technically a zero-knowledge-proof, designed to thwart dictionary and man-in-the-middle attacks, a particular issue with API only authentication. This is what allows authentication to be implemented with completely custom UI.

Mentioned earlier AWS Cognito provides a bearer token using the standard JWT format. JWTs are often used with many APIs and back-end services. Additionally, the token can be associated with specific Amazon IAM roles or users giving fined grained authorization for other AWS facilities such as dynamodb, S3, or API Gateway. This provides the means for robust back-end security.

Cognito can work with email or phone number account identifiers providing validation through email or SMS messages. both of these work well in the mobile experience because the validation is just a four or five digit number. In the case of SMS and Android, the application can actually inspect incoming SMS messages to see if it is a validation response.

Amazon provides libraries that are compatible with C#/.Net and Xamarin. This is what is needed for cross-platform solutions between Android and iOS using Xamarin Forms.

There are a bunch of different ways to set up Amazon Cognito, but for this article we’ll use a simpler approach using only email without two factor authentication, and no IAMA roles.

Cognito Setup

Amazon Cognito can be found at https://console.aws.amazon.com and selecting Cognito from the services after logging in. Here are the steps to create a basic Cognito User Pool that can be used with a mobile application:

To set up a basic pool:

  1. On the Cognito service page in the AWS console, select “Manage User Pools”
  2. Press the “Create a user pool”
  3. Enter the name of the pool. This is an internal name.
  4. Select “Step Through” for custom settings.
  5. Choose Username policy. For the purposes of this blog post, we chose “Email address or phone number” and select “Allow email addresses”. This means the user uses their email address as their sign-on name.
  6. Skip standard attributes and custom attributes.
  7. Choose a password policy. (The default is ok)
  8. Choose how you wish users to sign themselves up. Most apps would select “Allow users to sign themselves up.” Enterprise apps might want to select “Only allow administrators to create 1. users” For this post, we chose allow users to sign themselves up.
  9. Make sure MFA (multi-factor authentication) is disabled, and use email for registration is selected.
  10. No need to create a role for SMS messages because we are not doing SMS verification.
  11. Select “Code” for verification type. This means the user will receive a code in email they will use to provide validation.
  12. The email messages can be customized later.
  13. Skip tags.
  14. Select Add an app client. Add the client, Important: Unselect Generate client secret.
  15. Leave custom triggers blank. (These can be added later)
  16. Create the pool.
  17. Make a note of the pool id, pool secret and app client id. You can return to the console later if you have forgotten these.

Now we should have a pool and app client created.

Cognito in a Xamarin Application

The next thing to do is create the Xamarin Forms app. For this we create a standard Xamarin Forms App. Once the app is created, we have to add the Amazon libraries. As of this writing, we need to incorporate a few nuget packages from Amazon:

If SignUpAsync fails, it will throw an exception. If it succeeds, and use email as login or email the email attribute is set, Amazon Cognito will send an email with an authorization code. If you’ve enabled phone numbers as logins, or set a phone number in a attribute, it Amazon will send a text message with the authorization code. Once the user has the authorization code, we need to tell Amazon Cognito about it. That is done using code like this:

Attempting a Login: Allowing a user to login is initially straight forward.

Generally failed login attempts will throw exceptions such as NotAuthorizedException or UserNotFoundExecption. It is possible for the StarWithSrpAuthAsync call to succeed but not allow the user to login. An example of this is if the user needs to enter a new password, which is the case if an administrator creates the user, or the password has been reset. That information is kept in the ChallengeName property returned from the authentication call. Amazon Cognito also provides many things for a complete authentication system and experience, including forgot or reset password, change password, and updating attributes in the user profile.

Using Login Results

When a user successfully logs in, several things are returned, including:

  • IdToken
  • AccessToken
  • RefreshToken

The IdToken contains information (claims) about the user. It is defined in the OpenID Connect specification. The AccessToken also contains claims about the authenticated user, but does not have all the same information. It is primarily used to authorize operations in the context of the the specific user. Both IdToken and AccessToken will expire in an hour. This if for security reasons. If they didn’t expire, and someone were to gain access to the token via a man-in-the-middle attack, then the could reuse the token. When the token expires, whatever operation you are trying with that token, will fail with some kind of authorization exception. The particular error will come from whatever is consuming the token. Different APIs will give different responses, a common one for REST APIs is to return an HTTP 401 response. This is where RefreshToken is used. It can refresh, or request updated versions of the Id and Access tokens. This is done with this code:

This returns values like the initial sign in call, but doesn’t return a refresh token. The refresh token value is something will want to hold on to if the user is going to stay logged in between sessions. Conclusion Using the Amazon Cognito .Net SDK, we quickly incorporated a robust yet compatible authentication system. This allowed us to quickly added functionality for accessing server and cloud capabilities using standards such as Java Web Tokens and OpenId Connect. This was added to a Xamarin Forms application in cross platform code, allowing the same login experience on both iOS and Android.

A few other notes:

  • The tokens, especially the refresh token, are important and should be stored in a secure or encrypted place.
  • Logging out of the app is really just deleting the tokens.
  • Another way preserve the tokens without having to refresh is to enable “Remember devices” in the Cognito settings.
  • When you create a user in the AWS Cognito control panel, it can be created in the email validated state, however the user state will be FORCE_CHANGE_PASSWORD, meaning the user must change the password after logging in. (Which this sample code handles). See for more information: https://stackoverflow.com/a/45253010/57114

Usable Code on Github

There is a full project on github at https://github.com/curtisshipley/CognitoForms

This provides drop-in classes that you can add to your projects to provide authentication, registration, accepting a validation code, and updating a password.

For Xamarin Forms, the authentication code can be placed in a shared library, only requiring it to be written once but used on multiple platforms simultaneously.

Feel free to contact me at curtis@saltydogtechnology.com for questions or comments.