Primary Objective
Persist login through the use of Cognito User Pools and Cognito Federated Identities in an Android app. The app should handle refresh of tokens (via the sdk?). Email is used as username. Only allow 1 unique username (email) throughout entire application. Example: If a user logs in with Facebook with an email/username of [email protected], they should not be able to register a user through user pools with [email protected].
Setup
I have an Identity Pool created with my Authentication Providers being my Cognito User Pool, Facebook, and Google. I am using the native SDK's for Facebook and Google to sign in.
I have a SplashScreenActivity that checks to see if a user is logged in and redirects them accordingly (LoginActivity or MainActivity)
Using my user pool with currentUser.getSession(authenticationHandler)
works just fine, if logged in via the user pool.
First Time
- Not logged in
- AuthenticationHandler:getAuthenticationDetails:userId is null
- Show LoginActivity
Login
- Login via User Pool
- Success -> MainActivity
Restart App
- AuthenticationHandler:onSuccess
- Show MainActivity
If I log in via facebook and set my logins via the CognitoCachingCredentialsProvider
it doesn't update currentUser
, so my getSession()
call doesn't work. However, all I can then do is call getCachedIdentityId()
. Doesn't my CognitoCachingCredentialsProvider
expire?
Furthermore if I login with a user created from a User Pool and logout - currentUser.getUserId()
returns with my previously logged in user pool user, but getCachedIdentityId()
is null
private void logout() {
EasyPrefs.clear(this); //clear shared preferences for local variables
authHelper.getCredentialsProvider().clear(); //clear cached CognitoCredentialsProvider
CognitoUser currentUser = AuthHelper.getInstance().getUserPool().getCurrentUser();
currentUser.signOut(); //this is pointless if I login with federated identity
//TODO: Logout of Google?
//TODO: Logout of Facebook?
//assume logout was successful?
startActivity(new Intent(this, SplashScreenActivity.class));
finish();
}
Questions
- Since I am handling native login with user pool, facebook, and google - do I have to manage access tokens and refresh tokens manually? Or can the
CognitoCachingCredentialsProvider
handle that for me? - Can I login a user (via user pool, facebook, or google) and have them stayed logged in and how do I check?
- Can I natively create a user in my user pool when a user logs in via facebook/google?
Final Rant
If you have ever worked with Firebase Auth - THIS IS HOW I WANT THE APPLICATION TO FUNCTION! It is ridiculously easy to setup Firebase Auth with Firebase Users, Facebook login, and Google login.
- Login with identity provider
- Pass token to Firebase
- Get a Firebase User
- Done.
Want to logout? firebaseUser.logout(). Simple as that.
Code
SplashScreenActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
authHelper = AuthHelper.getInstance();
authHelper.init(this);
//Check if a cached identity exists?
//This will return an identity if user is user pool, facebook, google
String cachedIdentityId = authHelper.getCredentialsProvider().getCachedIdentityId();
Logger.d(TAG, cachedIdentityId == null ? "cachedIdentityId is null" : cachedIdentityId);
//This is never null unless app data has been cleared?
CognitoUser currentUser = authHelper.getUserPool().getCurrentUser();
//Even if I call currentUser.signOut(), this still returns a userId
String cachedUserId = currentUser.getUserId(); //because aws sucks
Logger.d(TAG, cachedUserId == null ? "cachedUserId is null" : cachedUserId);
//if user pool user is signed in, this will goto MainActivity
currentUser.getSession(authenticationHandler);
//not doing anything with cachedIdentityId because....?
}
private AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
@Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
}
@Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
//wait a few seconds, then goto LoginActivity
handler.postDelayed(timerTask, SECONDS * 2);
}
@Override
public void getMFACode(MultiFactorAuthenticationContinuation continuation) {
}
@Override
public void authenticationChallenge(ChallengeContinuation continuation) {
}
@Override
public void onFailure(Exception exception) {
Logger.e(TAG, AuthHelper.formatException(exception));
//wait a few seconds, then goto LoginActivity
handler.postDelayed(timerTask, SECONDS * 2);
}
};
LoginActivity
I handle each login scenario natively (user pool, facebook, google)
private void setLogins(String key, String token) {
Map<String, String> logins = new HashMap<>();
logins.put(key, token);
authHelper.getCredentialsProvider().setLogins(logins);
new RefreshCognitoCredentials().execute();
}
private class RefreshCognitoCredentials extends AsyncTask<Void, Void, String> {
@SuppressLint("LongLogTag")
@Override
protected String doInBackground(Void... voids) {
Map<String, String> logins = authHelper.getCredentialsProvider().getLogins();
for(String key : logins.keySet()) {
Logger.d(TAG, key + " - " + logins.get(key));
}
try {
authHelper.getCredentialsProvider().refresh();
} catch(NotAuthorizedException exception) {
authHelper.getCredentialsProvider().clear();
return null;
}
return authHelper.getCredentialsProvider().getIdentityId();
}
@SuppressLint("LongLogTag")
@Override
protected void onPostExecute(String response) {
if(response == null) {
Toast.makeText(getApplicationContext(), "Logins don't match. Please include at least one valid login for this identity or identity pool.", Toast.LENGTH_LONG).show();
return;
}
Logger.d(TAG, response);
startActivity(new Intent(AuthActivity.this, MainActivity.class));
finish();
}
}
The new
AWSMobileClient
that has been released as part of the AWS Amplify Framework might be of assistance here: https://aws-amplify.github.io/docs/android/authenticationYou will have basic Sign Up capabilities with Cognito User Pools:
If you've configured Identity Pools it will automatically get those credentials. All token fetch/refresh/etc. is handled automatically. You can also use Social sign in: