Following up on my earlier blog on implementing authentication with Firebase (email + password), this blog post will focus on implementing a social login with Firebase and managing an already-logged in user’s profile. If you haven’t read the previous post, you can read it here:
While allowing users to authenticate via email and password is helpful, it’s generally more convenient to instead let them log in to your service using a third-party auth provider like Google, Facebook, Twitter, etc. While this is easier for users, it also allows you access to more user data like their name, profile pic, and so on; whereas, with an email-password login system, we only have access to the user’s email.
In this blog post, we’ll first have a look at implementing social login using Firebase AuthUI, followed by a basic user profile management system that allows a logged-in user to view or modify their account details.
Social Authentication using Firebase
Like email and password authentication and login, Firebase also allows you to authenticate your users with various other providers like Google, Facebook, Twitter, GitHub, and more. You can find a list of all the available providers below:
While we can go ahead and implement social login using Firebase directly as we did with email-password authentication in the last post, to make things easier, we’ll instead be using FirebaseUI, which is a third party library provided by Firebase.
While FirebaseUI provides UI bindings for various Firebase APIs, Authentication is where it shines the most as it makes implementing social login extremely easy. Let’s now look at the implementation process.
Implementing social login with Firebase
Before starting, we need to ensure that the app in question is connected to Firebase. To do so, you can find a good tutorial here:
Once done, you need to add the following dependency in order to use FirebaseUI Auth:
dependencies {
// ...
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'
}
You also need to go to your newly-created app’s Firebase Console and enable the sign-in providers from the Authentication section:
Once you have the project configured, it’s time to get started with the actual authentication implementation.
Step 1: Add an AuthStateListener to your app
Attaching an AuthStateListener to a FirebaseAuth instance does two important things:
- Calls the listener once as soon as it’s attached.
- Calls the listener every time the current AuthState is changed (i.e. if the user logged in or logged out).
To ensure that the user is presented with the login screen as soon as the app is opened, it’s essential that the AuthStateListener is added to the entry-point of your app, preferably inside the onCreate method of your app’s MainActivity.
This is how it might look for you:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
FirebaseAuth.getInstance().addAuthStateListener { firebaseAuth ->
// called once this listener is attached and every time after the auth state changes
firebaseAuth.currentUser?.let {
// the user is logged in
} ?: run {
// the user is logged out, log him/her in
}
}
}
}
Step 2: Determine the authentication providers you’re going to use
Next up, we need to determine which auth providers we’re going to use for the app (in this example I’ll be using Email-Password, Google and Phone Number providers). You can use the helper methods present in AuthUI to do this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
FirebaseAuth.getInstance().addAuthStateListener { firebaseAuth ->
// called once this listener is attached and every time after the auth state changes
firebaseAuth.currentUser?.let {
// the user is logged in
} ?: run {
// the user is logged out, log him/her in
signIn()
}
}
}
fun signIn() {
// we are using Google, Email-Password, and Phone Number based authentication
val providers = listOf(
AuthUI.IdpConfig.GoogleBuilder().build(),
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.PhoneBuilder().build())
}
}
You can add more providers as needed, although using other providers might require you to make additional changes to your project. You can look at the changes required for using additional providers here:
Step 3: Configure the login screen and start the login process
Up next, you can optionally configure the look and feel of your login screen and then start the login process. The code for doing so looks like this:
class MainActivity : AppCompatActivity() {
// ...
fun signIn() {
// we are using Google, Email-Password, and Phone Number based authentication
val providers = listOf(
AuthUI.IdpConfig.GoogleBuilder().build(),
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.PhoneBuilder().build())
val authIntent = AuthUI.getInstance().createSignInIntentBuilder()
// set a custom logo to be shown on the login screen
.setLogo(R.mipmap.ic_launcher)
// set the login screen's theme
.setTheme(R.style.AppThemeNoActionbar)
// define the providers that will be used
.setAvailableProviders(providers)
// disable smart lock that automatically logs in a previously logged in user
.setIsSmartLockEnabled(false)
// set the terms of service and private policy URL for your app
.setTosAndPrivacyPolicyUrls("example.termsofservice.com", "example.privatepolicy.com")
.build()
startActivity(authIntent)
}
}
As you can see in the code above, FirebaseUI allows for some nice customization options on the login screen. If you start the app, this is what it might look like:
As you can see in the screenshots above, I played around with all the available providers, and FirebaseAuthUI took care of creating the UI for each of the providers. You can also add some logic to the AuthStateListener to perform an action when the user completes the login process.
This is how it might look:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
FirebaseAuth.getInstance().currentUser?.let { currentUser ->
// the user is already logged in, so get the details from him/her
// uid is unique for every logged in user, and wil always be not-null
val uid : String = currentUser.uid
// rest of the parameters might be null depending on the auth provider used
val name: String? = currentUser.displayName
val email: String? = currentUser.email
val profilePic : Uri? = currentUser.photoUrl
val contactNumber = currentUser.phoneNumber
}?: kotlin.run {
// currentUser is null, that means the user is not logged in yet!
signIn()
}
}
}
Step 4: Implement logout and account deletion functionality
To logout the currently logged-in user, you can simply call a helper method available in AuthUI.
And to delete a user’s account, you can call a similar method available inside AuthUI:
And that’s it! In the next section, we’ll look at how to implement profile management functionality with Firebase, which will allow users to view and update their profile.
Implementing user profile management
Once the authentication logic is built, you might also want to implement a user profile management feature into your app. While AuthUI doesn’t provide this (despite consistent requests from the community), it’s pretty simple to do ourselves.
Step 1: Create an activity to display a logged-in user’s profile
The first step to implementing a user profile management system would be to have an activity that displays our logged-in user’s info along with an option for them to modify it.
This is how the xml for this might look:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivUserImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4" />
<Button
android:id="@+id/btnUpdateImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Update Image" />
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/btnUpdateName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Update Name" />
<EditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:autofillHints="email"
android:inputType="textEmailAddress" />
<Button
android:id="@+id/btnUpdateEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Update Email" />
<EditText
android:id="@+id/etPhone"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:autofillHints="password"
android:inputType="phone" />
<Button
android:id="@+id/btnUpdatePhone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Update Phone" />
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:inputType="textPassword" />
<Button
android:id="@+id/btnUpdatePwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Update Password" />
</LinearLayout>
</ScrollView>
Step 2: Display the data for a logged-in user
Similar to what we did in the last step, this part consists of getting the logged-in user’s information and displaying it in the activity we have above:
class ProfileActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
FirebaseAuth.getInstance().currentUser?.let { firebaseUser ->
// if the user is logged in, display their info on the screen
etEmail.setText(firebaseUser.email)
etName.setText(firebaseUser.displayName)
etPhone.setText(firebaseUser.phoneNumber)
Picasso.get().load(firebaseUser.photoUrl).into(ivUserImage)
}
btnUpdateImage.setOnClickListener {
// update the profile image here
}
btnUpdateName.setOnClickListener {
// update the name here
}
btnUpdateEmail.setOnClickListener {
// update the email here
}
btnUpdatePhone.setOnClickListener {
// update the phone number here
}
btnUpdatePwd.setOnClickListener {
// update the password here
}
}
}
If you run the app at this current state, this is what it might look like:
Notice that we have empty click-handles for buttons, which we’ll be implementing in the next step.
Step 3: Updating the user’s details
Lastly, to finish up our profile management screen, we need to fill the placeholder callbacks we had earlier. While some methods (like updating the email and password) might look similar to how we did them in the last blog, the process to update other profile details is a little bit different.
Here’s a code snippet showcasing how to do this:
class ProfileActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
// ...
btnUpdateImage.setOnClickListener {
// update the profile image here
val picUpdate = UserProfileChangeRequest.Builder()
.setPhotoUri(Uri.parse("new.photo.url.here"))
.build()
FirebaseAuth.getInstance().currentUser?.updateProfile(picUpdate)
}
btnUpdateName.setOnClickListener {
// update the name here
val nameUpdate = UserProfileChangeRequest.Builder()
.setDisplayName(etName.text.toString())
.build()
FirebaseAuth.getInstance().currentUser?.updateProfile(nameUpdate)
}
btnUpdateEmail.setOnClickListener {
// update the email here
FirebaseAuth.getInstance().currentUser?.updateEmail(etEmail.text.toString())
}
btnUpdatePwd.setOnClickListener {
// update the password here
FirebaseAuth.getInstance().currentUser?.updatePassword(etPassword.text.toString())
}
}
}
The code here is pretty self-explanatory; to update the name and profile picture, we have to create a UserProfileChangeRequest object and pass it to the updateProfile() method present in the FirebaseUser class.
Updating the phone number, however, is a little bit more complex, and you can read more about updating it in detail here:
And that’s it! We not only saw how to implement social login, but we also took a look at implementing a profile management system successfully using Firebase. You can find the entire source code for the blog post here:
Thanks for reading! If you enjoyed this story, please click the 👏 button and share it to help others find it! Feel free to leave a comment 💬 below.
Have feedback? Let’s connect on Twitter.
Comments 0 Responses