using passportjs passport.authenticate() in Sapper route or sapper middleware

I used the answer from DioXine to implement Google Auth.
The cookie is now also http only.

import sirv from "sirv";
import express from "express";
import bodyParser from "body-parser";
import session from "express-session";
import sessionFileStore from "session-file-store";
import compression from "compression";
import * as sapper from "@sapper/server";
import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";

const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === "development";

passport.use(
  new GoogleStrategy(
    {
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: "http://localhost:3000/auth/google/callback",
    },
    function (accessToken, refreshToken, profile, cb) {
      // User.findOrCreate({ googleId: profile.id }, function (err, user) {
      //   return cb(err, user);
      // });
      return cb(null, profile);
    }
  )
);

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

passport.deserializeUser(function (obj, cb) {
  cb(null, obj);
});

const FileStore = sessionFileStore(session);

const sessionConfig = {
  secret: "sefmvks4Fgblolf4sdJHBd",
  resave: false,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,
    maxAge: 31536000,
  },
  //TODO: redis
  store: new FileStore({
    path: `.sessions`,
  }),
};

express()
  .use(passport.initialize())
  .use(bodyParser.json())
  .use(session(sessionConfig))

  .get("/auth/google", passport.authenticate("google", { scope: ["profile"] }))
  .get(
    "/auth/google/callback",
    passport.authenticate("google", { failureRedirect: "/auth/login" }),
    (req, res) => {
      res.redirect("/");
    }
  )
  .get("/auth/logout", (req, res) => {
    req.logout();
    req.session.destroy(function (err) {
      res.redirect("/");
    });
  })
  .use(
    compression({ threshold: 0 }),
    sirv("static", { dev }),
    sapper.middleware({
      session: (req) => {
        const user = req.session.passport ? req.session.passport.user.id : null;
        return { user };
      },
    })
  )
  .listen(PORT, (err) => {
    if (err) console.log("error", err);
  });

This is not changed:

<script context="module">
  export function preload(page, { user }) {
    return { user };
  }
</script>

<script>
  import { stores } from "@sapper/app";
  import { onMount } from "svelte";

  const { session } = stores();
  export let user;

  onMount(() => {
    console.log($session);
  });

</script>

<div>
  {#if !user}
    <p>Not logged in</p>
  {:else}
    <p>Logged in!</p>
  {/if}
</div>

If it only works after refresh check this: https://github.com/sveltejs/sapper/issues/567#issuecomment-542788270


You don't need to run passport.authenticate() inside the sapper.middleware. You need to add passport-local strategy firstly, then do serializeUser and deserializeUser, then create routes to do passport.authenticate and after that catch req.session.passport object in sapper.middleware. I don't use passport-local strategy, but here is my working server.js with passport-github strategy.

//server.js

import sirv from 'sirv';
import express from 'express';
import passport from 'passport';
import { Strategy } from 'passport-github';
import bodyParser from 'body-parser';
import session from 'express-session';
import sessionFileStore from 'session-file-store';
import compression from 'compression';
import * as sapper from '@sapper/server';

const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';

const FileStore = sessionFileStore(session);

passport.use(new Strategy({
    clientID: 'someClientID',
    clientSecret: 'someClientSecret',
    callbackURL: 'http://localhost:3000/auth/callback',
}, (accessToken, refreshToken, profile, cb) => {
    // console.log('success');
    return cb(null, profile);
}));

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

passport.deserializeUser(function (obj, cb) {
    cb(null, obj);
});

const expressServer = express() 
    .use(passport.initialize())
    .use(bodyParser.json())
    .use(session({
        secret: 'conduit',
        resave: false,
        saveUninitialized: true,
        cookie: {
            maxAge: 31536000
        },
        store: new FileStore({
            path: `.sessions`
        })
    }))

    .get('/auth/login',
        passport.authenticate('github'))
    .get('/auth/callback',
        passport.authenticate('github', { failureRedirect: '/auth/login' }),
        (req, res) => {
            res.redirect('/');
            //console.log(req.user.username);
        })
    .get('/auth/logout', (req, res) => {
        req.logout();
        req.session.destroy( function (err) {
            res.redirect('/'); 
        });
    })

    .use(
        compression({ threshold: 0 }),
        sirv('static', { dev }),
        sapper.middleware({
            session: req => {
                const user = req.session.passport ? req.session.passport.user.username : null;
                // console.log(req.session.passport.user.username);
                return { user };
            }
        })
    )
if (dev) {
    expressServer.listen(PORT, err => {
        if (err) console.log('error', err);
    });
}

export { expressServer }

Аfter this, you can catch that this { user } object in your client sapper route component through Stores using const { session } = stores(); console.log($session) or you can get it via special preload function to apply before page is rendered, like this for example in index.svelte

<script context="module">
  export function preload(page, { user }) {
    return { user };
  }
</script>

<script>
  import { stores } from "@sapper/app";
  import { onMount } from "svelte";

  const { session } = stores();
  export let user;

  onMount(() => {
    console.log($session);
  });

</script>

<div>
  {#if !user}
    <p>Not logged in</p>
  {:else}
    <p>Logged in!</p>
  {/if}
</div>

Here i use two approaches same time, but most of time it will be enough to use preload, no need to direct access to session in stores. Hope this will help you. Good luck!