Adding AWS Cognito Sign in and Sign up to Android App
Adding user Sign-in of AWS Cognito to Android app is relatively easy, so this post would be short and easy to follow. Before adding authentication to the project, we should add some prerequisites to our Android project. Basically, there are three prerequisites for each Android app which wants to connect to communicate with AWS. These are:
- Adding AWS Android SDK and other dependencies to the project
- Set Internet and network access permission to the project
- Adding AWS Credential to the project if we want to allow users to authenticate themselves from Google+, Facebook, etc. First, we will use Cognito, so we should add the credentials from our Cognito Pool.
Adding AWS Android SDK
For adding AWS Android SDK to the project, there are several ways, but I use the traditional one which is adding it to the dependencies of build.gradle. Each AWS services has its own dependency which should be added to the project. List of AWS dependencies can be found here.
For adding Sign-in to the application the following dependency should be added to gradle
:
dependencies {
implementation 'com.amazonaws:aws-android-sdk-core:2.7.4'
implementation 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.7.0'
}
Add permission to the project
Generally, for accessing services of your mobile such as Bluetooth, WiFi, etc. the permissions should be added to the manifest file of the project. Because we want to authenticate the user, we need the Internet and Network Access in our App, so we add them to the manifest file which are:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
We are done configuring the project but should add Cognito Userpool configuration later. So now is time for some coding and UI. For doing that, we need to have two empty activity and one class which are:
- Login Activity
- Signup Activity
- Cognito Class
Add Cognito Class to the project
After adding Cognito class to the project, add the following code to that.
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserAttributes;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserCodeDeliveryDetails;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationDetails;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GenericHandler;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.SignUpHandler;
import com.amazonaws.regions.Regions;
import static android.content.ContentValues.TAG;
public class Cognito {
// ############################################################# Information about Cognito Pool
private String poolID = "Place_Your_UserPoolID";
private String clientID = "Place_Your_ClientID";
private String clientSecret = "Place_Your_ClientSecret";
private Regions awsRegion = Regions.DEFAULT_REGION; // Place your Region
// ############################################################# End of Information about Cognito Pool
private CognitoUserPool userPool;
private CognitoUserAttributes userAttributes; // Used for adding attributes to the user
private Context appContext;
private String userPassword; // Used for Login
public Cognito(Context context){
appContext = context;
userPool = new CognitoUserPool(context, this.poolID, this.clientID, this.clientSecret, this.awsRegion);
userAttributes = new CognitoUserAttributes();
}
public void signUpInBackground(String userId, String password){
userPool.signUpInBackground(userId, password, this.userAttributes, null, signUpCallback);
//userPool.signUp(userId, password, this.userAttributes, null, signUpCallback);
}
SignUpHandler signUpCallback = new SignUpHandler() {
@Override
public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
// Sign-up was successful
Log.d(TAG, "Sign-up success");
Toast.makeText(appContext,"Sign-up success", Toast.LENGTH_LONG).show();
// Check if this user (cognitoUser) needs to be confirmed
if(!userConfirmed) {
// This user must be confirmed and a confirmation code was sent to the user
// cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
// Get the confirmation code from user
}
else {
Toast.makeText(appContext,"Error: User Confirmed before", Toast.LENGTH_LONG).show();
// The user has already been confirmed
}
}
@Override
public void onFailure(Exception exception) {
Toast.makeText(appContext,"Sign-up failed", Toast.LENGTH_LONG).show();
Log.d(TAG, "Sign-up failed: " + exception);
}
};
public void confirmUser(String userId, String code){
CognitoUser cognitoUser = userPool.getUser(userId);
cognitoUser.confirmSignUpInBackground(code,false, confirmationCallback);
//cognitoUser.confirmSignUp(code,false, confirmationCallback);
}
// Callback handler for confirmSignUp API
GenericHandler confirmationCallback = new GenericHandler() {
@Override
public void onSuccess() {
// User was successfully confirmed
Toast.makeText(appContext,"User Confirmed", Toast.LENGTH_LONG).show();
}
@Override
public void onFailure(Exception exception) {
// User confirmation failed. Check exception for the cause.
}
};
public void addAttribute(String key, String value){
userAttributes.addAttribute(key, value);
}
public void userLogin(String userId, String password){
CognitoUser cognitoUser = userPool.getUser(userId);
this.userPassword = password;
cognitoUser.getSessionInBackground(authenticationHandler);
}
// Callback handler for the sign-in process
AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
@Override
public void authenticationChallenge(ChallengeContinuation continuation) {
}
@Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
Toast.makeText(appContext,"Sign in success", Toast.LENGTH_LONG).show();
}
@Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
// The API needs user sign-in credentials to continue
AuthenticationDetails authenticationDetails = new AuthenticationDetails(userId, userPassword, null);
// Pass the user sign-in credentials to the continuation
authenticationContinuation.setAuthenticationDetails(authenticationDetails);
// Allow the sign-in to continue
authenticationContinuation.continueTask();
}
@Override
public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) {
// Multi-factor authentication is required; get the verification code from user
//multiFactorAuthenticationContinuation.setMfaCode(mfaVerificationCode);
// Allow the sign-in process to continue
//multiFactorAuthenticationContinuation.continueTask();
}
@Override
public void onFailure(Exception exception) {
// Sign-in failed, check exception for the cause
Toast.makeText(appContext,"Sign in Failure", Toast.LENGTH_LONG).show();
}
};
}
In summary, this class consists of the following features:
- registering a new user
- Confirming new user
- and login
Each of these methods can be called from other activities and we will call signUpInBackground
from signup activity and userLogin
from Login activity.
Add Signup Activity to the project
This activity can have several inputs, actually, it depends on standard attributes that you have selected in the configuration of your Cognito User Pool. During registration, the user should fill the information and be sent to the AWS Cognito service. After registration, the user will receive either an Email or SMS which consists of confirmation code. For confirming the user, the confirmation code should be sent. The following figure represents user account confirmation.
The UI would be something like the following picture.
Add Login Activity to the project
In this activity, we will have the following components in the layout:
- Two edit text for username and password
- One button for login
- One text view for signing up a new user
The login activity would be like the following figure.
You can download the layout files and source codes from my Github repository and add them to your project. Leave a comment below if you want the part II.
I have the same issue reported by other users: the sign in is successful even if the password is wrong. The code only checks the username. By debugging I see that the userLogin function receives the password, but it’s only used in getAuthenticationDetails callback, that is not fired. Instead, the onSuccess callback is.
Where I can find the GitHub link. I want to check how to implement MFA
I’m having an issue where even when I input an incorrect password, it says that the sign in was successful.
Please provide more information
Thank you very much for all the insides. After logging in via Amazon Cognito, is it possible to communicate with a custom Spring Backend or do one have to secure the Spring Backend via AWS Cognito as Well?
It depends on the application you have in your Backend and the services it provides, but generally, AWS Cognito and your Spring Backend are decoupled and you can define roles for authenticated users to give them right to use the services from your Spring Backend. It would be helpful if you provide some general information about your Backend.
Hi, how to fetch the cognito id of the user from the id token generated using the user loggin method.?
Hi the userlogin method in your class uses userid as a parameter is there any method through which we can use email and password to login
Yes, you can also put email address for login, but you need configure your AWS Cognito Userpool in attribute section of corresponding userpool.
Hi, where do I need to define the roles.? I just confirmed the user. It works sometimes I get the token and then again it stops working. is there some limit to the number of logins for one user.?
Roles should be defined in AWS IAM.
There is no limitation to number of logins for a user.
Hi, where do I need to define the roles.? I just confirmed the user. It works sometimes I get the token and then again it stops working. is there some limit to the number of logins for one user.?
Hi After following all the steps when I am sending username and password for login it is saying 400 unauthorised invalid username or password. Even though the user is confirmed and for the same user login is happening from the ios app. Do we need to send the pool ARN and app name as well.?
Can you please confirm if you have defined correct Roles for authenticated and unauthenticated users?
How to refresh the access token after it is expired?
This might help you: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html
for signing out, we can use user.signout() however, where do you find user to make it sign out? Also, how can you check if it really worked?
You can use `getCurrentUser` API to retrieve user information and then use it for signout, after signout it should comeback to the start page of the app.
Hi great work! Where can I find your GitHub repository?
Hi, you can find it here: https://github.com/keivanK1/Cognito-Android-Studio
please send me a part II
how do I signout after loggin in???
Just call the `signOut()`:
user.signOut();
forget password not working.
Yes, it is not included, so try to develop it, actually, it is easy!
If you got into trouble, ping me!
The authentication handler is still passing with “Success” even after wrong password entry, what could be the reason? It seems to just verify the username only.
No, it will check both, please check your configuration in Cognito, if you could not solve it tell me again.
Can I get the GitHub repository for the source of this project?
Thank you for asking and sorry for replying late. You can find it here.
You should change the default time so it would expire according the time you set. Also adding logout (or destroying activity which has JWT) would help.
how to check if i logged in one time after that it always show already logged in the app ?
After login, you will get ID token (as well as valid Access Token and Refresh Token) from your Cognito user pool which are valid for a time period, refresh token has longer expiry time(default 30 days, but you can set it on the app). So if your access token is valid then you will get “already logged in” message, and the Android SDK is so that it will automatically refresh ID and access token (it calls InitiateAuth) as long as the refresh token is valid. And if your refresh token is expired you will be logged out.
To check if you are already logged in and you have valid access token, you can check the expiration time.
Following docs may be helpful to take a look:
https://stackoverflow.com/questions/61110436/use-cognito-login-instead-of-certificates-to-authenticate-and-subscribe-to-aws-i/61642810#61642810
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
https://docs.amazonaws.cn/en_us/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html
https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/