Update JWT auth extractors, add state in openid redirect, add back cors for api router

This commit is contained in:
advplyr 2023-09-25 17:05:58 -05:00
parent e282142d3f
commit 0d5a30b214
3 changed files with 43 additions and 21 deletions

View File

@ -16,6 +16,18 @@ class Auth {
constructor() { constructor() {
} }
static cors(req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header("Access-Control-Allow-Methods", 'GET, POST, PATCH, PUT, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', '*')
res.header('Access-Control-Allow-Credentials', true)
if (req.method === 'OPTIONS') {
res.sendStatus(200)
} else {
next()
}
}
/** /**
* Inializes all passportjs strategies and other passportjs ralated initialization. * Inializes all passportjs strategies and other passportjs ralated initialization.
*/ */
@ -78,7 +90,7 @@ class Auth {
// Load the JwtStrategy (always) -> for bearer token auth // Load the JwtStrategy (always) -> for bearer token auth
passport.use(new JwtStrategy({ passport.use(new JwtStrategy({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromExtractors([ExtractJwt.fromAuthHeaderAsBearerToken(), ExtractJwt.fromUrlQueryParameter('token')]),
secretOrKey: Database.serverSettings.tokenSecret secretOrKey: Database.serverSettings.tokenSecret
}, this.jwtAuthCheck.bind(this))) }, this.jwtAuthCheck.bind(this)))
@ -123,15 +135,25 @@ class Auth {
httpOnly: true httpOnly: true
}) })
// persist state if passed in
if (req.query.state) {
res.cookie('auth_state', req.query.state, {
maxAge: 120000, // 2 min
httpOnly: true
})
}
const callback = req.query.redirect_uri || req.query.callback
// check if we are missing a callback parameter - we need one if isRest=false // check if we are missing a callback parameter - we need one if isRest=false
if (!req.query.callback) { if (!callback) {
res.status(400).send({ res.status(400).send({
message: 'No callback parameter' message: 'No callback parameter'
}) })
return return
} }
// store the callback url to the auth_cb cookie // store the callback url to the auth_cb cookie
res.cookie('auth_cb', req.query.callback, { res.cookie('auth_cb', callback, {
maxAge: 120000, // 2 min maxAge: 120000, // 2 min
httpOnly: true httpOnly: true
}) })
@ -155,9 +177,10 @@ class Auth {
} else { } else {
// UI request -> check if we have a callback url // UI request -> check if we have a callback url
// TODO: do we want to somehow limit the values for auth_cb? // TODO: do we want to somehow limit the values for auth_cb?
if (req.cookies.auth_cb?.startsWith('http')) { if (req.cookies.auth_cb) {
let stateQuery = req.cookies.auth_state ? `&state=${req.cookies.auth_state}` : ''
// UI request -> redirect to auth_cb url and send the jwt token as parameter // UI request -> redirect to auth_cb url and send the jwt token as parameter
res.redirect(302, `${req.cookies.auth_cb}?setToken=${data_json.user.token}`) res.redirect(302, `${req.cookies.auth_cb}?setToken=${data_json.user.token}${stateQuery}`)
} else { } else {
res.status(400).send('No callback or already expired') res.status(400).send('No callback or already expired')
} }
@ -201,10 +224,9 @@ class Auth {
// openid strategy callback route (this receives the token from the configured openid login provider) // openid strategy callback route (this receives the token from the configured openid login provider)
router.get('/auth/openid/callback', router.get('/auth/openid/callback',
passport.authenticate('openidconnect', { failureRedirect: '/login', failureMessage: true }), passport.authenticate('openidconnect'),
// on a successfull login: read the cookies and react like the client requested (callback or json) // on a successfull login: read the cookies and react like the client requested (callback or json)
this.handleLoginSuccessBasedOnCookie.bind(this) this.handleLoginSuccessBasedOnCookie.bind(this))
)
// Logout route // Logout route
router.post('/logout', (req, res) => { router.post('/logout', (req, res) => {
@ -288,9 +310,9 @@ class Auth {
*/ */
async jwtAuthCheck(jwt_payload, done) { async jwtAuthCheck(jwt_payload, done) {
// load user by id from the jwt token // load user by id from the jwt token
const user = await Database.userModel.getUserById(jwt_payload.id) const user = await Database.userModel.getUserByIdOrOldId(jwt_payload.userId)
if (!user || !user.isActive) { if (!user?.isActive) {
// deny login // deny login
done(null, null) done(null, null)
return return

View File

@ -180,7 +180,7 @@ class Server {
router.use(express.static(Path.join(global.appRoot, 'static'))) router.use(express.static(Path.join(global.appRoot, 'static')))
// router.use('/api/v1', routes) // TODO: New routes // router.use('/api/v1', routes) // TODO: New routes
router.use('/api', this.authMiddleware.bind(this), this.apiRouter.router) router.use('/api', Auth.cors, this.authMiddleware.bind(this), this.apiRouter.router)
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router) router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
// RSS Feed temp route // RSS Feed temp route

View File

@ -6,7 +6,7 @@ class SessionController {
constructor() { } constructor() { }
async findOne(req, res) { async findOne(req, res) {
return res.json(req.session) return res.json(req.playbackSession)
} }
async getAllWithUserData(req, res) { async getAllWithUserData(req, res) {
@ -63,32 +63,32 @@ class SessionController {
} }
async getOpenSession(req, res) { async getOpenSession(req, res) {
const libraryItem = await Database.libraryItemModel.getOldById(req.session.libraryItemId) const libraryItem = await Database.libraryItemModel.getOldById(req.playbackSession.libraryItemId)
const sessionForClient = req.session.toJSONForClient(libraryItem) const sessionForClient = req.playbackSession.toJSONForClient(libraryItem)
res.json(sessionForClient) res.json(sessionForClient)
} }
// POST: api/session/:id/sync // POST: api/session/:id/sync
sync(req, res) { sync(req, res) {
this.playbackSessionManager.syncSessionRequest(req.user, req.session, req.body, res) this.playbackSessionManager.syncSessionRequest(req.user, req.playbackSession, req.body, res)
} }
// POST: api/session/:id/close // POST: api/session/:id/close
close(req, res) { close(req, res) {
let syncData = req.body let syncData = req.body
if (syncData && !Object.keys(syncData).length) syncData = null if (syncData && !Object.keys(syncData).length) syncData = null
this.playbackSessionManager.closeSessionRequest(req.user, req.session, syncData, res) this.playbackSessionManager.closeSessionRequest(req.user, req.playbackSession, syncData, res)
} }
// DELETE: api/session/:id // DELETE: api/session/:id
async delete(req, res) { async delete(req, res) {
// if session is open then remove it // if session is open then remove it
const openSession = this.playbackSessionManager.getSession(req.session.id) const openSession = this.playbackSessionManager.getSession(req.playbackSession.id)
if (openSession) { if (openSession) {
await this.playbackSessionManager.removeSession(req.session.id) await this.playbackSessionManager.removeSession(req.playbackSession.id)
} }
await Database.removePlaybackSession(req.session.id) await Database.removePlaybackSession(req.playbackSession.id)
res.sendStatus(200) res.sendStatus(200)
} }
@ -111,7 +111,7 @@ class SessionController {
return res.sendStatus(404) return res.sendStatus(404)
} }
req.session = playbackSession req.playbackSession = playbackSession
next() next()
} }
@ -130,7 +130,7 @@ class SessionController {
return res.sendStatus(403) return res.sendStatus(403)
} }
req.session = playbackSession req.playbackSession = playbackSession
next() next()
} }
} }