The login page is the fist thing that most web application users encounter. Account creation is the gateway through which all new application users pass through before they can use a web application. This means that authentication (account creation, login and user data management) is a critical component for most web applications.
This article discusses the Amazon Web Services (AWS) Cognito service and how it can be used to build server side authentication for a Java web application constructed using the Spring framework.
The Java source code for the demonstration application described in this article is available on GitHub, under the Apache 2 software license.
Creating strong authentication software that can resist password attacks requires careful, informed, design. Authentication requires persistent storage of user information (passwords and user account information). This is often supported by a database that is available 24 hours a day, seven days a week (24/7). This database will incur an operating cost, regardless of how much it is used.
For the nderground social network, Topstone Software developed a custom authentication infrastructure that uses the RDS/Postgres database. In addition to the encrypted password and a unique “salt” value, for each user, this database hosts access and security information used by nderground’s groups (the nderground Karass). The RDS/Postgres database was chosen over a NoSQL database like Amazon’s DynamoDB for critical user information storage because Postgres is a transaction safe SQL database.
AWS Cognito simplifies application development by providing an authentication service. In addition to storing password and email information, Cognito can store standard and custom user account values. Cognito is a “serverless” service that does not require the deployment of a 24/7 database server like RDS/Postgres. Cognito’s cost model is “pay as you go”. Currently the first 50,000 monthly users (users who sign-in at least once in a month) are free.
The custom authentication software developed for nderground could be integrated into new Topstone Software web applications. However, this would incur the cost of a 24/7 RDS/Postgres server.
One of the architectural objectives for the Web applications designed and built by Topstone Software is cost that scales with the number of users. A application with only a few users should have a low cost, which increases as the user base and performance demands increase. Using AWS Cognito helps meet this architectural objective by removing the requirement for a database server to store user authentication information.
Understanding AWS Cognito
The different ways that Cognito can be applied can make the Amazon documentation difficult to understand. Documentation that describes an authentication task (for example, account creation and verification via email or SMS text message) describes a number of different use cases in the same section.
At the time this article was written, Amazon did not provide Java reference code for Cognito server side authentication. The AWS Java SDK documentation for the Cognito API has minimal documentation and it can be difficult to understand how to apply the API.
This article and the associated GitHub Java source code have been written to help Java software engineers understand how to integrate Cognito into a Java Web application.
Cognito User Pools
Cognito can support one more more “user pools”. Each “pool” contains the login and user information for a group of users. Production and test user pools can be created so that application testing does not impact the Cognito production user information.
Cognito also provides a user interface that allows management of users within a particular pool. This user interface is available via the AWS console login, which can be protected with two factor authentication.
Cognito Authentication Support
The AWS Cognito service provides support for a wide range of authentication features, many of which are not used in this demonstration application. For example, Cognito can support two factor authentication for high security applications and OAuth, which allows an application to authenticate using an OAuth provider like Google, Facebook or Twitter.
Cognito supports the steps needed to securely create an application account. This includes sending a temporary password to the user’s email and temporary authentication using this password, which allows the user to create a permanent password.
Cognito also supports password reset for an existing account for the case where a user has forgotten their password. Cognito will email the user a code, which can be used to create a new password.
The diagram below illustrates the web page flow for new accounts and forgotten passwords. In this demonstration application, the Spring code supports the control flow via Spring controller objects.
Overview of the Cognito Demo Application
The Cognito demonstration application contains the basic components for application authentication and user management. This includes the server Java code that makes use of Cognito and the web pages associated with authentication. The code and web pages are open source, published under the Apache 2 software license
The Spring Tool Suite (release 3.9.4), which is based on Eclipse, was used to develop the application. The project files are included in the GitHub repository.
The application web pages are built using Java Server Pages (JSP) — a server side rendering technology for building Java web applications. The pages use Bootstrapand Kube for device independent formatting and fonts.
The Spring controllers provide the server side logic for the web pages (e.g., form input validation and processing). Most of the controllers have separate functions that process HTTP GET and POST operations.
The view contains the Java Server Pages (JSP) web pages. Each web page corresponds to a controller class.
The services package contains the code that supports the controllers. In this demo application the core class is AuthenticationService, which implements the AuthenticationInterface. The AuthenticationService class contains the code that interfaces with Cognito.
This article assumes that the reader already has an Amazon Web Services account and has experience with other Amazon services.
Obtaining a Cognito IAM ID and Secret Key
The recommended approach for Amazon Services is that the minimal permissions possible should be used for an application. This compartmentalizes access and security. For example, to use DynamoDB you use DynamoDB permissions and for Cognito you use Cognito permissions.
I used the following steps to create IAM permissions that I used in the Cognito demo application.
- Create an IAM group named cognitoPowerUser. This group has a single set of permissions: AmazonCognitoPowerUser
- Create an IAM user associated with the cognitoPowerUser group (I named this user cognitoPowerUser).
- Click on the link for the user on the IAM users section. Then go the the Security Credentials tab to obtain the Cognito ID and secret key.
- Fill in the ID and Secret key in the cognito_demo.services.CognitoResources.java Interface in the source code you can clone from GitHub.
Constructing the Cognito User Pool
The steps needed to create the Cognito User Pool used by the demo application are described here.
The Cognito Demo Application
Authentication consists of much more than support for login. A screen show of the login page is shown below:
Users create an account by entering their proposed user name, email address and location. The location is an example of arbitrary information that can be associated with a user’s Cognito information.
When this information is submitted to Cognito, the user will be emailed a temporary password. This assures that the email address entered by the user is valid.
When the user logs in using the temporary email address, the Java software in the page controller can check to see if the login used a temporary password. If this is the case, the user can be routed to a page that allows them to reset their password with a password of their choosing.
As the screen shot above shows, the user can also recover a forgotten user name or reset a forgotten password. Forgotten password reset takes place via a code that is emailed to the user.
Authentication software must also support password and user information changes for logged in users. The demo application page is shown below. This web page is a proxy for the main page for an application.
Java Authentication API
The Java API used for authentication in this application is outlined below:
- changeEmail(String userName, String newEmailAddr)
- changeFromTemporaryPassword(final PasswordRequest passwordRequest)
- changePassword(PasswordRequest passwordRequest)
- createNewUser(UserInfo userInfo)
- deleteUser(String userName, String password)
- findUserByEmailAddr(final String email)
- forgotPassword(final String userName)
- getUserInfo(String userName)
- hasUser(String userName)
- resetPassword(ResetPasswordRequest resetRequest)
- updateUserAttributes(UserInfo newInfo)
- userLogin(String userName, String password)
- userLogout(String userName)
This API abstracts Cognito operations so that Cognito dependencies are not introduced outside of the AuthenticationService class. In theory the AuthenticationService class could be replaced by another class that used another authentication infrastructure (e.g., the RDS/Postgres authentication infrastructure developed for the nderground social network).
In practice migrating away from Cognito once it is adopted would be problematic since the application users would have to recreate their accounts using the new infrastructure.