felpsey
felpsey

Reputation: 15

Cookie not persisting after redirecting in express.js

I'm currently implementing authentication within my application.

Instead of handling authentication myself, I'm using Discord. This issue is not caused by the use of Discord's API specifically.

The logic:

  1. User proceeds to http://localhost:3000/auth
  2. User clicks on the 'Login with Discord' button, which redirects to Discord's login interface
  3. Upon entering correct credentials, Discord redirects the user back to the application with an access_token
  4. If the access_token is present, express-session initiates the session and adds discord_access_token to the session
  5. The user is redirected to the dashboard

The problem:

Everything up to step 5 above works as intended.

Once redirected to http://localhost:3000/dashboard by res.redirect('/dashboard') in discordAuthController.js the cookie is no longer visible in developer tools and therefore the client cannot access the session data. But if I go to the /dashboard route manually it appears!

Both redirect and manual requests go to the address http://localhost:3000/dashboard.

I've tested different routes when redirecting, whenever I use res.redirect('/ROUTE') the cookie is not present in client storage unless I refresh or go to the route manually.

http://localhost:3000/auth/discord route file (routes/auth/discord.js)

import express from 'express';
import session from 'express-session'

import * as discordAuthController from '../../app/controllers/auth/discordAuthController.js'

const router = express.Router()

router.get('/', async (req, res) => {
    await discordAuthController.index(req, res)
    .catch(err => {
        console.log(err)
        res.sendStatus(500)
    })
})

export default router

/controllers/auth/discordAuthController.js

import { exchangeToken } from '../../models/auth/discord.js'

async function index(req, res) {
  await exchangeToken(req.query.code)
  .then(response => {
    req.session.initialised = true
    req.session.discord_user_id = response.access_token

    res.redirect('/dashboard')
  })  
  .catch(err => {
    console.log(err)
    res.sendStatus(500)
  })
};

export { index }

/models/auth/discord.js

import * as dotenv from 'dotenv' 
dotenv.config()

const api_endpoint = process.env.DISCORD_API_ENDPOINT
const client_id = process.env.DISCORD_CLIENT_ID
const client_secret = process.env.DISCORD_CLIENT_SECRET
const redirect_uri = process.env.DISCORD_REDIRECT_URI
const guild_id = process.env.DISCORD_GUILD_ID

async function makeExchange(auth_code) {
    return await fetch(api_endpoint, {
        method: 'POST',
        headers: { 
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: new URLSearchParams({
          client_id,
          client_secret,
          grant_type: 'authorization_code',
          code: auth_code,
          redirect_uri
        })
      })
    .then(response => response.json())
}

export async function exchangeToken (auth_code) {
    return await makeExchange(auth_code)
}    
    

Upvotes: 0

Views: 1100

Answers (1)

felpsey
felpsey

Reputation: 15

Due to the application redirect coming from a site with a different domain and the current cookie type set to strict, the cookie was behaving normally.

Lax allows the cookie to be sent on some cross-site requests, whereas Strict never allows the cookie to be sent on a cross-site request (Simant, 2020)

After setting the sameSite value of the cookie to lax, the cookie was loaded by the browser as data was read.

Upvotes: 1

Related Questions