#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, не была актуальной. Как только я обновил его, проект работал нормально.