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).
Related
I would like to ask that is there any methods in Facebook SDK able to identify whether the user logged in in the Facebook Apps changed or not? Because I am using the AccessToken.CurrentAccessToken method to check whether user logged in before or not, but it won't update if user in Facebook Apps changed. Thanks!
Here is how I perform checking:
// Get the current access token if user logged in before
AccessToken objAccessToken = AccessToken.CurrentAccessToken;
// Check the token value, access token validity and data access validity
bool blnIsLoggedIn = objAccessToken != null && !objAccessToken.IsExpired && !objAccessToken.IsDataAccessExpired;
if (blnIsLoggedIn)
{
// If user logged in, direct perform to get user profile
GetFacebookUserProfile(objAccessToken, strUserProfileScopes);
}
else
{
// If user not logged in, perform login
LoginManager.Instance.LogIn(CurrentActivity, DefaultScopes);
}
private void GetFacebookUserProfile(AccessToken objAccessToken, string strUserProfileScopes)
{
GraphRequest objGraphRequest = GraphRequest.NewMeRequest(objAccessToken, new clsGraphJSONObjectCallback());
Bundle objParameters = new Bundle();
objParameters.PutString("fields", strUserProfileScopes);
objGraphRequest.Parameters = objParameters;
objGraphRequest.ExecuteAsync();
}
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) {
//....
}
};
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);
I downloaded the Applozic Chat SDK for android. Currently I found that you can login to a user account with the username and any password. I am wonder how should I implement the code to have it check if the user entered the password correctly?
While doing Applozic Login/Register you need to set the user.setAuthenticationTypeId(User.AuthenticationType.APPLOZIC.getValue()); and set the password .If password is incorrect you will get the exception in onFailure of UserLoginTask there you can check for this string in exception Invalid uername/password
UserLoginTask.TaskListener listener = new UserLoginTask.TaskListener() {
#Override
public void onSuccess(RegistrationResponse registrationResponse, Context context) {
//After successful registration with Applozic server the callback will come here
}
#Override
public void onFailure(RegistrationResponse registrationResponse, Exception exception) {
//If any failure in registration the callback will come here
//Here Invalid uername/password exception will be thrown if password is wrong check for the string Invalid uername/password in exception
}};
User user = new User();
user.setUserId(userId); //userId it can be any unique user identifier
user.setDisplayName(displayName); //displayName is the name of the user which will be shown in chat messages
user.setEmail(email); //optional
user.setImageLink("");//optional,pass your image link
user.setPassword(password);//Set the password
user.setAuthenticationTypeId(User.AuthenticationType.APPLOZIC.getValue());//You need to set the Authentication type
new UserLoginTask(user, listener, this).execute((Void) null);
Applozic login sample github code link
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();
}
});