Как мне добавить свойство сеанса к объекту req, используя Auth0 и passport с приложением MERN?

#javascript #node.js #passport.js #auth0 #mern

#javascript #node.js #passport.js #auth0 #mern

Вопрос:

Я работаю над существующим приложением MERN, и у меня возникли проблемы с auth0 и passport. Из того, что я могу сказать, auth0 обеспечивает аутентификацию и перенаправляет обратно на мой сервер. Однако user свойство, которое должно быть добавлено к объекту req, похоже, не добавляется, что, я полагаю, должно происходить в serializeUser функции. Это чужая кодовая база, поэтому я не знаком с ней на 100% и не могу проконсультироваться с программистом, чтобы задать вопросы.

Я перечислю свой код ниже и попытаюсь изложить свой путь через него, чтобы объяснить мой мыслительный процесс. Я пытался искать похожие вопросы и учебные пособия в Интернете, но ответа пока не появилось.

Вот app.js файл:

 const cookieParser = require('cookie-parser')
const express = require('express')
const session = require('express-session')
const logger = require('morgan')
const passport = require('passport')
const cors = require('cors')
require('dotenv').config()

// connect to the database and define models
require('./db/config')
require('./models')

// require the router modules
const adminRouter = require('./routes/admin')
const authenticationRouter = require('./routes/authentication')
const challengeRouter = require('./routes/challenge')
const codeRouter = require('./routes/code')
const configRouter = require('./routes/config')
const feedbackRouter = require('./routes/feedback')
const hintRouter = require('./routes/hint')
const postAuthenticationRouter = require('./routes/postAuthenticationRouter')
const submissionRouter = require('./routes/submission')
const standingRouter = require('./routes/standing')
const userRouter = require('./routes/user')

const app = express()

// configure express
app.use(logger('dev'))
app.use(express.json())
app.use(cookieParser())

// configure express-session
const { SESSION_SECRET } = process.env
const sessionConfig = {
  secret: SESSION_SECRET,
  cookie: {},
  resave: false,
  saveUninitialized: false,
}

if (app.get('env') === 'production') {
  sessionConfig.cookie.secure = true
}

// prevent status 304
app.disable('etag')

// import passport-auth0 strategy
const auth0Strategy = require('./passport/auth0')

app.use(session(sessionConfig))

passport.use(auth0Strategy)

// load passport strategies
app.use(passport.initialize())
app.use(passport.session())

// gets run after successful Auth0 authenticated login
passport.serializeUser((user, done) => {
  done(null, user);
})

// gets run for each subsequent request after logging in
passport.deserializeUser((user, done) => {
  done(null, user);
})

app.use((req, res, next) => {
  res.locals.isAuthenticated = req.isAuthenticated()
  next()
})

// allow CORS
const corsOptions = {
  origin: process.env.REACT_SERVER,
  credentials: true,
}
app.use(cors(corsOptions))

// authentication middleware
const authCheckMiddleware = require('./middleware/auth-check')

// tell express which router to use based on endpoint
app.use('/api/v1/admin', authCheckMiddleware, adminRouter)
app.use('/api/v1/challenge', authCheckMiddleware, challengeRouter)
app.use('/api/v1/code', authCheckMiddleware, codeRouter)
app.use('/api/v1/config', authCheckMiddleware, configRouter)
app.use('/api/v1/feedback', authCheckMiddleware, feedbackRouter)
app.use('/api/v1/hint', authCheckMiddleware, hintRouter)
app.use('/api/v1/standing', authCheckMiddleware, standingRouter)
app.use('/api/v1/submission', authCheckMiddleware, submissionRouter)
app.use('/api/v1/user', authCheckMiddleware, userRouter)
app.use('/api/v1/postAuthentication', postAuthenticationRouter)
app.use('/', authenticationRouter)

module.exports = app
 

app.js Файл импортируется в www файл, что завершает настройку внутреннего сервера:

 #!/usr/bin/env node

var app = require('../app');
var debug = require('debug')('backend:server');
var http = require('http');

var port = normalizePort(process.env.PORT || '3334');
app.set('port', port);

var server = http.createServer(app);

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    return val;
  }

  if (port >= 0) {
    return port;
  }

  return false;
}

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe '   port
    : 'Port '   port;

  switch (error.code) {
    case 'EACCES':
      console.error(bind   ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind   ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe '   addr
    : 'port '   addr.port;
  debug('Listening on '   bind);
}
 

Используемая стратегия auth0 импортируется из auth0 файла:

 const Auth0 = require('passport-auth0')
const User = require('mongoose').model('User')

const {
  AUTH0_DOMAIN,
  AUTH0_CLIENT_ID,
  AUTH0_CLIENT_SECRET,
  AUTH0_CALLBACK_URL,
} = process.env

module.exports = new Auth0(
  {
    domain: AUTH0_DOMAIN,
    clientID: AUTH0_CLIENT_ID,
    clientSecret: AUTH0_CLIENT_SECRET,
    callbackURL: `${AUTH0_CALLBACK_URL}/authenticated`,
    scope: 'openid email profile <proprietary-url-1> <proprietary-url-2>',
  },
  // After Auth0 authentication, update user info in MongoDB
  // to ensure it remains sync'd with Auth0's source of truth
  async (accessToken, refreshToken, extraParams, profile, done) => {
    try {
      const userData = {
        auth0Id: profile.id,
        username: profile.nickname,
        displayName: profile.displayName,
        last_login: new Date(),
        roleType: profile._json['<proprietary-url-1>'],
      }
      let user = await User.findOneAndUpdate(
        { auth0Id: profile.id },
        userData,
        {
          new: true,
        },
      )
      if (user) return done(null, user)
      user = await User.create(userData)
      return done(null, user)
    } catch (err) {
      return done(err)
    }
    return done(null, profile)
  },
)
 

I’ve tried commenting the try…catch block out above and inserting a console.log(profile) in its place and received no output.

In app.js the code imported in app.use('/', authenticationRouter) is:

 const express = require('express')
const passport = require('passport')
require('dotenv').config()

const router = express.Router()
const {
  AUTH0_DOMAIN,
  AUTH0_CALLBACK_URL,
  AUTH0_CLIENT_ID,
  REACT_SERVER,
} = process.env

// Pass user authentication off to Auth0
router.get(
  '/login/auth0', 
  passport.authenticate('auth0', {
    scope: "openid email profile"
  }))

// This route gets called once Auth0 has successfully authenticated the user
router.get('/authenticated', (req, res, next) => {
  passport.authenticate('auth0', (passportAuthErr, user) => {
    if (passportAuthErr) return next(passportAuthErr)
    if (!user) return res.redirect(`${REACT_SERVER}/login`)
    return req.logIn(user, (loginErr) => {
      if (loginErr) return next(loginErr)
      const { returnTo } = req.session
      delete req.session.returnTo
      return res.redirect(returnTo || `${REACT_SERVER}/authenticated`)
    })
  })(req, res, next)
})

// Perform session logout and redirect to homepage
router.get('/logout/auth0', (req, res) => {
  req.session.destroy(() => {
    res.redirect(`https://${AUTH0_DOMAIN}/v2/logout?returnTo=${AUTH0_CALLBACK_URL}/deauthenticatedamp;client_id=${AUTH0_CLIENT_ID}`)
  });
});

router.get('/deauthenticated', (req, res) => {
  res.clearCookie('connect.sid')
  res.json({ success: true })
})

module.exports = router
 

На который перенаправляется postAuthenticationRouter.js , когда оно возвращается из Auth0:

 const express = require('express')
const jwt = require('jsonwebtoken')

const router = express.Router()
const { JWT_SECRET } = process.env

// This route gets called by the client once it has been
// redirected to /authenticated locally (after Auth0 authentication)
router.get('/', (req, res) => {
  console.log(req.session)
  if (!req.session?.passport?.user) {
    console.log('error!!')
    return res.status(400).json({
      success: false,
      message: 'Authentication failed!',
    })
  }
  const {
    nickname: username,
    _id: userId,
    role: roleType,
  } = req.session.passport.user
  const payload = {
    sub: req.session.passport.user._id,
  }
  const token = jwt.sign(payload, JWT_SECRET)
  const userData = {
    username,
    userId,
    roleType,
  }
  return res.json({
    success: true,
    message: 'You have successfully logged in.',
    token,
    user: userData,
  })
})

module.exports = router
 

Вывод из объекта console.log(req.session) дает мне объект сеанса, но в нем нет свойства passport. Я добавил вопросительные знаки условной цепочки и console.log('error!!') , чтобы убедиться, что я, по крайней мере, зашел так далеко, что я и есть, потому error!! что регистрируется. Но дальше я ничего не могу сделать.

Итак, если я смогу выяснить, как session добавить объект к req объекту, как только перенаправление вернется из Auth0, я смогу продолжить работу над этим проектом.

Комментарии:

1. Вы пробовали req.user вместо req.session.passport.user ? Это важно, потому что, если вы используете req.session.passport.user , вы, по сути, извлекаете информацию о пользователе из файла cookie сеанса (который может быть устаревшим).

2. Это дает мне значение undefined

Ответ №1:

Оказывается, версия nodejs, которую я использовал на своем Chromebook, не была актуальной. Как только я обновил его, проект работал нормально.