How to Authenticate Users And Save Data in a Database Using Firebase

[ad_1]

When you go about constructing an utility, there are a ton of issues to contemplate. And these are all primarily involved with the consumer a part of the undertaking.

When you begin to take into consideration the server in your utility, issues can get fairly sophisticated. One means to alleviate a few of that strain is to use Firebase – and two options in specific:

  1. Authenticating customers utilizing Firebase Auth
  2. Storing information utilizing a Realtime Database

In this text, you may study:

  • How to construct an Android utility in Kotlin which authenticates customers with Firebase Auth
  • How to use Retrofit2 to make requests to your server
  • How to construct a server in Node.js with Express that can obtain requests out of your utility and fetch information from a Realtime Database in Firebase

All of this may look like a easy activity, however it isn’t. There is a lot of establishing to do and we now have to deal with varied configurations as nicely. But I’ll additionally define some pitfalls that can assist prevent time and frustration.

Trust me – you need to study from my errors.

If you need to skip over all the clarification, you’ll be able to head to the underside of the article and see the complete supply code there by means of the hyperlinks.

Ok, let’s get began.

Setting up your undertaking

Our utility will encompass each a entrance finish and a again finish. From the frontend perspective, there can be a login/signup web page and one other web page that can fetch/ship random information to our database.

We can be utilizing Firebase Authentication right here to validate registered customers. There are a number of methods to authenticate customers:

  • Email & Password
  • Google/Facebook/Twitter/Github account (what is named Federated Identity Provider Identification)
  • Phone quantity
  • Custom authorization
  • Anonymous authorization

In our utility we’ll use the Email & Password choice, as it’s the extra simple strategy (and in most instances, the extra widespread answer).

This authentication will occur in our consumer and there can be no want for any communication to our again finish for this activity.

To make requests to the our server, we can be utilizing Retrofit2 by making GET requests. In these GET requests, we can be sending the info that wants to be up to date alongside a token (extra in regards to the token in the Server part).

From the backend facet, our server is in cost of accepting requests from customers utilizing our utility to both fetch/save/delete information (or CRUD).

To give you the chance to let authenticated customers entry the database, we’ll want to use Firebase’s Admin SDK. This framework will give us entry to an API to confirm authenticated customers and move requests to our database.

We can be saving customers’ information utilizing Firebase’s Realtime Database. After all is finished on the backend facet, we can be deploying it through Heroku.

Photo by dylan nolte on Unsplash

How to Build out the Client  Side/ UI

After opening a new Kotlin undertaking, we’d like to import some dependencies. First and foremost, you want to add Firebase to your undertaking.

Follow the steps outlined right here to achieve this.

Once that’s executed, add the next dependency to your utility degree construct.gradle file:

implementation 'com.google.firebase:firebase-auth:19.4.0'

When customers open the applying, they’ll both login or signup (whether it is their first time).

Since we now have agreed that customers can be validated primarily based on a mixture of their e mail and password, we’ll create a easy exercise that has two EditTexts for doing precisely that. We’ll even have two buttons to signify the selection to both signup or login.

<?xml model="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:instruments="http://schemas.android.com/tools"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/email_edit_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:trace="Enter your email"
        android:enterType="textEmailAddress"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.153" />

    <EditText
        android:id="@+id/password_edit_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:trace="Enter your password"
        android:enterType="textPassword"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email_edit_text"
        app:layout_constraintVertical_bias="0.046" />

    <Button
        android:id="@+id/Login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textual content="Login"
        android:background="#39e600"
        android:onClick="loginUser"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.139"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password_edit_text"
        app:layout_constraintVertical_bias="0.146" />

    <Button
        android:id="@+id/Signup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textual content="Signup"
        android:background="#4d94ff"
        android:onClick="signupUser"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.647"
        app:layout_constraintStart_toEndOf="@+id/Login"
        app:layout_constraintTop_toBottomOf="@+id/password_edit_text"
        app:layout_constraintVertical_bias="0.146" />
</androidx.constraintlayout.widget.ConstraintLayout>
Our Login Screen Layout
bundle com.tomerpacific.todo.actions

import android.content material.Intent
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.duties.OnFullListener
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.UserProfileChangeRequest
import com.tomerpacific.todo.R

class LoginExercise : AppCompatActivity() {

    personal var personEmail : String = ""
    personal var personPassword: String = ""

    override enjoyable onCreate(savedInstanceState: Bundle?) {
        tremendous.onCreate(savedInstanceState)
        setContentView(R.structure.activity_login)
      
      // START 1 ---------------------- //

        discoverViewById<EditText>(R.id.email_edit_text).apply {
            setOnEditorActionListener {_, actionId, keyEvent ->
                if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE ||
                    keyEvent == null ||
                    keyEvent.keyCode == KeyEvent.KEYCODE_ENTER) {
                    personEmail = textual content.toString()
                }
                false
            }

            setOnFocusChangeListener {view, gainedFoucs ->
                personEmail = textual content.toString()
            }
        }

        discoverViewById<EditText>(R.id.password_edit_text).apply {
            setOnEditorActionListener {_, actionId, keyEvent ->
                if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE ||
                    keyEvent == null ||
                    keyEvent.keyCode == KeyEvent.KEYCODE_ENTER) {
                    personPassword = textual content.toString()
                }
                false
            }

            setOnFocusChangeListener {view, gainedFoucs ->
                personPassword = textual content.toString()
            }
        }
      
      // END 1 ---------------------------------------- //
    }

    override enjoyable onStart() {
        tremendous.onStart()
        FirebaseAuth.getInstance().currentUser?.let {
            Intent(this@LoginExercise, MainActivity::class.java).apply {
                startActivity(this)
            }
        }
    }

  // START 2 ----------------------- //
    enjoyable loginUser(view : View) {

        if (personEmail.isEmpty() || personPassword.isEmpty()) {
            Toast.makeText(this, "Please make sure to fill in your email and password", Toast.LENGTH_SHORT).present()
            return
        }

        FirebaseAuth.getInstance().signalInWithEmailAndPassword(personEmail, personPassword)
            .addOnFullListener(this) { activity ->
                if (activity.isSuccessful) {
                    replaceFirebaseUserDisplayName()
                } else {
                    Toast.makeText(this, "An error has occurred during login. Please try again later.", Toast.LENGTH_SHORT).present()
                }
            }
    }
  
  // END 2 ----------------------------- //

  // START 3 --------------------------- //
    enjoyable signupUser(view: View) {

        if (personEmail.isEmpty() || personPassword.isEmpty()) {
            Toast.makeText(this, "Please make sure to fill in your email and password", Toast.LENGTH_SHORT).present()
            return
        }

        FirebaseAuth.getInstance().createUserWithEmailAndPassword(personEmail, personPassword)
            .addOnFullListener(this) { activity ->
                if (activity.isSuccessful) {
                    replaceFirebaseUserDisplayName()
                } else {
                    Toast.makeText(this, "An error has occurred during signup. Please try again later.", Toast.LENGTH_SHORT).present()
                }
            }
    }

    personal enjoyable replaceFirebaseUserDisplayName() {

        FirebaseAuth.getInstance().currentUser?.apply {
            val profileUpdates : UserProfileChangeRequest = UserProfileChangeRequest.Builder().setDisplayName(personEmail).construct()
            updateProfile(profileUpdates)?.addOnFullListener(OnFullListener {
                when(it.isSuccessful) {
                    true -> apply {
                        Intent(this@LoginExercise, MainActivity::class.java).apply {
                            startActivity(this)
                            end()
                        }
                    }
                    false -> Toast.makeText(this@LoginExercise, "Login has failed", Toast.LENGTH_SHORT).present()
                }
            })
        }
    }
  // END 3 ------------------------------------- //

}
LoginExercise.kt

Let’s see what is going on on in the above code.

  1. We are attaching listeners to our edit texts to determine once they have both misplaced focus or the person has pressed the executed button.
  2. The loginUser methodology is in cost of, nicely, authenticating the person primarily based on their prior credentials (utilizing the signalInWithEmailAndPassword API).
  3. The signupUser methodology makes use of the createUserWithEmailAndPassword API.
  4. You can see that we now have overridden the onStart lifecycle methodology to determine when the person returns to the applying and to replace the UI appropriately if the person is already logged in.

When operating our utility, we’ll see this:

Nothing too fancy

That was the straightforward half. Before we transfer on to writing the logic to talk with the again finish, let’s first construct the again finish.

Photo by Roger Starnes Sr on Unsplash

How to Set Up the Server

We can be utilizing Express when constructing our server. Below is a template for such a server which additionally provides headers to bypass any CORS points we’d encounter:

const specific = require('specific')
var bodyParser = require('body-parser')
const app = specific()
var port = course of.env.PORT || 3000

app.use(bodyParser.urlencoded())

app.use(operate(req, res, subsequent) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', true)
  return subsequent()
});

app.pay attention(port, () => console.log(`Example app listening at http://localhost:${port}`))

Similar to what we did in the consumer, we additionally want to add Firebase to our Node.js server. If you recall the steps you took to create a undertaking in Firebase and selected an Android undertaking, you want to add to that undertaking one other utility which can symbolize our server. By clicking on Add App in the principle display screen of Firebase console,

You can be introduced with a platform to select from:

You want to select the Web choice (the one with the </> icon)

After doing the preliminary configuration contained in the Firebase console, you’ll need to add the configuration object to your undertaking:

var firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "PROJECT_ID.firebaseapp.com",
  databaseURL: "https://PROJECT_ID.firebaseio.com",
  projectId: "PROJECT_ID",
  storageBucket: "PROJECT_ID.appspot.com",
  messagingSenderId: "SENDER_ID",
  appId: "APP_ID",
  measurementId: "G-MEASUREMENT_ID",
};
Example of firebaseConfig (fill in your tasks’ particulars)

We will place these configurations in our most important file (app.js):

const specific = require('specific')
var bodyParser = require('body-parser')
const app = specific()
var port = course of.env.PORT || 3000

<--- FIREBASE CONFIG --->
var firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "PROJECT_ID.firebaseapp.com",
  databaseURL: "https://PROJECT_ID.firebaseio.com",
  projectId: "PROJECT_ID",
  storageBucket: "PROJECT_ID.appspot.com",
  messagingSenderId: "SENDER_ID",
  appId: "APP_ID",
  measurementId: "G-MEASUREMENT_ID",
};
  
  // Initialize Firebase
firebase.initializeApp(firebaseConfig);
<---- END FIREBASE CONFIG --->

app.use(bodyParser.urlencoded())

app.use(operate(req, res, subsequent) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', true)
  return subsequent()
});

app.pay attention(port, () => console.log(`Example app listening at http://localhost:${port}`))

You is perhaps considering: “I am saving all this secret information in the client. It will be visible to everyone!”. This is totally true, however in the case of Firebase, it’s okay.

How to Configure the Database

We’re getting there, however we nonetheless have some extra configuration to do. This time it has to do with our Realtime database.

Head over to your Firebase console and select the undertaking you created earlier in this text. On the left menu, you will notice a Realtime Database choice. Click it.

Next, on the best facet, a window will load with the next tabs:

Under the Data tab, there can be your database’s URL. Remember it as we’ll want to use it afterward.

The different essential tab to take a look at is the Rules tab. These guidelines specify who has entry to your database and what they’ll do there.

Initially (and for testing functions), the principles there are fairly lax and let anybody learn and write out of your database. Before you make your utility stay, be sure to replace these guidelines with one thing extra restrictive. Don’t fear, you’ll get to see an instance.

How to Set UP Firebase Admin SDK

Next up, we’d like to arrange Firebase Admin SDK. Since we already arrange the mandatory issues in the Firebase console, we’d like to set up the firebase admin bundle.

npm set up firebase-admin --save

Now we’d like to generate a personal key since our undertaking is a service account. Inside the Firebase Console, observe these steps:

First, subsequent to Project Overview, there may be a gear icon. Click it and select Project Settings:

Then click on on the Service Accounts tab, and click on the Create Service Account button.

Choose Node.js because the configuration snippet, and click on on Generate new personal key.

Place this file inside your undertaking and alter the trail to it in the code snippet supplied by Firebase.

⚠️ Make certain to exclude this file in your .gitignore file and to by no means add it to any public repository.

Putting all of it collectively, our app.js file will appear to be this:

const specific = require('specific')
var bodyParser = require('body-parser')
const app = specific()
var port = course of.env.PORT || 3000

<--- FIREBASE CONFIG --->
var firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "PROJECT_ID.firebaseapp.com",
  databaseURL: "https://PROJECT_ID.firebaseio.com",
  projectId: "PROJECT_ID",
  storageBucket: "PROJECT_ID.appspot.com",
  messagingSenderId: "SENDER_ID",
  appId: "APP_ID",
  measurementId: "G-MEASUREMENT_ID",
};
  
  // Initialize Firebase
firebase.initializeApp(firebaseConfig);
<---- END FIREBASE CONFIG --->

const serviceAccount = require("PATH_TO_YOUR_SERVICE_ACCOUNT_FILE.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "URL_TO_DATABASE"
});  
  
app.use(bodyParser.urlencoded())

app.use(operate(req, res, subsequent) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', true)
  return subsequent()
});

app.pay attention(port, () => console.log(`app listening at http://localhost:${port}`))

Remember the database URL I discussed earlier? You will want to insert it inside the item you move to Firebase’s admin initializeApp methodology.

How to Create An Endpoint & Deploy

Phew, that was a lot of establishing. Right now, our server is in a position to run, however it gained’t do something since there isn’t any endpoint configured.

To repair that scenario, let’s outline one among our endpoints:

app.get('/getData', operate (req, res) {
  if (req.headers.authtoken) {
    admin.auth().verifyIdToken(req.headers.authtoken)
    .then(() => {
      var database = admin.database()
      var uid = req.question.uid
      database.ref('/customers/' + uid).as soon as('worth')
      .then(operate(snapshot) {
        var information = snapshot.val() ? snapshot.val() : []
        res.standing(200).ship({ our_data: information})
      }).catch(operate(error) {
        res.standing(500).json({ error: error})
      })
    }).catch(() => {
      res.standing(403).ship('Unauthorized')
    })
  } else {
    res.standing(403).ship('Unauthorized')
  }
})

Our endpoint is named getData and you’ll see that earlier than doing another logic, we’re extracting the authtoken despatched and verifying it utilizing Firebase admin.

If every part works accurately, we proceed with getting the person’s ID and utilizing that to fetch the person’s information from the database.

How to Make the Requests on the Client

As I discussed earlier, we can be utilizing Retrofit2 to make our requests to the server.

I gained’t go into element about how to use Retrofit2 right here (there are many articles that just do that), so beneath you’ll find the usual implementation of constructing community requests utilizing Retrofit2.

enjoyable fetchDataFromDB() {
        val person = FirebaseAuth.getInstance().currentUser

        if (person != null) {
            person.getIdToken(false).addOnFullListener{
                if (it.isSuccessful) {
                    val token = it.end result?.token

                    val retrofit = Retrofit.Builder()
                        .baseUrl(TodoConstants.BASE_URL_FOR_REQUEST)
                        .addConverterFactory(GsonConverterFactory.create())
                        .construct()
                    val service = retrofit.create(DataService::class.java)
                    val name = service.getData(token, getUserUUID())

                    name.enqueue(object: Callback<Result> {
                        override enjoyable onResponse(name: Call<Result>, response: Response<Result>) {
                            if (response.isSuccessful) {
                                val physique = response.physique() as Result
                               //Here we now have the info despatched again from the server
                            }
                        }

                        override enjoyable onFailure(name: Call<Result>, t: Throwable) {

                        }
                    })
                }
            }
        }
    }

Notice that after getting the FirebasePerson object, we use the getIdToken methodology to extract the token which can be despatched to the server.

In the identical method, we will create one other GET request to set information in our database.

And that is it! Thanks for following alongside.

This article relies on what I went by means of when constructing my very own utility. You can test it out beneath (the supply code can be out there):

Todo – Apps on Google Play

Ever had a exhausting time remembering all of the belongings you want to get executed in a day? Ever wanted a place to write down your grocery listing? This utility will allow you to write down all of the duties you need to get achieved and allow you to share it with anybody by means of Whatsapp.

TomerPacific/Todo

Todo List utility that makes use of Firebase to Authenticate customers and save information in a database. There can be an choice to export the listing to WhatsApp. – TomerPacific/Todo

[ad_2]

Source hyperlink

Write a comment