Home > OS >  Access token giving promise
Access token giving promise

Time:06-13

So I have jwt authentication with access and refresh tokens. But the problem is when the access token expires and i generate a new access token on the frontend (react). It gives me a promise

backend/routes/auth.js (Route to refresh token and where im generating the refresh token and storing it (login route))


router.post("/login", async (req, res) => {
  try {
    // Validating user
    const { error } = loginValidation(req.body);
    if (error) return res.status(400).json(error.details[0].message);

    // Making sure email is correct
    const user = await User.findOne({ email: req.body.email });
    if (!user) return res.status(400).json("Invalid email or password");

    // Making sure the password is correct
    const validPass = await bcrypt.compare(req.body.password, user.password);
    if (!validPass) return res.status(400).json("Invalid email or password");

    const userPayload = {
      _id: user._id,
      displayname: user.displayname,
      username: user.username,
      email: user.email,
      bookmarkedTweets: user.bookmarkedTweets,
      likedTweets: user.likedTweets,
      followers: user.followers,
      following: user.following,
      date: user.date,
      month: user.month,
      day: user.day,
      year: user.year,
      profilePic: user.profilePic,
    };

    // Generating access token
    const accessToken = generateAccessToken(userPayload);

    // Generating refresh token
    const refreshToken = jwt.sign(
      user.toJSON(),
      process.env.REFRESH_TOKEN_SECRET
    );

    // Getting info for refresh token
    const newRefreshToken = new RefreshToken({
      refreshToken: refreshToken,
    });

    // Saving refresh token into db
    await newRefreshToken.save();

    res.json({
      _id: user._id,
      displayname: user.displayname,
      username: user.username,
      email: user.email,
      password: user.password,
      bookmarkedTweets: user.bookmarkedTweets,
      likedTweets: user.likedTweets,
      followers: user.followers,
      following: user.following,
      date: user.date,
      month: user.month,
      day: user.day,
      year: user.year,
      profilePic: user.profilePic,
      accessToken: accessToken,
      refreshToken: refreshToken,
    });
  } catch (error) {
    res.sendStatus(500);
    console.log(error);
  }
});


router.post("/refresh/token", async (req, res) => {
  try {
    // Getting refresh token
    const refreshToken = req.body.token;

    // Finding refresh token
    const _refreshToken = await RefreshToken.findOne({ refreshToken: refreshToken });

    // Making sure there is a refresh token and that refresh token exists in db
    if (refreshToken == null) return res.sendStatus(401);
    if (!_refreshToken) return res.sendStatus(403);

    // Vaifying refresh token
    jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
      if (err) return res.sendStatus(403);

      const userPayload = {
        _id: user._id,
        displayname: user.displayname,
        username: user.username,
        email: user.email,
        bookmarkedTweets: user.bookmarkedTweets,
        likedTweets: user.likedTweets,
        followers: user.followers,
        following: user.following,
        month: user.month,
        day: user.day,
        year: user.year,
        date: user.date,
        profilePic: user.profilePic,
      };

      // Generating access token
      const accessToken = generateAccessToken(userPayload);

      res.json({ accessToken });
    });
  } catch (error) {
    res.sendStatus(500);
  }
});

backend/middlewares/authenticateToken.js

const jwt = require("jsonwebtoken");

require("dotenv").config();

module.exports = function authenticateToken(req, res, next) {
  const authHeader = req.headers["authorization"]; // Getting auth header
  const token = authHeader && authHeader.split(" ")[1]; // Getting access token from auth header
  if (token == null) return res.sendStatus(401);

  jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) return res.sendStatus(403); // Making sure the token is valid
    req.user = user;
    next();
  });
};

backend/utils/generateAccessToken.js

const jwt = require("jsonwebtoken");

require("dotenv").config();

module.exports = function (user) {
  return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "15s" });
};

frontend/src/api/axios.js

import axios from "axios";

const BASE_URL = "http://localhost:5000";
const accessToken = sessionStorage.getItem("accessToken");
const refreshToken = localStorage.getItem("refreshToken");

export default axios.create({
  baseURL: BASE_URL,
  headers: { "Content-Type": "application/json" },
});

const client = axios.create({ baseURL: BASE_URL });

export const axiosAuth = async ({ ...options }) => {
  // Sending access token with request
  client.defaults.headers.common.Authorization = `Bearer ${accessToken}`;

  const onSuccess = (response) => response;
  const one rror = (error) => {
    if (error.response.status === 403) {
      // Making function to generate new access token
      const refresh = async () => {
        const { data } = await axios.post(`${BASE_URL}/auth/refresh/token`, {
          token: refreshToken,
        });
        return data;
      };

      // Generating access token
      const newAccessToken = refresh();
      sessionStorage.setItem("accessToken", newAccessToken);

      return client({ ...options });
    }

    throw error;
  };

  return client(options).then(onSuccess).catch(onError);
};

frontend/src/modals/Tweet.modal.js (Where im using axiosAuth)

import React, { useState } from "react";

import AccountCircleOutlinedIcon from "@mui/icons-material/AccountCircleOutlined";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import PublicOutlinedIcon from "@mui/icons-material/PublicOutlined";
import CollectionsOutlinedIcon from "@mui/icons-material/CollectionsOutlined";
import GifBoxOutlinedIcon from "@mui/icons-material/GifBoxOutlined";
import BarChartOutlinedIcon from "@mui/icons-material/BarChartOutlined";
import SentimentSatisfiedAltOutlinedIcon from "@mui/icons-material/SentimentSatisfiedAltOutlined";
import ScheduleOutlinedIcon from "@mui/icons-material/ScheduleOutlined";
import LocationOnOutlinedIcon from "@mui/icons-material/LocationOnOutlined";
import Loader from "../components/Loader/Loader.comp";

import { useDispatch, useSelector } from "react-redux";
import {
  setTweetModal,
  setTweetPending,
  tweetErrorClear,
  tweetFail,
} from "../features/tweet.slice";
import { axiosAuth } from "../api/axios";

function Tweet() {
  const [textfield, setTextfield] = useState("");
  const { user } = useSelector((state) => state.user);
  const { tweetModalIsOpen, error, isLoading } = useSelector(
    (state) => state.tweet
  );
  const { profilePic } = user;

  const dispatch = useDispatch();

  if (error !== "") {
    setTimeout(() => dispatch(tweetErrorClear()), 3000);
  }

  const handleOnClick = async () => {
    dispatch(setTweetPending(true));

    try {
      await axiosAuth({
        url: "/posts/create",
        method: "POST",
        body: { textfield },
      });
    } catch (error) {
      dispatch(tweetFail(error.response.data));
    }
  };

  return (
    <div
      className={
        tweetModalIsOpen
          ? `h-screen w-screen absolute inset-0 bg-white py-2 px-4`
          : `hidden`
      }
    >
      <div className="flex-items justify-between">
        <div
          className="p-1 cursor-pointer rounded-full hover:bg-gray-200 transition-color"
          onClick={() => dispatch(setTweetModal(false))}
        >
          <ArrowBackIcon style={{ fontSize: "1.5rem" }} />
        </div>
        <button
          className="py-1 px-4 transition-color rounded-full bg-blue-500 text-white font-bold hover:bg-blue-600"
          onClick={() => handleOnClick()}
        >
          {isLoading ? <Loader forPage={false} /> : <h1>Tweet</h1>}
        </button>
      </div>
      <div className="flex mt-6 space-x-2">
        {profilePic === "" ? (
          <AccountCircleOutlinedIcon style={{ fontSize: "2rem" }} />
        ) : (
          <img src={profilePic} alt="profile_pic" />
        )}
        <div>
          <textarea
            name="tweet"
            id="tweet"
            rows="4"
            value={textfield}
            onChange={(e) => setTextfield(e.target.value)}
            className="w-screen outline-none text-xl text-gray-700 placeholder:text-gray-600"
            placeholder="What's happening?"
          ></textarea>
          <div className="flex-items space-x-2 text-blue-400 font-bold py-0.5 px-2 rounded-full hover:bg-blue-50 transition-color cursor-pointer w-max">
            <PublicOutlinedIcon style={{ fontSize: "1.5rem" }} />
            <h1>Everyone can reply</h1>
          </div>
          <div className="ring-1 ring-gray-100 my-2 w-[75%]" />
          <div className="flex-items space-x-2 text-blue-500">
            <div className="tweet-icon">
              <CollectionsOutlinedIcon style={{ fontSize: "1.5rem" }} />
            </div>
            <div className="tweet-icon">
              <GifBoxOutlinedIcon style={{ fontSize: "1.5rem" }} />
            </div>
            <div className="tweet-icon">
              <BarChartOutlinedIcon style={{ fontSize: "1.5rem" }} />
            </div>
            <div className="tweet-icon">
              <SentimentSatisfiedAltOutlinedIcon
                style={{ fontSize: "1.5rem" }}
              />
            </div>
            <div className="tweet-icon">
              <ScheduleOutlinedIcon style={{ fontSize: "1.5rem" }} />
            </div>
            <div className="tweet-icon">
              <LocationOnOutlinedIcon style={{ fontSize: "1.5rem" }} />
            </div>
          </div>
        </div>
      </div>
      {error && <h1 className="error err-animation">{error}!</h1>}
    </div>
  );
}

export default Tweet;

Thanks in advance!

CodePudding user response:

I assume that _refreshToken is the promise you refer to. In order to obtain the eventual value of that promise (that is, the refresh token itself), add async to your middleware function and await to the assignment statement:

router.post("/refresh/token", async (req, res) => {
  try {
    const refreshToken = req.body.token;
    const _refreshToken = await RefreshToken.findOne({ refreshToken: refreshToken });

Same in frontend/src/api/axios.js:

const one rror = async (error) => {
  ...
  const newAccessToken = await refresh();
  • Related