Mahier
Mahier

Reputation: 113

How do I create custom tokens in Firebase using React?

I am trying to create a custom a custom token to log users in with their username. I've been through some of the documentation https://firebase.google.com/docs/auth/admin/create-custom-tokens#web, which was linked to me via How to provide user login with a username and NOT an email?, and I have seen that I need to add

Create custom tokens using the Firebase Admin SDK

and

Sign in using custom tokens on clients

At the moment I can kinda see what needs to be included based on the documentation, but I am unsure as to where this would go in the source code. Where do I add the code from the documentation? This is the source code for the userUser.js file, in case it helps.

import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import firebase from "firebase/app";
import "firebase/auth";

import initFirebase from "../../config";
import {
  removeUserCookie,
  setUserCookie,
  getUserFromCookie,
} from "./userCookie";

initFirebase();

export const mapUserData = async (user) => {
  const { uid, email } = user;
  const token = await user.getIdToken(true);
  return {
    id: uid,
    email,
    token,
  };
};

const useUser = () => {
  const [user, setUser] = useState();
  const router = useRouter();

  // this is most likely where the custom token for
  // username goes
  const logout = async () => {
    return firebase
      .auth()
      .signOut()
      .then(() => {
        router.push("/");
      })
      .catch((e) => {
        console.error(e);
      });
  };

  useEffect(() => {
    const cancelAuthListener = firebase
      .auth()
      .onIdTokenChanged(async (userToken) => {
        if (userToken) {
          const userData = await mapUserData(userToken);
          setUserCookie(userData);
          setUser(userData);
        } else {
          removeUserCookie();
          setUser();
        }
      });

    const userFromCookie = getUserFromCookie();
    if (!userFromCookie) {
      return;
    }
    setUser(userFromCookie);
    return () => cancelAuthListener;
  }, []);

  return { user, logout };
};

export { useUser };

Any help would be greatly appreciated.

Upvotes: 0

Views: 5556

Answers (1)

I'm Joe Too
I'm Joe Too

Reputation: 5840

You can only use the admin sdk in a server environment (like in Firebase Functions or some other server) - you can't use it in the client environment where you're using React. Conceptually, the way this works is:

  1. User enters a username and password in your client app
  2. Client app sends the username and password to your server
  3. Server checks the username and password and, if correct, creates a custom token using the admin SDK and sends that back to the client app
  4. Client app uses that custom token to sign into Firebase

So it would look something like this (note - I don't handle any errors here but you'll want to):

// client.js
const sendToServer = (username, password) => {
  // Step 1 - client sends the username/password to the cloud function
  return axios.post(`${myCloudFunctionUrl}/login`, {
    username,
    password
  }).then((response) => {
    // Step 5 - the client logs the user in with the custom token
    return firebase.auth().signInWithCustomToken(response.data.token)
  }).then(() => {
    // Step 6 - the user is now logged in and redirected to the dashboard
    router.push("/dashboard")
  })
}
// server.js (using Firebase Functions, but use whatever back end you want)
exports.login = functions.https.onRequest((req, res) => {
  const {username, password} = req.body
  // Step 2 - function verifies the username and password and gets the user's uid for the custom token
  return verifyUserInDatabase(username, password).then((uid) => {
    // Step 3 - the server creates a custom token
    return admin.auth().createCustomToken(uid)
  }).then((token) => {
    // Step 4 - the server sends the token back in its response
    res.json({ token })
  })
})

Upvotes: 7

Related Questions