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.
Related
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 have registered one user using AWS Cognito android SDK. I am login with user on one device and I am able to fetch all the attributes of the user.But when I log in with same user on different device it gives me CognitoNotAuthorizedException (user is not authenticated message) and I am not able to fetch the attributes.
I have checked on Cognito user pool and the user has been registered and confirmed also phone number has been verified.Is there any version specification for getUserDetails() method which I am missing.I also ran signup/login/forgot password functionality on second device and all are working fine except getUserDetail.Below Code I am using for getting user details.
private void getDetails() {
AppHelper.getPool().getUser(mUsername).getDetailsInBackground(detailsHandler);
}
GetDetailsHandler detailsHandler = new GetDetailsHandler() {
#Override
public void onSuccess(CognitoUserDetails cognitoUserDetails) {
AppHelper.setUserDetails(cognitoUserDetails);
}
#Override
public void onFailure(Exception exception) {
showDialogMessage(getString(R.string.could_not_fetch_user_data), AppHelper.formatException(exception), true);
}
};
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 have an android app with a login that sends login information over https to a java rest api that verifies the login credentials and then sends back a response stating whether the login was successful. My question is simple, what should I do to make sure the user doesn't have to login in again when they restart the app?
There are many ways to deal with one time login, it depends a lot on how the architecture is implemented on server side to make it work. Usually Login API are closely coupled for security reason. let me example what I mean by closely coupled.
As you want to Login in to Mobile App work once and the next time user opens the Mobile app you don't want to prompt user with SignIn screen again. and surely you don't want to save the confidential information like Username and Password on Mobile app as persistent data, as it can be fetched easily from android device. so what you do.
Lets assume you pass login credentials with a deviceID unique to the Android Device. Something like shown below. following is the JSON data sent to the LoginAPI
{
"username": "example#example.com",
"password": "it's not a secret",
"deviceId": "123456789"
}
Now as you don't want to save the Login credentials, server will generate a random alpha numeric String pass it to you in response every time you login to the Mobile App.
{
"Success": true,
"SuccessMessage": "credentials are correct, really!",
"ErrorMessage": null,
"Date": "dd/mm/yyyy",
"token": "1eghe4qha23aehraeh456789" // now this is a nasty String
}
you may now save the date and the token in Mobile App as persistent data.
So next time your user opens the app you may let user bypass the SignIn screen, and in background you can check if users token id is correct by sending that to the server, something like this. you may choose SharedPreferences or create a file and save it there
{
"API_TYPE": "login",
"deviceId": "123456789",
"token": "1eghe4qha23aehraeh456789"
}
Server may check this token id against the deviceID to check if this was the correct token for the device and respond.
You may ask why are we checking the token again as this was sent by the server in first place stating the credentials were correct. I agree you do have a point, lets say it is a fail safe condition, if the user changes the password from the website or something which causes to change the token on server for that user to change, if the server denies the token provided by you just ask the user to login again.
This will make sure that a user is logged in to only one Android Device at any given point of time.
You can store the credentials after the first login.
So when the user restart app, you can automatically make the request auth.
You can chose the best option to storage the credentials using the doc:
Data-Storage
This is simple, but maybe not the best way to do this.
I have the same problme,and I got a Json data from the server like this
{"error":0,"message":"ok","token":"7c75015e92e40511911e34752ee456e1","execute_time":"0.2723"}
so I keep the StuTokenin SharedPreferences,and when my app start,check the StuToken does it exist,like this
Map<String, String> loginInfo = InfoUtil.getLoginInfo(MainActivity.this);
if (loginInfo != null) {
if (loginInfo.get("StuToken") != null) {
getStuInfo(loginInfo.get("StuToken"));
Toast.makeText(MainActivity.this, "登录状态", Toast.LENGTH_SHORT).show();
} else {
initIntent(LoginActivity.class);
this.finish();
}
} else {
initIntent(LoginActivity.class);
this.finish();
}
}
hope that will works for you
You can use shared preference to store the values of the user and to check whether the user is already logged in or not. You can follow this Link.
Try out this code in your SplashScreen Activity.You can store a value in a sharedPrefence to check whether user is Logged in or not.
public class SplashActivity extends AppCompatActivity {
private static final long TIME_OUT_MILI = 3000;
private SharedPreferences mAppPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
mAppPreferences = AppUtil.getAppPreferences(this);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (mAppPreferences.getBoolean("is_Logged_in", false)) {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
} else {
startActivity(new Intent(SplashActivity.this, LoginActivity.class));
}
finish();
}
}, TIME_OUT_MILI);
}
}
In Your LoginActivity's do this:
public class LoginActivity extends AppCompatActivity {
private SharedPreferences mAppPreferences;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
SharedPreferences.Editor editor = mAppPreferences.edit();
editor.putBoolean(Constants.SETTINGS_IS_LOGGED_IN, true);
editor.commit();
}
}
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).