Search code examples
reactjsfirebaseauthenticationfirebase-authenticationrefresh-token

How do I use a Firebase refresh token to persist authentication?


I have been trying to figure this out for weeks and either can't seem to understand the documentation, or something. I appreciate any help you can give.

I am using the Firebase SDK

I have my server-side route, in which I can access the token and could send it to the front:

const admin = require("firebase-admin")
admin.initializeApp()

exports.loginRoute = (req, res) => {
    const user = {
        email: req.body.email,
        password: req.body.password
    }
    const { valid, errors } = validateLoginData(user)

    if (!valid) {
        return res.status(400).json(errors)
    }

    admin
        .auth()
        .signInWithEmailAndPassword(user.email, user.password)
        .then((data) => {
            console.log(data.user.refreshToken, "refresh token")
            return data.user.getIdToken(true)
        })
        .then((token) => {
            return res.json({ token })
        })
        .catch((err) => {
            console.error(err)
            if (err.code === "auth/user-not-found") {
                return res.status(400).json({ general: "User not found" })
            } else if (err.code === "auth/wrong-password") {
                return res
                    .status(400)
                    .json({ password: "User credentials don't match" })
            } else {
                res.status(500).json({
                    error: "Something went wrong, please try again."
                })
            }
        })
}

Here is where I could use the refresh token (on the front end) to fetch a new authentication token, but I can't figure out how to create a route to do this:

if (token) {
        const decodedToken = jwtDecode(token)
        if (decodedToken.exp * 1000 < Date.now()) {
            localStorage.setItem("Authentication", false)
    //axios request to persist authentication would go here
        }
    }

Does anyone have a route that would work, or advice on what to do?

EDIT

const login = async (credentials) => {
        let token
        await axios
            .post("/api/login", credentials)
            .then((res) => {
                token = res.data.token
                const FBIdToken = `Bearer ${token}`
                localStorage.setItem("token", token)
                localStorage.setItem("FBIdToken", FBIdToken)
                localStorage.setItem("Authentication", true)
                context.setAuthenticated((prev) => true)
            })
            .then(() => {
                context.getUserData()
            })
            .then(() => {
                context.setUserState((prevUserState) => ({
                    ...prevUserState,
                    token
                }))
            })
            .catch((err) => {
                context.setUserErrors((prev) => ({
                    ...prev,
                    errors: err.response.data
                }))
            })
        history.push("/")
    }

Observer (client-side):

firebase.auth().onAuthStateChanged((user) => {
        if (user) {
            firebase
                .auth()
                .currentUser.getIdToken(/* forceRefresh */ true)
                .then((idToken) => {
                    const FBIdToken = `Bearer ${idToken}`
                    localStorage.setItem("FBIdToken", FBIdToken)
                })
                .catch((err) => {
                    console.log(err)
                })
        } else {
            localStorage.removeItem("FBIdToken")
        }
    })

Solution

  • If you sign in with the Firebase Authentication JavaScript SDK in the client-side code, it already persists the user's sign-in state, and tries to restore it when you reload the page. You shouldn't have to do anything for that yourself.

    It seems like you were using the same SDK in a server-side environment though, which is quite unusual. If you want to mint tokens yourself in a server-side environment, you should use the Firebase Admin SDK to do so. You can then send that token back to the client, and use it to sign in to Firebase Authentication there.

    But for the vast majority of use-cases, I recommend using the Firebase Authentication SDK in your client-side code, so that the SDK managed refreshing of the token for you. If you then want to pass the token to the server, you can use getIdToken() as you do now. You can also monitor ID token generation, or more commonly monitor if a user's sign-in session is restored as shown in the first example of the documentation on detecting the current user.