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

2

11

I used passportjs in the past with expressjs and currently I'm trying to incorporate it with Sapper app but I'm unable to figure out how to inlcude the passport.authenticate() in my route because it's a sapper route not an express route. Also if I try to run everything in my server.js file I run into the issue of how to integrate it with the sapper middleware. How do you use passport.authenticate() in/with Sapper middleware or sapper routes js files (which is the front not server routes)?

My server.js is typical:

const sirv = require('sirv');
import express from 'express';
var cookieParser = require('cookie-parser');
import * as sapper from '@sapper/server';
const session = require('express-session');
var passport = require('passport');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/passport', { 
 useNewUrlParser: true });
const MongoStore = require('connect-mongo')(session);
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    store: new MongoStore({ url: 'mongodb://localhost/passport' }),
    cookie: { secure: false, maxAge: 1000 * 60 * 60 * 24 * 7 }
}));
app.use(passport.initialize());
app.use(passport.session());

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

const assets = sirv('static', {
    maxAge: 31536000, // 1Y
    immutable: true
});

 app.use(assets, sapper.middleware({
    session: req => ({
        user: req.session && req.session.user
    })})).listen(process.env.PORT, err => { if (err) console.log('error', err); });

As you can see, Sapper is just a middleware so if I want to authenticate a user and send it to the front/sapper, I need to figure out how to run passport.authenticate() inside the middleware function, right?

If I want to use passport in the route JS file which is sapper front route:

//How to import passport.js here to make passport.authenticate() middleware available?
import passport from './passport';
import User from './mongoso';

export async function post(req, res, next) {

    res.setHeader('Content-Type', 'application/json');
    /* Retrieve the data */ 
    var data = req.body;


    req.session.user = data.email;
    console.log("Here's the posted data:", data);
    console.log("information in the session is:", req.session);

    /* Returns the result */
    return res.end(JSON.stringify({ Email: req.session.user }));
    //return res.json({ data: data });

}

Any ideas? Greatly appreciated if someone out there could help.

Shammy answered 25/10, 2019 at 13:37 Comment(0)
S
14

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!

Studnia answered 1/11, 2019 at 12:15 Comment(0)
T
3

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

Thor answered 31/5, 2020 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.