15

So far I'm really impressed with Firebase. But I would like to know how would I go about customizing my user email authentication. I would like to add the following fields: Age and Gender for users to fill out when registering.

here's my SignUpActivity.java.

public class SignUpActivity extends AppCompatActivity {
private EditText inputEmail, inputPassword, signupInputUsername, signupInputAge;
private Button btnLinkLogin, btnSignUp, btnResetPassword;
private ProgressBar progressBar;
private FirebaseAuth auth;
private Spinner sexSpinner;
String defaultTextForSpinner = "Sex";
String[] arrayForSpinner = {"Male", "Female"};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_signup);

    //Get Firebase auth instance
    auth = FirebaseAuth.getInstance();

    inputEmail = (EditText) findViewById(R.id.signup_input_email);
    signupInputUsername = (EditText) findViewById(R.id.signup_input_username);
    inputPassword = (EditText) findViewById(R.id.signup_input_password);
    signupInputAge = (EditText) findViewById(R.id.signup_input_birthday);
    sexSpinner = (Spinner)findViewById(R.id.spinner);
    sexSpinner.setAdapter(new CustomSpinnerAdapter(this, R.layout.spinner_row, arrayForSpinner, defaultTextForSpinner));

    btnSignUp = (Button) findViewById(R.id.btnSignUp2);
    btnLinkLogin = (Button) findViewById(R.id.btnorlogin);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    EditText edittext = (EditText) findViewById(R.id.signup_input_birthday);


    edittext.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            //To show current date in the datepicker
            Calendar mcurrentDate = Calendar.getInstance();
            int mYear = mcurrentDate.get(Calendar.YEAR);
            int mMonth = mcurrentDate.get(Calendar.MONTH);
            int mDay = mcurrentDate.get(Calendar.DAY_OF_MONTH);

            DatePickerDialog mDatePicker=new DatePickerDialog(SignUpActivity.this, new DatePickerDialog.OnDateSetListener() {
                public void onDateSet(DatePicker datepicker, int selectedyear, int selectedmonth, int selectedday) {
                    // TODO Auto-generated method stub
                /*      Your code   to get date and time    */
                }
            },mYear, mMonth, mDay);
            mDatePicker.setTitle("Select birthday");
            mDatePicker.show();  }
    });


    btnSignUp.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            String email = inputEmail.getText().toString().trim();
            String password = inputPassword.getText().toString().trim();

            if (TextUtils.isEmpty(email)) {
                Toast.makeText(getApplicationContext(), "Enter email address.", Toast.LENGTH_SHORT).show();
                return;
            }

            if (TextUtils.isEmpty(password)) {
                Toast.makeText(getApplicationContext(), "Enter password.", Toast.LENGTH_SHORT).show();
                return;
            }

            if (password.length() < 6) {
                Toast.makeText(getApplicationContext(), "Password too short, enter minimum 6 characters.", Toast.LENGTH_SHORT).show();
                return;
            }

            progressBar.setVisibility(View.VISIBLE);
            //create user
            auth.createUserWithEmailAndPassword(email, password)
                    .addOnCompleteListener(SignUpActivity.this, new OnCompleteListener<AuthResult>() {
                        @Override
                        public void onComplete(@NonNull Task<AuthResult> task) {
                            Toast.makeText(SignUpActivity.this, "createUserWithEmail:onComplete:" + task.isSuccessful(), Toast.LENGTH_SHORT).show();
                            progressBar.setVisibility(View.GONE);
                            // If sign in fails, display a message to the user. If sign in succeeds
                            // the auth state listener will be notified and logic to handle the
                            // signed in user can be handled in the listener.
                            if (!task.isSuccessful()) {
                                Toast.makeText(SignUpActivity.this, "Authentication failed." + task.getException(),
                                        Toast.LENGTH_SHORT).show();
                            } else {
                                startActivity(new Intent(SignUpActivity.this, MainActivity.class));
                                finish();
                            }
                        }
                    });

        }
    });
}

@Override
protected void onResume() {
    super.onResume();
    progressBar.setVisibility(View.GONE);
}

public class CustomSpinnerAdapter extends ArrayAdapter<String> {

    Context context;
    String[] objects;
    String firstElement;
    boolean isFirstTime;

    public CustomSpinnerAdapter(Context context, int textViewResourceId, String[] objects, String defaultText) {
        super(context, textViewResourceId, objects);
        this.context = context;
        this.objects = objects;
        this.isFirstTime = true;
        setDefaultText(defaultText);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        if(isFirstTime) {
            objects[0] = firstElement;
            isFirstTime = false;
        }
        return getCustomView(position, convertView, parent);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        notifyDataSetChanged();
        return getCustomView(position, convertView, parent);
    }

    public void setDefaultText(String defaultText) {
        this.firstElement = objects[0];
        objects[0] = defaultText;
    }

    public View getCustomView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View row = inflater.inflate(R.layout.spinner_row, parent, false);
        TextView label = (TextView) row.findViewById(R.id.spinner_text);
        label.setText(objects[position]);

        return row;
    }

}
ReyAnthonyRenacia
  • 17,219
  • 5
  • 37
  • 56
ngx311
  • 487
  • 2
  • 7
  • 22

4 Answers4

18

When you are using Firebase authentication with user and password, the only data that you can get after authentication is that data that you get from FirebaseUser object. As you probably see, there also a couple of methods that can help you get the data easier, like: getEmail(), getDisplayName(), getPhotoUrl() and so on.

Unfortunately, you cannot add additional data like age and gender to the FirebaseUser object. If you want to add additional data, you need to create a model class for your user and then store it in your Firebase database.

You model class should look like this:

public class User {
    String name, gender, emailAddress;
    int age;

    public User(String name, String gender, String emailAddress, int age) {
        this.name = name;
        this.gender = gender;
        this.emailAddress = emailAddress;
        this.age = age;
    }

    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    public String getGender() {return gender;}
    public void setGender(String gender) {this.gender = gender;}

    public String getEmailAddress() {return emailAddress;}
    public void setEmailAddress(String emailAddress) {this.emailAddress = emailAddress;}

    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • 1
    What about using the [setDisplayName](https://firebase.google.com/docs/reference/android/com/google/firebase/auth/UserProfileChangeRequest.Builder#public-userprofilechangerequest.builder-setdisplayname-string-displayname) method to store custom fields as a JSON Object like this example `{"display_name": "User 1", "age": 25, "gender": "Male", "points": 5371}`? Is it good practice? And what is the maximum of characters can I store inside display_name? – Taha Sami Feb 03 '22 at 13:01
  • 1
    @TahaSami That's not even possible. setDisplayName()` method takes an argument of type String. You cannot call that method with a JSON object. – Alex Mamo Feb 04 '22 at 06:54
  • 1
    But I can convert JSON Object as String type. By the way, I tried this by myself and when I sort long content inside the display name field the result will be null, I don't know where can I store the custom fields, I don't want to use Firestore because there is a lot of scenarios like connection failure also there is no support transaction between Firestore and Auth. I don't know why the Firebase team does not add a new field to Auth and this field will be for custom fields. I don't want to use Firebase Functions because it may fail also. Any other solutions? – Taha Sami Feb 04 '22 at 07:08
  • 1
    @TahaSami Using an extra step in which you can convert a JSON object into a String might be possible. I'm not aware of any other solutions. However, without seeing your code, I cannot say why you got null. If you are looking for a solution, please post a new question, here on StackOverflow, using its own [MCVE](https://stackoverflow.com/help/mcve), so I and other Firebase developers can help you. Besides that, send that as a request directly to the Firebase team. Maybe it will implement such functionality at one point in time. – Alex Mamo Feb 04 '22 at 07:20
  • 1
    Okay, I appreciate your help, Thank you. – Taha Sami Feb 04 '22 at 07:25
6

In general, after sign up, you can create a node tree for user like picture. Then, you can get the info with user key that can get by calling FirebaseUser. I hope it helps. If you have any questions, let me know.an example

ShineMan
  • 354
  • 5
  • 14
2

Sorry about the delay, but I was looking for how to store considerably larger custom data - which isn't possible, however here's how I do it.

Store small amounts of extra data inside the displayName (Javascript).

result.user.updateProfile({
    displayName: gender + '|' + sex + '|' + username
})
.then(function () {
    console.log(`Profile updated.`);

To retrieve the data, simply split up the displayName (Node.js):

        return admin.auth().getUser(uid)
    })
    .then(userRecord => {
        displayName = userRecord.getDisplayName();

        parts = String(user.displayName).split('|');

        gender = parts[0];
        sex = parts[1];
        username = parts[2];
iStuart
  • 413
  • 4
  • 6
  • This is a nice workaround, thank you. Do you happen to know how much info I can put into this field? How many characters? I can't find that info anywhere. – darksoulsong Dec 11 '19 at 12:43
  • 3
    Further to my original reply, I discovered the 'correct' way to do this using "Firebase Auth Custom Claims". You can read about that here: https://firebase.google.com/docs/auth/admin/custom-claims and there is a fantastic NetNinja video here: https://www.youtube.com/watch?v=SSiLsIkPQWs&list=PL4cUxeGkcC9jUPIes_B8vRjn1_GaplOPQ&index=18&t=0s – iStuart May 16 '20 at 19:39
  • You should use your firebase db or firestore to store these kind of data. Using displayName to store such information might work but it doesn't make any sense and it's wrong! Also custom claims are for storing information like "roles". Such as: Checking if the user is admin or not. If you check out the documentation for custom claims, you will see the warnings and the best practices for it. – cyonder Aug 12 '20 at 18:27
  • Using custom claims for additional data is not recommenced see this statement in google documentation `Custom claims are only used to provide access control. They are not designed to store additional data (such as profile and other custom data). While this may seem like a convenient mechanism to do so, it is strongly discouraged as these claims are stored in the ID token and could cause performance issues because all authenticated requests always contain a Firebase ID token corresponding to the signed in user` shorturl.at/enENP – Paritosh Agrawal May 30 '21 at 09:22
1

After signing up a new user you will receive a generated uuid of the user from Firebase, that's when you need to take the uuid and create a new user entry in your Firestore repository/Realtime database with the uuid that you were provided and the additional informations.

Yassir Khaldi
  • 1,452
  • 1
  • 17
  • 30