6245d1eabd
bcrypt with a fixed salt is deterministic but defeats the purpose of bcrypt salting. API keys are high-entropy UUIDs (128 bits), making brute-force resistance unnecessary — HMAC-SHA256 with the app secret is the correct primitive for deterministic, secure key hashing. Also fixes application.author.id → application.authorId in the API key auth path, which was silently setting req.payload.id to undefined.
81 lines
3.0 KiB
JavaScript
81 lines
3.0 KiB
JavaScript
|
|
//var jwt = require('express-jwt');
|
|
var { expressjwt: jwt } = require("express-jwt"),
|
|
secret = require('../config').secret,
|
|
crypto = require('crypto');
|
|
const { v4 } = require('uuid'),
|
|
passport = require('passport'),
|
|
auths = ['Api-Key', 'Basic', 'Bearer', 'Token'];
|
|
|
|
function getTokenFromHeader(req) {
|
|
if(req.headers.authorization && auths.indexOf(req.headers.authorization.split(' ')[0]) !== -1) {
|
|
return req.headers.authorization.split(' ')[1];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const auth = {
|
|
requiredJwt: jwt({
|
|
secret: secret,
|
|
algorithms: ['HS256'],
|
|
requestProperty: 'payload',
|
|
getToken: getTokenFromHeader
|
|
}),
|
|
optional: jwt({
|
|
secret: secret,
|
|
algorithms: ['HS256'],
|
|
requestProperty: 'payload',
|
|
credentialsRequired: false,
|
|
getToken: getTokenFromHeader
|
|
}),
|
|
required: async (req, res, next) => {
|
|
if(req.headers.authorization && req.headers.authorization.split(' ')[0] === process.env.AUTHORIZATION_PREFIX) {
|
|
passport.authenticate('headerapikey', { session: false }, function (err, application, info) {
|
|
if (err) {
|
|
return next({message: "An authentication error occured.", err: err.message});
|
|
}
|
|
if (!application) {
|
|
return res.status(401).json({message: "Unauthorized - You are not allowed to access this resource.", err: err, info: info});
|
|
}
|
|
req.application = application;
|
|
req.payload = { id: application.authorId };
|
|
return next();
|
|
})(req, res, next);
|
|
} else {
|
|
return jwt({
|
|
secret: secret,
|
|
algorithms: ['HS256'],
|
|
requestProperty: 'payload',
|
|
getToken: getTokenFromHeader
|
|
})(req, res, next);
|
|
}
|
|
},
|
|
getAuthType: (req) => {
|
|
if(req.headers.authorization && auths.indexOf(req.headers.authorization.split(' ')[0]) !== -1) {
|
|
let value = req.headers.authorization.split(' ')[0];
|
|
return value;
|
|
}
|
|
return null;
|
|
},
|
|
generateAPIKey: () => {
|
|
const key = v4().replace(/-/g, '');
|
|
const maskedKey = `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
const encryptedKey = crypto.createHmac('sha256', secret).update(key).digest('hex');
|
|
return { key, masked: maskedKey, encrypted: encryptedKey };
|
|
},
|
|
authenticateKey: async (req, res, next) => {
|
|
passport.authenticate('headerapikey', { session: false }, function (err, application, info) {
|
|
if (err) {
|
|
return res.status(err.status || 500).json({message: "An authentication error occured (authenticateKey).", err: err.message});
|
|
}
|
|
if (!application) {
|
|
return res.status(401).json({message: "Unauthorized - You are not allowed to access this resource.", err: err, info: info});
|
|
}
|
|
req.application = application;
|
|
return next();
|
|
})(req, res, next);
|
|
}
|
|
};
|
|
|
|
module.exports = auth;
|