adaptorex
Version:
Connect all your live interactive storytelling devices and software
134 lines (120 loc) • 4.43 kB
JavaScript
/**
* API authentication types
*
* @module authentication
* @copyright Lasse Marburg 2021
* @license MIT
*/
class Auth {
constructor() {
}
/**
* Move socket auth credentials to request object and forward to auth method
*
* @todo rewrite this to use cookie and/or sessionStorage and/or localStorage tokens from client instead of requiring username and password everytime
*
* @param {Object} socket - socket connection object
* @param {function} next - call next middleware
*/
authenticateSocket(socket, next) {
if(socket.handshake.auth) {
socket.request.originalUrl = socket.handshake.url
if (socket.handshake.auth.user && socket.handshake.auth.password){
log.trace(0, "socket auth with user and pw");
let user = socket.handshake.auth.user;
let password = socket.handshake.auth.password;
log.trace(0, "user:password",user, password, socket.request.headers);
socket.request.headers.authorization = "Basic "+ Buffer.from(user + ':' + password).toString('base64')
}
else if (socket.handshake.auth.token){
log.trace(0, "socket auth with token", socket.handshake.auth.token);
socket.request.headers.authorization = "Basic "+ socket.handshake.auth.token;
}
}
this.authenticate(socket.request, {}, next)
}
}
class NoAuth extends Auth{
constructor(default_user) {
super()
this.default_user = default_user
log.info(0, "Authentication disabled")
}
authenticate(req, res, next) {
req.user = this.default_user
next()
}
}
/**
* @param {Object|Array} user - list of authorized users
*/
class BasicAuth extends Auth {
constructor(user) {
super()
if(!Array.isArray(user) || (Array.isArray(user) && !user.length)) {
log.warn(0, "Basic Authentication is enabled but there is no user that is authorized to connect to adaptor.")
this.user = []
} else {
this.user = user
}
log.info(0, "Basic Authentication enabled")
}
/**
* authentication middleware function for express routing.
*
* respond with www-authenticate necessity. Only pass if request header contains valid authentication
*
* parse login and password from headers
* match against users list
*
* Verify if login and password are set and match credentials of an existing user
*
* With help by this Post: https://stackoverflow.com/questions/23616371/basic-http-authentication-with-node-and-express-4/33905671#33905671
*
* User example:
* @example
* "users": [
* {
* "login": "my_user",
* "password": "abcd1234"
* }
* ]
*
* @todo add Set-Cookie with Session Auth or create Session Token or similar
*
* @param {Object} req - request Object
* @param {Object} res - response Object
* @param {function} next - express next function
* @returns {undefined} Returns prematurely if authentication failed and skips next request handlings
*/
authenticate(req, res, next) {
const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')
let auth = {}
if(Array.isArray(this.user)) {
for(let u of this.user) {
if(u.login == login) {
auth = {login: u.login, password: u.password}
break
}
}
} else {
auth = {login: this.user.user, password: this.user.password}
}
if (!login || !password || login !== auth.login || password !== auth.password) {
log.warn(0, `Authentication failed for ${req.method} request to ${req.originalUrl}, user: '${login}' and pw: '${password}'. See trace log for details.`)
log.trace("Request headers", req.headers)
//res.set('WWW-Authenticate', 'Basic realm="401"')
if(typeof res.status === "function") {
res.status(401).send({name:'AuthenticationError',message:'Authentication enabled. See Readme on how to configure authentication for your adaptor server.'})
}
return
}
req.user = auth
next()
}
}
module.exports = {
BasicAuth:BasicAuth,
NoAuth:NoAuth
}