2
votes

EDIT: for some reason my Vue frontend isn't setting the session cookie

Goal:

Implement Login with Twitch and have synced authenticated session between frontend and backend

Description:

I have a problem with making Passport.js authentication work when in development environment with my architecture described below. I'm trying to implement Login with Twitch (social website) using passport-twitch

Frontend: Vue.js, webpack

Backend: Node, Express, Passport

My Development Environment:

I run npm run dev and have a hot-reload server running my Vue.js client on localhost:8080. Note that this hot-reload server for reloading the frontend is only run when I'm developing.

I also have my Node backend serving some APIs while running on localhost:3000 and my local frontend will make HTTP requests to the local backend.

Production Environment

When preparing for production, I will run npm run build on my Vue.js frontend and it will be minified and placed in a dist folder as plain static HTML, CSS, and JS files.

My Node/Express backend server will serve these static files as well as supporting the backend APIs.

Code

This is my auth.js module that I import in my app.js file that npm start will start server with:

const passport = require('passport')
const TwitchStrategy = require('passport-twitch').Strategy

// Retrieve our encrypted secrets depending on the environment
const config = require('config')

// Postgresql connection
var knex = require('knex')({
  client: 'pg',
  connection: config.get('postgres')
});

passport.use(new TwitchStrategy({
  clientID: config.get('twitch.clientID'),
  clientSecret: config.get('twitch.clientSecret'),
  callbackURL: config.get('twitch.authCallbackURL'),
  scope: 'user_read'
}, (accessToken, refreshToken, profile, done) => {
  // Upsert into users table
  knex.raw(`
    INSERT INTO users
      (twitch_id)
    VALUES
      (${profile.id})
    ON CONFLICT (twitch_id) DO UPDATE SET
      updated_at = now() at time zone 'utc'
    WHERE users.twitch_id = '${profile.id}'
    RETURNING *`)
  .then((rows, err) => {
    return done(err, profile)
  })
}))

passport.serializeUser(function(user, done) {
    done(null, user);
});

passport.deserializeUser(function(user, done) {
    done(null, user);
});

module.exports = passport

I have these endpoints on localhost:3000 backend running to support passport-twitch

app.get('/auth/twitch', passport.authenticate('twitch'))

app.get('/auth/twitch/callback', passport.authenticate('twitch', { failureRedirect: '/'}), (req, res) => {
  // Successful authentication, redirect home.
  return res.redirect('/')
})

Problem when in Development Environment Workflow

I have 2 servers running in development environment.

When writing and testing frontend code in dev, I'm running hot-reloaded frontend at localhost:8080, and call the backend.

But I need to call localhost:3000/auth/twitch to redirect user to Login with Twitch page. However, when user finishes logging in, it will redirect me to localhost:3000/auth/callback, which redirects to localhost:3000/.

The authenticated sessions are disconnected at this point and frontend has no idea how to auth?

How do I sync the authenticated session in development mode between Vue.js client/frontend and the Node backend?

// This helps me get the current authenticated user in the session (returns empty hash in this case)
// Works fine in production mode since the Node backend gets to serve the minified frontend files
app.get('/self', (req, res) => {
  res.json(req.user || {})
})
1

1 Answers

0
votes

Basically I just had to initialize Axios with withCredentials: true

import axios from 'axios'

export default() => {
  return axios.create({
    baseURL: `http://localhost:8081`,
    withCredentials: true
  })
}

Also, when redirecting from my OAuth callback, I had to change it to redirect to my localhost:8080 (hot-reloading Vue frontend) instead of the static files served by my Node backend at localhost:3000

app.get('/auth/twitch/callback', passport.authenticate('twitch', { failureRedirect: '/'}), (req, res) => {
  // Successful authentication, redirect home.
  if(process.env.NODE_ENV === 'production') return res.redirect('/')
  res.redirect('http://localhost:8080')
})