Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle Firebase expiring authentication tokens on React Native

I am using firebase in my react native app as a backend. All client API request go to my firebase cloud functions that handle all the backend logic with Firebase (authentication, firestore, storage). For my authentication logic, I am getting a token once the use logs in with email and password. The token is stored in AsyncStorage and added to the header of every request to the backend.

Everything works so far, the problem is that the token expires after 1 hour or so and the use is forced to login again. I searched the firebase docs and the web and I know that I need to ask for a new token before every authenticated request. The problem is that I can't figure how?

To add more precision, I am using a middleware function (isAuthenticated) for all requests that needs authentication where I verify the token (this is done in my backend server).

// Users routes
app.post("/signup", signup);
app.post("/login", login);
app.post("/user/image", isAuthenticated, uploadImage);
app.post("/user", isAuthenticated, addUserDetails);

My question is how can I implement the token refresh logic, can I ask for a new token with the expired one? Thanks in advance.

Update: (More details) Here is how I get my token in the first place: Client applications (mobile+web) send REST requests to my firebase functions. e.g for login, I have this function :

exports.login = (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);

  firebase
    .auth()
    .signInWithEmailAndPassword(user.email, user.password)
    .then(data => {
      return data.user.getIdToken();
    })
    .then(token => {
      return res.json({ token });
    })
    .catch(err => {
      return res
        .status(403)
        .json({ general: "Wrong credentials, please try again" });
    });
};

Once, logged in, clients receive the token and store it using AsyncStorage/LocalStorage for include it in the authorization header.

For all the authenticated roots, I check the user token using this middleware function:

exports.isAuthenticated = async (req, res, next) => {
  let idToken;
  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith("Bearer ")
  ) {
    idToken = req.headers.authorization.split("Bearer ")[1];
  } else {
    console.error("No token found");
    return res.status(403).json({ error: "Unauthorized" });
  }
      admin
      .auth()
      .verifyIdToken(idToken)
      .then(decodedToken => {
        req.user = decodedToken;
        return db
          .collection("users")
          .where("userId", "==", req.user.uid)
          .limit(1)
          .get();
      })
      .then(data => {
        return next();
      })
  } catch (err) {
    console.error(err);
  }
};

Thanks in advance.

like image 746
Amine Avatar asked Jan 21 '20 06:01

Amine


1 Answers

I can see no one has updated this for a while, so I figured I'd write what I've done on my backend to ReAuthenticate an expired token. This was buried somewhere, and was difficult to find, since the official docs don't cover this.

let body = {
        grant_type: "refresh_token", 
        refresh_token: req.body.refreshToken || null
};

if (!body.refresh_token) {
    return res.status(400).json({
        message: ERROR._400
    })
}

const {data: {refresh_token, id_token}} = await axios.post(`https://securetoken.googleapis.com/v1/tokenkey=${config.apiKey}`, body);
like image 124
Vai130 Avatar answered Sep 22 '22 01:09

Vai130