I am implementing AWS with an Android application for the first time.
We would like to use Cognito to authenticate our users, and selectively provide data from DynamoDB.
I have successfully set up my user pool and can see new registrations appear in the user list. Trying to login with an email that does not exist fails.
However, Cognito always logs in with a valid email address, regardless of password input.
What is wrong with my process?
public class CognitoController extends Application {
static CognitoUserPool pool;
static String userEmail;
public void onCreate(){
super.onCreate();
pool = new CognitoUserPool(this,
"us-east-xxxx",
"xxxx",
"xxxx",
new ClientConfiguration(),
Regions.US_EAST_1);
}
}
-
private void actionAdminLogin(){
UtilityInterfaceTools.hideSoftKeyboard(AdminLoginActivity.this);
String inputEmail = ((EditText) findViewById(R.id.input_admin_email)).getText().toString();
String inputPassword = ((EditText) findViewById(R.id.input_admin_password)).getText().toString();
CognitoController.userEmail = inputEmail;
details = new AuthenticationDetails(inputEmail, inputPassword, null);
AuthenticationHandler auther = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
Toast.makeText(AdminLoginActivity.this, "Congratulations It Works...", Toast.LENGTH_LONG).show();
startActivity(new Intent(AdminLoginActivity.this, AdminPortalActivity.class));
finish();
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation continuation, String email) {
continuation.setAuthenticationDetails(details);
continuation.continueTask();
}
#Override
public void getMFACode(MultiFactorAuthenticationContinuation continuation) {
continuation.continueTask();
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
continuation.continueTask();
}
#Override
public void onFailure(Exception exception) {
TextView errorMessage = findViewById(R.id.message_invalid_credentials);
errorMessage.setText(exception.toString());
errorMessage.setVisibility(View.VISIBLE);
}
};
CognitoController.pool.getUser(inputEmail).getSessionInBackground(auther);
}
I think your problem (which is not a problem by the way) is either:
In your pool Cognito setting, you chose your devices to be remembered.
Remembered
devices are also tracked. During user authentication, the key and secret pair assigned to a remembered device is used to authenticate the device to verify that it is the same device that the user previously used to sign in to the application. APIs to see remembered devices have been added to new releases of the Android, iOS, and JavaScript SDKs. You can also see remembered devices from the Amazon Cognito console.
The token is already cached:
Caching
The Mobile SDK for Android caches the last successfully authenticated user and the user's tokens locally on the device, in SharedPreferences. The SDK also provides methods to get the last successfully authenticated user.
Your Application Update
In fact for better user experience, you want the user to use the app, and don't need to login every time that she wants to use your app (e.g., look at mail apps, social media apps, etc.). However, you application need to handle that, you have two choices here:
Redirect to login if necessary: If the user is already logged in and wants to use the application again, your app needs to verify the user against the Cognito user pool, and only then, redirect the user to the login page if necessary.
Remove the token: If you really want the user to login every time that she uses the application, then remove the token if the user signs out; but I do not recommend this, for the sake of user experience.
I am actually using Firebase Google auth for signing in the user and after that, I want to take the basic details of the user into the database which also includes the mobile number of a user, so my question is can use Phone Number Authentication just to verify the mobile number of user (using OTP) and after that I can store it into database?
Thank You!
If you are already signing in a user with Google. You can link/update the phone number for that user:
https://firebase.google.com/docs/reference/js/firebase.User#updatePhoneNumber
https://firebase.google.com/docs/reference/js/firebase.User#linkWithPhoneNumber
This means the user will have 2 providers linked (phone/google.com). The user will be able to sign in with either in the future. Though if you only want to expose Google as the provider. You can just provide that in your sign in page.
You can store the user in the database too.
Because Firebase Authentication is amazing,
you can have more than one authentication "method" on an account:
In this example, the app in question, allows users to create an account using either
phone
email
Facebook link
So. On the welcome screen of the app, it says "Join SuperApp, using phone, email or Facebook link!!" and there's three buttons for those three methods.
But in fact, the way Firebase Authentication works, each user can actually have more than one of those.
The third user there has both email and phone authentication. The fourth user there has all three!
So, let's say a user creates an account with "email authentication".
Later, there will be a button that says "Also link your Facebook account!",
and another one, "Also link your phone number!"
(Note that in many cases, depending on what your startup is, you may want users to have more than one authentication method, for greater security. Or, you may just want to make it easier for them to log in.)
Incredibly, Firebase can handle all that. (Or, you can spend a year developing it by hand!)
Some points...
Yes, Firebase entirely takes care of sending the email or SMS verification codes. Completely automatic.
Note that you can't "use" the amazing email/phone verification service "for other purposes". For example, in an app we're doing there's a feature where you need to verify a purchase or something, using an SMS code. Note that that has absolutely nothing to do with the user logging-in, it's just an unrelated feature in the app which uses an SMS code. You can not (unfortunately!) use Firebase's epic SMS-system to do that. (You'd just use any of the widely available sms-code-sending services, instead.)
A huge point of confusion. Note that the email and/or phone number discussed here are stored by Firebase in the Firebase Authentication system. The image above is from the Firebase Authentication system. There is NO CONNECTION AT ALL to your data tables. So, almost every app has in the data tables something like "userData/". Sure, you may, for your convenience (or whatever) have fields in the data tables like "email" "Facebook name" or whatever. Those fields have NO CONNECTION AT ALL, in any way, to the "actual, real" Firebase Authentication system (as seen in the image above). Those are just fields you populate in your development - they could hold any value you put in there. There are many, many questions here on SO where folks confuse some value "user_email" that happens to be in the database, with the "actual, real" email or phone used by Firebase in the Firebase Authentication system.
Note that if you start off with Facebook or another social authentication. Sometimes those social media have the users email/phone/etc. But that has absolutely no connection to the actual Firebase authenticated email/phone.
If the user happens to need to change their actual authenticated email or phone, there's a function for that link
The Firebase Authentication system is so amazing that many mobile projects use Firebase purely for the convenience of the amazing Firebase Authentication system, even if they are not especially using Firebase as such.
(One detail on the email front. You'll use your own email server to send the emails. (ie, godaddy or sendmail or whatever - just whatever email server you normally use.) There's a slot on the backend of Firebase where you just type in your email server/password. Alternately, during development/testing Firebase will even send the email for you, but it only arrives slowly since it's a mass-used account being used by about a zillion Firebase-app-developers like yourself.)
Summary
Amazingly you can have more than one authentication method, it is all handled by Firebase
Note that any phone/email etc you may happen to have in your data, has absolutely No connection to the "actual" phone/email in Firebase Authentication
In the Firebase console, look on the left and go one above "Database" to see "Authentication" ! Many news developers don't realize this!
If you want to use firebase phone authentication for phone number verification only, according to me you do this but by implementing following:
First in Sign-in Method page, enable the Phone Number sign-in method and to send verification code on phone number use this
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
you will get response in mCallbacks and to initialize callback use this
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
// this method executed if phone number verified you can do your stuff here if you only want to verify phone number.
// or you can also use this credentials for authenticate purpose.
}
#Override
public void onVerificationFailed(FirebaseException e) {
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
//and then execute your method if number entered is correct.
}
};
Don't forgot to use latest firebase dependencies like
compile 'com.google.firebase:firebase-auth:11.4.2'
Hope this will help you.
private FirebaseAuth mAuth;
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;
String mVerificationId = "";
PhoneAuthProvider.ForceResendingToken mResendToken;
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks()
{
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
//edtVerify.setText(phoneAuthCredential.getSmsCode());
signInWithPhoneAuthCredential(phoneAuthCredential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
if (e instanceof FirebaseAuthInvalidCredentialsException) {
Log.e("Invalid Phone Number ","====>");
} else if (e instanceof FirebaseTooManyRequestsException) {
Log.e("Quota exceeded ","====>");
}
}
#Override
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) {
mVerificationId = verificationId;
mResendToken = token;
}
};
//SEND VERIFICATION CODE...
try {
PhoneAuthProvider.getInstance().verifyPhoneNumber(mLastEnteredPhone, 120,
TimeUnit.SECONDS,
mContext,
mCallbacks);
} catch (Exception e) {
e.printStackTrace();
}
//VERIFY CODE...
try {
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithPhoneAuthCredential(credential);
} catch (Exception e) {
e.printStackTrace();
}
//SIGN IN WITH PHONE...
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential)
{
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful())
{
FirebaseUser user = task.getResult().getUser();
String currentUid = user.getUid();
String currentPhone = user.getPhoneNumber();
} else {
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
Log.e("Invalid Code ","====>");
}
}
}
});
}
Yes you can use it with the help of following code
PhoneAuthProvider.getInstance().verifyPhoneNumber(
mobnum, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
signInWithPhoneAuthCredential(phoneAuthCredential);
}
#Override
public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
verificationId=s;
}
#Override
public void onVerificationFailed(FirebaseException e) {
Toast.makeText(getApplicationContext(),"Verification Failed"+e.getMessage(),Toast.LENGTH_LONG).show();
}
}); // OnVerificationStateChangedCallbacksPhoneAuthActivity.java
after that you have to use the following method
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent i = new Intent(Otp_Verification_Activity.this,HomeActivity.class);
startActivity(i);
finish();
} else {
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
}
}
}
});
}
and for more you can visitclickhere
and you watch video on youtube clickhere
i was try to create login session with session key, the session key always generate new key either we do Login/registration, i can retrieve the data from my gson
LoginService loginService = retrofit.create(LoginService.class);
Observable<LoginResponse> obs = loginService.logins(emai,pass,"1", Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID), Build.MODEL, Build.VERSION.RELEASE);
obs.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
public void onNext(LoginResponse loginResponse) {
int responses = loginResponse.getCODE();
String texts="";
if(responses == 1)
{
User user = loginResponse.getDATALIST().get(0);
setPrefIsLogin(true);
setPrefSessionUid(user.getSESSIONKEY(),user.getUSERID());
nextActivity();
}
else{
}
}
});
the question is, how to make handler to handle the save session check if there is another login activity with the same account?
You should never assign two accessToken/Session for one user. You will send the same accessToken to the other instance of the new user. Plus side, user won't be able to duplicate his/her work by two accessToken.
If you want to force the other/first one AUTO-LOGOUT, you can use Firebase notification feature to send a notification to that particular deviceID and force it to stop. You can check firebase's tutorial to see how they work.
Another slow procedure is to check before login & everyother server side work to check if there are instance of same user. You will send an error and user end will receive it and show the error accompanying by logging out the user.
I using AWS cognito to verify the user's phone number. I have a problem:
When the user enter his details, I send it to AWS.
AWS try to send code to the user's phone number, but if the user enter a wrong number AWS return exception "invalid phone number". So I ask the user to update the number, but when I try to update it in AWS, they return exception "the user is not authenticated". How can I update the number to the right number after the user just signup and still not confirmed?
This is my code:
// Create a CognitoUserAttributes object and add user attributes
CognitoUserAttributes userAttributes = new CognitoUserAttributes();
// Add the user attributes. Attributes are added as key-value pairs
// Adding user's given name.
// Note that the key is "given_name" which is the OIDC claim for given name
userAttributes.addAttribute("name", userName);
userAttributes.addAttribute("family_name", userFamily);
// Adding user's phone number
userAttributes.addAttribute("phone_number", prepareValidPhoneNumberForAWS(userPhone));
SignUpHandler signupCallback = new SignUpHandler()
{
int t=0;
#Override
public void onSuccess(CognitoUser cognitoUserUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails)
{
// Sign-up was successful
// Check if this user (cognitoUser) has to be confirmed
if(!userConfirmed)
{
t=0;
// This user has to 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
{
// The user has already been confirmed
t=1;
}
}
#Override
public void onFailure(Exception exception)
{
// Sign-up failed, check exception for the cause
exception.printStackTrace();
}
};
userPool.signUpInBackground(currentUser.getUser_id(),currentUser.getUuid(),userAttributes,null,signupCallback);
Currently this usecase is not supported by Cognito because customer needs to be signed-in to update the phone number and unconfirmed accounts cannot sign-in.
One option is to let user create a new account with the correct email address. Another option is that end user contacts developer, developer can use AdminUpdateUserAttributes to update the user phone number.
I'm starting to use Backendless.com mBaaS on Android
I sign in user via Google and I got token and everything is OK, but the logged in user isn't created an Users table, so I can not use it to store user specific data.
So I tried to combine user login from here with user creation from documentation:
if (result.isSuccess()) {
logined = true;
loginInBackendless(result.getSignInAccount());
BackendlessUser user = new BackendlessUser();
user.setProperty("email", result.getSignInAccount().getEmail().toString());
user.setProperty("name", result.getSignInAccount().getDisplayName().toString());
user.setPassword(UUID.randomUUID().toString());
Backendless.UserService.register(user, new AsyncCallback<BackendlessUser>() {
public void handleResponse(BackendlessUser registeredUser) {
// user has been registered and now can login
}
public void handleFault(BackendlessFault fault) {
// an error has occurred, the error code can be retrieved with fault.getCode()
}
});
the question is:
Is it right way to create user? it seems not OK, because every time google user is logged in, a new Backendless user is created (or his record in Users table is updated).