Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

http-only cookie + token : double job?

Dears,

I'm trying to find how managing authentification on client side using the hhtp-only cookie sent by the server.

What I don't understand is that since the HTTP only cookie can't be accessed by the front end, how the front end knows that the user is (still) authenticated ?

So far, the only solution if found is to send to the client a token when the authentication succeed. And keep this token in a second cookie created by the client.

But it seems to me that I'm doing the same job twice.

1- managing the HTTP only cookie on server side, especially the expiration date 2- managing also on client side the expiration date of the second cookie.

How can avoid this ? I'd like to manage the authentification on client side based on the HTTP only server cookie. If there is a server cookie, then go on, else redirect to login page.

I'm using node/express on server side and react on client one. The session is stored in redis, both sides are HTTPS using certificates.

Thks

like image 453
Jerome Avatar asked Apr 13 '26 14:04

Jerome


1 Answers

You don't need to store another cookie. I suppose you use token based authentication on your endpoint, eg. JWT. Then you think about this scenario:

  1. User send username/password to server.
  2. Check user credentials and if there are valid, create http-only cookie with the token
    const user = await getUser({ where: { email } });

    const valid = await bcrypt.compare(password, user.password);
    if (!valid) {
      throw new UserInputError('Form Arguments invalid', {
        invalidArgs: {
          'password': 'Invalid password!',
        },
      });
    }

    const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
    /
    res.cookie('token', token, {
      httpOnly: true,
      maxAge: 1000 * 60 * 60 * 24 * 365,
    });

  1. Write auth middlerware to put the userId onto the req for future requests to access
const jwt = require('jsonwebtoken');
const { AuthenticationError } = require('apollo-server');

module.exports = async function(req, res, next) {
  const { token } = req.cookies;

  if (token) {
    try {
      const { userId } = jwt.verify(token, process.env.APP_SECRET);

      if (!userId) return next();
      req.userId = userId;

    } catch (e) {
      console.log(e);
    }
  }

  next();
};
  1. Check on each request the userId. If there is no userId, user doesn't logged in
  if (!req.userId) {
     throw new AuthenticationError('Log in!');
   }
  1. If user's token is invalid/expired you will get AuthenticationError. Catch it and redirect to login page.
  2. If your UI depends on user status, you can create easy-to-use component (i am using React) to check it.

User Component:

import { Query } from 'react-apollo';
import gql from 'graphql-tag';
import PropTypes from 'prop-types';

const CURRENT_USER_QUERY = gql`
  query CURRENT_USER_QUERY {
    me {
      userId
      firstName
      lastName
      profilePictureUrl
    }
  }
`;

const User = props => (
  <Query {...props} query={CURRENT_USER_QUERY} fetchPolicy={'cache-first'}>
    {payload => props.children(payload)}
  </Query>
);

User.propTypes = {
  children: PropTypes.func.isRequired,
};

export default User;

If we get me object from server, you know, there is a logged in user, so you can render depends on user's status:

import { Link } from 'react-router-dom';
import React from 'react';

<User>
  {({ loading, error, data: { me } }) => {
    if (loading || error || !me) return (
      <Button component={Link} to={'/login'}>Login</Button>
    );
    if(me) return (
      <Button component={Link} to={'/dashboard'}>Go to dashboard</Button>
    )
  }}
</User>
like image 58
landorid Avatar answered Apr 15 '26 04:04

landorid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!