How to store and retrieve AWS Cognito JWT Token in Android - android

My Android app authenticates with Cognito in an AsyncTask and receives the JWT token as part of the CognitoUserSession. This is then set in the CognitoCachingCredentialsProvider using setLogins().
I would like to retrieve this JWT token in another AsyncTask. However getLogins() on the CognitoCachingCredentialsProvider is returning a size 0 Map.
What is the easiest way to get back the token? Should I store it in Shared Preferences again?

Get/SetLogins in the SDK just update a map inside the credentials provider, they don't save it long term. If you need to access it across threads at some arbitrary time, that would be a reasonable way to accomplish it. Otherwise, just use the exact same credentials provider and it'll be there.

CognitoCachingCredentialsProvider is actually using the shared preferences to save data, but getLogins is returning a size 0 hash.
The easy way to persist data (JWT) was the shared preferences in the AsyncTask doInBackgraound method and retrieve wherever it is required.

Could get these token(refresh token / access token / id token) from aws in login time and put in preference like following:
final AuthenticationHandler authenticationCallBackManager = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
/*SharedPreferenceCacheManager.writeString(
LoginActivity.this,
"ACCESS_TOKEN",
userSession.getAccessToken().getJWTToken());*/
//System.out.println(userSession.getAccessToken().getJWTToken());//<<-------access token
/*SharedPreferenceCacheManager.writeString(
LoginActivity.this,
"REFRESH_TOKEN",
userSession.getRefreshToken().getToken());*/
//System.out.println(userSession.getRefreshToken().getToken());//<<---------refresh token
SharedPreferenceCacheManager.writeString(
LoginActivity.this,
"ID_TOKEN",
userSession.getIdToken().getJWTToken());
//System.out.println(userSession.getIdToken().getJWTToken());//<<-----------id token(this id uses Authorization in aws-side)
//....
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
AuthenticationDetails authenticationDetails = new AuthenticationDetails(
userId,
String.valueOf(editTextPassword.getText()), null);
//System.out.println(CognitoServiceConstants.AUTH_PARAM_USERNAME);
//System.out.println(CognitoServiceConstants.AUTH_PARAM_REFRESH_TOKEN);
// 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 continuation) {
//....
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
//....
}
#Override
public void onFailure(Exception exception) {
//....
}
};

Related

Cognito IDToken Renewal using RefreshToken

Hi I am android application developer , I am using cognito authentication mechanism for mobile app. Once i authenticate my user i get RefreshToken and IDToken. According to Amazon cognito it expire IDToken after exactly one hour.I am trying to get my session again to get token again and here is how i am trying to get it done.
String poolId = 'xxxxxx';
String clientId = 'xxxxxx';
String clientSecret = 'xxxxxx';
CognitoUserPool userPool = new CognitoUserPool(context, poolId, clientId, clientSecret,Regions.EU_WEST_1);
CognitoUser user = userPool.getUser();
user.getSessionInBackground(new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
String idToken = userSession.getIdToken().getJWTToken();
Map<String, String> logins = new HashMap<String, String>();
logins.put("cognito-idp." + Constants.REGION + ".amazonaws.com/" + Constants.UserPool, userSession.getIdToken().getJWTToken());
credentialsProvider.setLogins(logins);
credentialsProvider.refresh();
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
Log.i("MQTT","Detail");
}
#Override
public void getMFACode(MultiFactorAuthenticationContinuation continuation) {
Log.i("MQTT","MFACode");
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
Log.i("MQTT","Challenge");
}
#Override
public void onFailure(Exception exception) {
Log.i("MQTT","Fail");
}
});
I have userpoolid and need to know where from i can get clientID and clientSecret.So that i get data in onSuccess callback and get IDToken.
Really thankful if someone can help out.
Thanks
Go to your user pool in cognito and click on the pool you want to
work on.
Under general settings, click on app clients.
Now click on add app client.
Give your app a name
Make sure the Generate client secret is selected.
Click on create client app.
Now you can note down the client id and client secret.
Docs: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html

How to get password from aws cognito - android?

so im working with aws cognito, and im a bit confused on how to get the password?
If a user enters a password into an edit text, how do i get the password the user had entered when they signed up so i can compare the password they're logged in with to the password the registered with?
Here is the code i had to register my user:
userPool.signUpInBackground(username_ET.getText().toString(), password_ET.getText().toString(), userAttributes, null, signupCallback);
And here is the code i used to log in:
private AuthenticationHandler authenticationHandler = new AuthenticationHandler()
{
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice)
{
Log.d(COGNITO_LOGIN,"Login success I think?!");
cognitoUser.getDetailsInBackground(getDetailsHandler);
//Here i need to compare passwords before i can move on.
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId)
{
Log.d(COGNITO_LOGIN,passwordET.getText().toString());
// The API needs user sign-in credentials to continue
AuthenticationDetails authenticationDetails = new AuthenticationDetails(userId, passwordET.getText().toString(), 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("verificationCode");
// Allow the sign-in process to continue
multiFactorAuthenticationContinuation.continueTask();
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
}
#Override
public void onFailure(Exception exception)
{
// Sign-in failed, check exception for the cause
Log.d(COGNITO_LOGIN,"Login failed!");
Log.d(COGNITO_LOGIN,exception.getMessage());
exceptionMessage(exception.getMessage());
}
};
cognitoUser.getSessionInBackground(authenticationHandler);
With the authentication handler, i only need to pass in the correct username (or userID) to make the onSuccess run. Password isnt even required. So i am confused, to where the user must enter also the correct password in order for them to log in.
You don't need to compare passwords. When you sign up, Cognito stores a salt and a verifier for the password you signed up with. Cognito doesn't store your password in the form you entered it in but only a salt and verifier. When you use the code below, Cognito uses the Secure Remote Password protocol to match the verifier stored internally. Since we use the password you provided for computations, you cannot retrieve it. Note that in the onSuccess callback you will get tokens if the call is successful as noted below.
// Callback handler for the sign-in process
AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession cognitoUserSession) {
// Sign-in was successful, cognitoUserSession will contain tokens for the user
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
// The API needs user sign-in credentials to continue
AuthenticationDetails authenticationDetails = new AuthenticationDetails(userId, password, 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
}
};
// Sign-in the user
cognitoUser.getSessionInBackground(authenticationHandler);

Cognito User Pool: How to refresh Access Token Android

How do you refresh the access token using Cognito for Android? The documentation suggest the following (https://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-android-sdk.html):
// Implement authentication handler
AuthenticationHandler handler = new AuthenticationHandler {
#Override
public void onSuccess(CognitoUserSession userSession) {
// Authentication was successful, the "userSession" will have the current valid tokens
// Time to do awesome stuff
}
#Override
public void getAuthenticationDetails(final AuthenticationContinuation continuation, final String userID) {
// User authentication details, userId and password are required to continue.
// Use the "continuation" object to pass the user authentication details
// After the user authentication details are available, wrap them in an AuthenticationDetails class
// Along with userId and password, parameters for user pools for Lambda can be passed here
// The validation parameters "validationParameters" are passed in as a Map<String, String>
AuthenticationDetails authDetails = new AuthenticationDetails(userId, password, validationParameters);
// Now allow the authentication to continue
continuation.setAuthenticationDetails(authDetails);
continuation.continueTask();
}
#Override
public void getMFACode(final MultiFactorAuthenticationContinuation continuation) {
// Multi-factor authentication is required to authenticate
// A code was sent to the user, use the code to continue with the authentication
// Find where the code was sent to
String codeSentHere = continuation.getParameter()[0];
// When the verification code is available, continue to authenticate
continuation.setMfaCode(code);
continuation.continueTask();
}
#Override
public void authenticationChallenge(final ChallengeContinuation continuation) {
// A custom challenge has to be solved to authenticate
// Set the challenge responses
// Call continueTask() method to respond to the challenge and continue with authentication.
}
#Override
public void onFailure(final Exception exception) {
// Authentication failed, probe exception for the cause
}
};
user.getSession(handler);
Here is why this does not work. The user object which I am getting the Session for is no longer authenticated when the token expires. So retrieving the cached user via the below, will return null
CognitoUser user = userPool.getCurrentUser();
Because the above returns null, I try to get the user object by id
CognitoUser user = userPool.getUser(userId);
Which works perfectly, except that user is not authenticated and will fail during the following callback stage because the userID is null
#Override
public void getAuthenticationDetails(final AuthenticationContinuation continuation, final String userID)
Only when I attempt this call before the token expires does this work, and I can receive a new access token. But how to do this after the token has expired? Any help on this would be appreciated. Thanks in advance
When you call getSession(...) - to get tokens - and if the cached tokens have expired, the SDK will automatically refresh tokens (as long as the refresh token has not expired). If the refresh token too has expired, then getAuthenticationDetails(...) is invoked because now the user credentials (username, password, etc) are required to get new set of tokens. It should not matter how you get the user object, i.e. through getCurrentUser() or getUser(...) methods, as long as there are valid cached tokens or if the tokens can be refreshed, you will get valid tokens with getSession(...).
Retry with the latest SDK (ver 2.3.1).

Firebase FCM token - When to send to server?

Okay so I have an app which on first start takes you through a few welcoming slides, then takes you to a login/register page and then to MainActivity.
I have just implemented FCM and the services generate a token before any of those pages have been seen by the user. How could I make it so that the service runs after I get to MainActivity?
The problem is I'm trying to send the token as soon as it is refreshed to the MySQL DB to the appropriate user account, but since the user hasn't signed in yet, that is null and my message to the server fails. What's a good way to design this? I thought of saving the token in SharedPreferences and sending it to the server after the user has logged in but that creates lots of complications when the token is refreshed at some later point?!
Possible solution:
I'm not sure I completely understand how the 2 services run but say in onTokenRefresh I just save the token into SharedPreferences and in MainActivity I get the value from SP and then I send it to the server. In that case when the token is refreshed the new value will immediately go into SharedPreferences again. But I would still need to check if it's a new value in SP and then reupload it to the server. This is confusing!
Note that you can always retrieve the token with:
FirebaseInstanceID.getInstance().getToken();
This will return null if the token has not yet been generated or the token if it has been generated. In your case it is very likely that the token will be generated by the time the user has signed in. So you should be able to send it to your app server as soon as the user has signed in. If it is not available then you would send it in the onTokenRefresh callback as Chintan Soni mentioned.
Edit
Using the new Firebase SDK (21.0.0) , you will get your token this way :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});
You better add a listener for more handling on the response .
Yes FCM token is generated automatically. But try to see this in a different angle.
This is how I handled it.
Let FCM generate token as soon as your app starts. OnTokenRefresh will be called and you just save it in your preferences as:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
SharedPreferenceUtils.getInstance(this).setValue(getString(R.string.firebase_cloud_messaging_token), token);
// To implement: Only if user is registered, i.e. UserId is available in preference, update token on server.
int userId = SharedPreferenceUtils.getInstance(this).getIntValue(getString(R.string.user_id), 0);
if(userId != 0){
// Implement code to update registration token to server
}
}
Hope you are clear with the way. Ask if you need more clearance on it.
Edit
Using the new Firebase SDK (21.0.0) , you need to override onNewToken() method instead of onTokenRefresh()
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
We handled it like this:
Our server create/update the token value against a user id (primary key)
Use 2 SharedPreferences
String - token String
Boolean (updated) - whether token is updated on server or not.
In case of token refresh we update the token string and set the boolean to false.
Later whenever user login each time we check for boolean (updated), if that is false - we attach the current token to his id and send it to server and set updated to true.
December 2020 update : Using the new Firebase SDK (21.0.0) you can get it by overriding onNewToken() method :
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
Or by FirebaseInstallations.getInstance() within your scope :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});

Firebase authWithOauthToken is not working

I am developing an app in Android using firebase.I have created the login activity where i have a method that logs user in when they pass the credentials(user creation is already done).Then i will save the token recieved in onAuthenticated callback so that i can log user in automatically next time when he/she opens the app without asking to enter the credentials.
Here is the code
private void loginWithPassword(final String email, String password) {
progressDialog.show();
FirebaseConnections.getConnection().authWithPassword(email, password,
new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
// Authentication just completed successfully :)
IGStorePreference.getInstance().saveString(Constants.TOKEN, authData.getToken());
IGStorePreference.getInstance().saveString(Constants.UID, authData.getUid());
IGStorePreference.getInstance().saveString(Constants.PROVIDER, authData.getProvider());
dismissProgressDialog();
}
#Override
public void onAuthenticationError(FirebaseError error) {
// Something went wrong :(
dismissProgressDialog();
Snackbar.make(parentView, error.getMessage(), Snackbar.LENGTH_LONG).show();
}
});
}
And then i check onCreate whether we have token token to log user in
private void checkIfTokenExistAndLogin() {
if (IGStorePreference.getInstance().isPrefExists(Constants.TOKEN)) {
progressDialog.show();
String provider = IGStorePreference.getInstance().getString(Constants.PROVIDER);
String token = IGStorePreference.getInstance().getString(Constants.TOKEN);
FirebaseConnections.getConnection().authWithOAuthToken(provider, token, new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
IGStorePreference.getInstance().saveString(Constants.TOKEN, authData.getToken());
IGStorePreference.getInstance().saveString(Constants.UID, authData.getUid());
IGStorePreference.getInstance().saveString(Constants.PROVIDER, authData.getProvider());
dismissProgressDialog();
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
dismissProgressDialog();
Snackbar.make(parentView, firebaseError.getMessage(), Snackbar.LENGTH_LONG).show();
}
});
}
}
But the problem is that i am getting an error while login user with authWithOAuthToken.Please help what i am missing.
This is the error i recieve everytime.
FirebaseError: Invalid authentication credentials provided.
authWithOAuthToken is used to login with a social provider. For example, user signs in with Google and gets an OAuth token returned from Google. Then app sends this OAuth token to Firebase auth server via authWithOAuthToken. User can log in after server verifies the OAuth token.
In your case, user logged in with email/password. The token you received was a Firebase auth token issued by Firebase auth server not an OAuth token issued by social provider.
Please refer to the doc for details: https://firebase.google.com/support/guides/firebase-android#sign_a_user_in_with_a_social_provider_numbered

Categories

Resources