64

I'd like to use Firebase for my web app that is for people with dementia in a care home. They do not have email or social network accounts so will need a simple username / password sign up / sign in.

What is the easiest way to do this? From what I can see in the docs I'd have to use a custom auth flow but I do not have an existing auth server.

If I do need ot do this what is the easiest way to provide the token? In Azure there is Functions and AWS has Lambda but I see nothing here is Firebase

Steve Lee
  • 5,191
  • 5
  • 17
  • 18

5 Answers5

57

You are correct that username/password sign-in is not supported natively in Firebase Auth at this moment.

You can implement a custom provider as shown in this example. This allows you to meet any custom requirements, but is admittedly a bit more involved than using the built-in providers. There is an example of this here that you can use as a starting point.

A workaround you could take without needing to use custom auth with another backend is to accept usernames in your UI, but on the underlying logic, append "@yourowndomain.com" before calling the functions to sign up or sign in with email.

So you would be using email/password authentication, mapping <username> to <username>@yourowndomain.com

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1
    Interesting idea - I did not think of that simple 'hack' My concern would be an unexpected attempt by Firebase to use the email address. I'd have to handle that in a catch all rule. Plus would the password reset flow insist on someone responding to an email to that address? – Steve Lee May 31 '16 at 08:49
  • Hello Steve. Indeed some situations may trigger sending an email to those addresses - but you always have to trigger them yourself. The cases where emails are sent are: email address verifications, password resets, and email address changes. If you do not offer any of this functionality in your app, no email will be sent. However, please ensure you use a domain name that does not have real email addresses there, to prevent conflicts. – Alfonso Gomez Jordana Manas May 31 '16 at 18:53
  • 12
    Thanks. Not having a password reset is going to be problematical :( – Steve Lee Jun 01 '16 at 20:32
  • 1
    I am not sure I understand. From your question it seems like you did not want to support email and password sign ups, only username and password. Could you clarify? – Alfonso Gomez Jordana Manas Jun 01 '16 at 21:42
  • No, you got my requirements perfectly. But I thought you said a password reset requires an email which is no good for people who do not have email accounts. I realise a simple reset is unsecure but have to live with that for these users – Steve Lee Jun 03 '16 at 08:24
  • Apologies Steve, I still do not follow, perhaps we could take this over email (you can file a support request [here](https://firebase.google.com/support/contact/troubleshooting/?hl=en) and mention my name). If you do not want to support password resets for your users, because they do not have real emails, I think this workaround should work for you :). But perhaps I am misunderstanding something. – Alfonso Gomez Jordana Manas Jun 03 '16 at 19:09
  • Thanks Alfonso. I'll try your suggested approach. All I want is users to be about to sign up, sign in and reset password WITHOUT having an email. – Steve Lee Jun 06 '16 at 08:40
  • I think this is a valid use case that Firebase over looks. I should be able reset passwords in a variety of ways, without email address. How I do that, should be up to me, and be between me, my app, and my users. – Steve Kennedy Feb 20 '17 at 14:20
  • 1
    @SteveLee how do you validate the person resetting the password is the account owner though?? – lopu Apr 27 '18 at 00:09
14

You can use sign in with custom token

Firebase gives you complete control over authentication by allowing you to authenticate users or devices using secure JSON Web Tokens (JWTs). You generate these tokens on your server, pass them back to a client device, and then use them to authenticate via the signInWithCustomToken() method.

  1. You need to save username and password in your database or rtdb or firestore
  2. When user touch the login button, client will send username and password to your backend. If the username and password correct, generate custom token and send it back to the client
  3. Client then can login with custom token from the server using signInWithCustomToken() method

More detail can be read in this documentation

Faruk
  • 5,438
  • 3
  • 30
  • 46
  • 2
    This means you still saving the passwords on your BE - So you didnt export the authentication problem to Firebase - AFAIU this was the main idea using it – chenop May 01 '20 at 20:34
  • You should tell this to the one who asks the question @chenop. Maybe he need to migrate old systems, or maybe he just don't trust google I don't know. I just provide solution for the problem, because the username/password sign-in is not supported by FirebaseAuth right now. – Faruk Jul 19 '20 at 21:27
13

Appending a dummy domain at end is a kind of a patch up and should be avoided. To enable username login just follow these simple steps.

Sign Up

During sign up take the userid , email and password . Register the user with normal email and password. On Success of it save the email against the user_id in a separate node(branch).

mButtonSignUp.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(isValid()){
                mProgressBar.setVisibility(View.VISIBLE);
                final String userId = mEditTextUserId.getText().toString();
                final String emailId = mEditTextEmail.getText().toString() ;
                String password = mEditTextPassword.getText().toString() ;
                firebaseRef.createUser(emailId, password, new Firebase.ResultHandler() {
                    @Override
                    public void onSuccess() {
                       firebaseRef.child("user_ids").child(userId).setValue(emailId);
                       Toast.makeText(getBaseContext(),"You are successfully registered ",Toast.LENGTH_SHORT).show();
                       mProgressBar.setVisibility(View.GONE);
                    }

                    @Override
                    public void onError(FirebaseError firebaseError) {
                        mProgressBar.setVisibility(View.GONE);
                        Toast.makeText(getBaseContext(),firebaseError.toString(),Toast.LENGTH_SHORT).show();
                    }
                });

            }
        }
    });

Database

Database structure will look like this

enter image description here

Login

Check if the user has entered an email or userId. If it is a email id then directly perform login with it otherwise fetch the email id associated with the username and perform login.

    Button buttonLogIn = (Button)findViewById(R.id.button_login);
    buttonLogIn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {             

            mProgressBar.setVisibility(View.VISIBLE);
            String username = mEditTextEmail.getText().toString() ;
            final String password = mEditTextPassWord.getText().toString() ;

          //  Check if it is an email or not
             if(android.util.Patterns.EMAIL_ADDRESS.matcher(username).matches())                 {
                performLogin(username,password);
            }else{
              //get the emailId associated with the username      
        firebaseRef.child("user_ids").child(username)
              .addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if(dataSnapshot!=null){
                            String userEmail =  dataSnapshot.getValue(String.class);
                            performLogin(userEmail,password);
                        }
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {
                        //Handle Error
                    }
                });
            }

        }
    });

private void performLogin(String emailId, String password) {
    firebaseRef.authWithPassword(emailId,password, new Firebase.AuthResultHandler() {
        @Override
        public void onAuthenticated(AuthData authData) {
            uid = authData.getUid() ;
            Toast.makeText(getBaseContext(), authData.toString(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAuthenticationError(FirebaseError firebaseError) {
            Toast.makeText(getBaseContext(), firebaseError.toString(), Toast.LENGTH_SHORT).show();
        }
    });
}
Saurabh Padwekar
  • 3,888
  • 1
  • 31
  • 37
  • If I am understanding the only difference is you propose storing a randomised email for those who do not have one and check that on the server at login? I guess that is a little more secure. That's still a patchup though :) – Steve Lee Dec 19 '16 at 10:11
  • 6
    If the proposal is to store the user's real email address, this seems problematic as the address could be exposed to any client. – okhobb Aug 30 '17 at 14:12
3

You may use Alfonso's solution as well. And where you need a real e-mail, you can set an textfield for an e-mail when the user registers and you can keep it in your database and you can use it.

Alper
  • 1,415
  • 2
  • 13
  • 20
0

I couldn't find support by firebase for this. However, doing the following will solve your problem.

SIGN UP


 private void clickListener() {
     registerbtn.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View view) {
             String email = emailEdit.getText().toString();
             String pass = passwordEdit.getText().toString();

             if (email.isEmpty()) {
             emailEdit.setError("Lutfen Kullanici Adinizi Giriniz.");
             return;
             }
             if (pass.isEmpty()) {
             passwordEdit.setError("Lütfen Parolanizi Giriniz.");
             return;
             }
             email=email+"@gmail.com";
             createAccount(email,pass);
         }
     });

 }

 private void createAccount(String email, String pass){
     auth.createUserWithEmailAndPassword(email, pass)
             .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                 @Override
                 public void onComplete(@NonNull Task<AuthResult> task) {
                     if (task.isSuccessful()){
                         FirebaseUser user=auth.getCurrentUser();
                         updateUi(user,pass);

                     }
                     else{

                         Toast.makeText(MainActivity.this, "Error: "+
                                 task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                     }
                 }
             });
 }

This way we add the phrase "@gmail.com" to the email. email=email+"@gmail.com";

Let the value we take as an example be "abdulkerim". Even if the user enters the phrase "abdulkerim", we get a username instead of an email, thanks to the "@gmail.com" we added to the end.

database view