Front-End Web & Mobile
Integrating Amazon Cognito with the Android AccountManager API
This is the fourth part in a six-part series on synchronizing data within an Android mobile app to the AWS Cloud. Check out the full series:
- An Introduction to the Sync Framework for Android
- Building a ContentProvider for Android
- Using a ContentProvider in Android Mobile Apps
- Integrating Amazon Cognito with the Android AccountManager API (this article)
- Building a Synchronization Endpoint with AWS Mobile Hub
- Building a Synchronization Framework for Android
If you have an Android phone, you can go into Settings > Account and add accounts for your internet services like Gmail. However, this area is an extensible on-device secure identity store that other applications can use to store their own credentials. If your app needs to synchronize with a cloud-based data store, you need to implement your own account manager to use the Android Sync Framework.
You could provide a sign-in and sign-up UI within your app and never have to think about the Account Manager. Many apps do this already. However, there are several advantages to Account Manager that make it worthwhile to understand. The Account Manager is the standard method on Android to authenticate users. It simplifies the process for the developer. It automatically handles several scenarios for you (like access denied and multiple token types) and can easily share authentication tokens between cooperating apps. Finally, and likely most importantly if you are reading this series of articles, it has support for background processes like synchronization services.
Your account type also appears as an entry within the phone settings, as shown in the following image.
To integrate with the Account Manager, you:
- Create the authenticator, which does the actual work.
- Create the activities where users enter their credentials.
- Create the service so we can communicate with the authenticator.
Whenever your app needs an authentication token, it only communicates with one method – the AccountManager.getAuthToken() method. The Account Manager authenticates the user, displaying a UI that is designated, if necessary.
You can implement the authentication service (including the authenticator, activities, and service) as an apklib so that it can be used by multiple apps, or you can integrate it into a single apk with your app.
Create the authenticator
The account authenticator is the class that the Account Manager uses to fulfill all the tasks necessary – getting the stored authentication token, presenting the account login-in screen, and handling the user authentication with the server (in this case Amazon Cognito). This is code you need to write by extending the AbstractAccountAuthenticator. There are a number of methods to implement:
- addAccount() is called when the user clicks the Add Account button in the Account page of the Settings app.
- getAuthToken() is called when an app (including your app and the sync framework) tries to retrieve an authentication token for the user from a previous successful login on this device. If one is not available, the user is prompted to log in.
In both cases, this is mostly boilerplate code. You substitute the name of your Activity, which does the authentication, and you manage the token type. In the following code, we show only one token type.
The AuthTokenType class is as follows:
This is a read-only mapping of the valid auth token types (we only have one called rw) and their common display names (Read-Write). You can add other types here and provide the appropriate code to produce them when you prompt for authentication.
Create the Activity
You must provide a UI that the user can interact with during the sign-in and sign-up process. This is an Activity class that derives from AccountAuthenticatorActivity. This activity shows the user a log-in form, authenticates with the server, and returns the result via the setAccountAuthenticatorResult() method to the calling authenticator. In this case, we use the IdentityManager, which is built into the AWS Mobile SDK for Android. To set up the appropriate configuration for Amazon Cognito, use AWS Mobile Hub:
- Open the AWS Mobile Hub console. If you do not have an AWS account, sign up for the AWS Free Tier.
- If this is not your first project, click Create a new project.
- Type a name for your project, and then click Create project.
- Click the User Sign-in tile.
- Click Email and Password.
- Scroll to the bottom of the page, and then click Create user pool.
- Click Integrate in the left menu.
- Click Download in step 1.
This downloads the awsconfiguration.json file that contains all of the resource constants for your project. You have no configured your project to include a simple sign-up / sign-in flow with email verification of the account.
To add the awsconfiguration.json file to your project:
- Expand the app folder.
- Right-click the res folder.
- Choose New > Android resource directory.
- Select Resource type: raw
- Click OK.
- Place the awsconfiguration.json file in the created app/src/main/res/raw directory within your project.
Next, add the AWS Mobile SDK for Android to your project. Add the following to the app/build.gradle file:
Then edit AndroidManifest.xml to include appropriate permissions to access AWS services:
Finally, create the CognitoAuthenticatorActivity.java file as follows:
Most of the work is done within the IdentityManager onSuccess callback, which adds the account to the Account Manager if requested. IdentityManager then sets up the result with the appropriate callback information (most notably the authentication token) and finishes the Activity.
We are using the native UI included in the AWS Mobile SDK for Android to ask for credentials. The native UI supports the following authentication flows for Amazon Cognito User Pools, Facebook, and Google: sign-up with validation, forgot password, and sign-in with optional multi-factor authentication.
Create the service
The final class to write is the Android service that binds everything together. This is a remarkably simple class because the AbstractAccountAuthenticator (which our CognitoAuthenticator class extends) already implements the IBinder interface that we would normally have to implement. You can use that to bind the authenticator to the service:
The service also needs to be registered with the Android system. This requires you to define the service in the AndroidManifest.xml file within the <application> node:
There are three parts here:
- The AUTHENTICATE_ACCOUNTS permission was added to the list of permissions.
- The CognitoAuthenticatorActivity activity was added as a valid activity.
- The <service> node was added, referencing an authenticator.xml file.
The authenticator.xml file describes the authenticator so that it can be placed in the Accounts list appropriately. It is placed in the xml resource directory. Create the xml resource directory (right-click the res folder, select New > Android resource directory, select Resource type = xml, and then click OK), then add the authenticator.xml file:
The accountType is defined within the CognitoAuthenticatorActivity. You can use any drawable for the icons. The label is displayed on the Accounts page within Settings. Finally, you must provide a list of preferences. These can be anything you wish, and will be displayed in the Accounts preference page. For example:
Testing Account Manager
Run your app in the Android emulator, then immediately switch to the home screen and swipe down to access Settings. Choose Accounts, and then choose Add Account. You will see the same screen you saw before. Click your account authenticator and the following screen displays:
Create a new account (including validation), and then sign-in. If you go back to the Accounts page after signing in, you see the preferences screen you defined:
Wrap up
There are a number of issues with this authenticator that we didn’t cover. For example, if you fail to sign in successfully, you cannot close the authenticator. You also can’t delete an account. These issues would need to be solved before this code is considered production ready. However, we will move on and look at the approved method of synchronizing offline data with a cloud service in the next article.