enable log in via either token or through proxy auth

This commit is contained in:
Igor Serebryany 2023-10-06 14:04:31 -07:00
parent eae72ed29a
commit 49c1b8f106
No known key found for this signature in database
GPG key ID: 0AF607692FD9EB24
3 changed files with 108 additions and 28 deletions

View file

@ -1,8 +1,11 @@
const uuidv4 = require("uuid").v4
const bcrypt = require('./libs/bcryptjs') const bcrypt = require('./libs/bcryptjs')
const jwt = require('./libs/jsonwebtoken') const jwt = require('./libs/jsonwebtoken')
const requestIp = require('./libs/requestIp') const requestIp = require('./libs/requestIp')
const Logger = require('./Logger') const Logger = require('./Logger')
const Database = require('./Database') const Database = require('./Database')
const User = require('./objects/user/User')
class Auth { class Auth {
constructor() { } constructor() { }
@ -53,16 +56,46 @@ class Auth {
token = authHeader && authHeader.split(' ')[1] token = authHeader && authHeader.split(' ')[1]
} }
if (token == null) { let proxyUsername = null
Logger.error('Api called without a token', req.path) if (Database.serverSettings.proxyAuthEnabled) {
const uHeader = Database.serverSettings.proxyAuthUsernameHeader.toLowerCase()
proxyUsername = uHeader ? req.headers[uHeader] : null
}
if (token == null && proxyUsername == null) {
Logger.error('Api called without a token and proxy auth is disabled', req.path)
return res.sendStatus(401) return res.sendStatus(401)
} }
const user = await this.verifyToken(token) let user
if (!user) { if (token) {
Logger.error('Verify Token User Not Found', token) user = await this.verifyToken(token)
return res.sendStatus(404) if (!user) {
Logger.error('Verify Token User Not Found', token)
return res.sendStatus(401)
}
user.isFromProxy = false
} else if (proxyUsername) {
const eHeader = Database.serverSettings.proxyAuthEmailHeader.toLowerCase()
const proxyEmail = eHeader ? req.headers[eHeader] : null
user = await this.getProxyUser(proxyUsername, proxyEmail)
if (!user) {
Logger.error('Cannot get user from proxy headers', proxyUsername)
return res.sendStatus(401)
}
user.isFromProxy = true
} }
// if we got here, we should have a valid user in `user`
if (!user) {
Logger.error('Unknown error getting user')
return res.sendStatus(401)
}
// is the user active
if (!user.isActive) { if (!user.isActive) {
Logger.error('Verify Token User is disabled', token, user.username) Logger.error('Verify Token User is disabled', token, user.username)
return res.sendStatus(403) return res.sendStatus(403)
@ -110,6 +143,49 @@ class Auth {
}) })
} }
async getProxyUser(username, email) {
const user = await Database.userModel.getUserByUsername(username)
if (user) {
return user
}
if (!email) {
return null
}
const account = {
username,
email,
type: 'user',
isActive: true,
}
return await this.createUser(account)
}
// create and return a new user from an object with lots of user properties defined
async createUser(account) {
const username = account.username
const usernameExists = await Database.userModel.getUserByUsername(username)
if (usernameExists) {
return null
}
account.id = uuidv4()
const password = account.password || uuidv4()
delete account.password
account.pash = await this.hashPass(password)
account.token = await this.generateAccessToken({ userId: account.id, username })
account.createdAt = Date.now()
const newUser = new User(account)
const success = await Database.createUser(newUser)
return success ? await Database.userModel.getUserById(newUser.id) : null
}
/** /**
* Payload returned to a user after successful login * Payload returned to a user after successful login
* @param {oldUser} user * @param {oldUser} user
@ -187,8 +263,9 @@ class Auth {
}) })
} }
// proxy users can change their password without knowing their old password
const compare = await this.comparePassword(password, matchingUser) const compare = await this.comparePassword(password, matchingUser)
if (!compare) { if (!compare && !req.user.isFromProxy) {
return res.json({ return res.json({
error: 'Invalid password' error: 'Invalid password'
}) })

View file

@ -1,10 +1,7 @@
const uuidv4 = require("uuid").v4
const Logger = require('../Logger') const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority') const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database') const Database = require('../Database')
const User = require('../objects/user/User')
const { toNumber } = require('../utils/index') const { toNumber } = require('../utils/index')
class UserController { class UserController {
@ -97,15 +94,8 @@ class UserController {
return res.status(500).send('Username already taken') return res.status(500).send('Username already taken')
} }
account.id = uuidv4() const newUser = await this.auth.createUser(account)
account.pash = await this.auth.hashPass(account.password) if (newUser) {
delete account.password
account.token = await this.auth.generateAccessToken({ userId: account.id, username })
account.createdAt = Date.now()
const newUser = new User(account)
const success = await Database.createUser(newUser)
if (success) {
SocketAuthority.adminEmitter('user_added', newUser.toJSONForBrowser()) SocketAuthority.adminEmitter('user_added', newUser.toJSONForBrowser())
res.json({ res.json({
user: newUser.toJSONForBrowser() user: newUser.toJSONForBrowser()

View file

@ -51,6 +51,11 @@ class ServerSettings {
this.timeFormat = 'HH:mm' this.timeFormat = 'HH:mm'
this.language = 'en-us' this.language = 'en-us'
// Reverse Proxy
this.proxyAuthEnabled = false
this.proxyAuthUsernameHeader = ''
this.proxyAuthEmailHeader = ''
this.logLevel = Logger.logLevel this.logLevel = Logger.logLevel
this.version = null this.version = null
@ -94,6 +99,11 @@ class ServerSettings {
this.dateFormat = settings.dateFormat || 'MM/dd/yyyy' this.dateFormat = settings.dateFormat || 'MM/dd/yyyy'
this.timeFormat = settings.timeFormat || 'HH:mm' this.timeFormat = settings.timeFormat || 'HH:mm'
this.language = settings.language || 'en-us' this.language = settings.language || 'en-us'
this.proxyAuthEnabled = !!settings.proxyAuthEnabled
this.proxyAuthUsernameHeader = settings.proxyAuthUsernameHeader || ''
this.proxyAuthEmailHeader = settings.proxyAuthEmailHeader || ''
this.logLevel = settings.logLevel || Logger.logLevel this.logLevel = settings.logLevel || Logger.logLevel
this.version = settings.version || null this.version = settings.version || null
@ -151,6 +161,9 @@ class ServerSettings {
sortingIgnorePrefix: this.sortingIgnorePrefix, sortingIgnorePrefix: this.sortingIgnorePrefix,
sortingPrefixes: [...this.sortingPrefixes], sortingPrefixes: [...this.sortingPrefixes],
chromecastEnabled: this.chromecastEnabled, chromecastEnabled: this.chromecastEnabled,
proxyAuthEnabled: this.proxyAuthEnabled,
proxyAuthUsernameHeader: this.proxyAuthUsernameHeader,
proxyAuthEmailHeader: this.proxyAuthEmailHeader,
dateFormat: this.dateFormat, dateFormat: this.dateFormat,
timeFormat: this.timeFormat, timeFormat: this.timeFormat,
language: this.language, language: this.language,