const jwt = require('express-jwt').expressjwt;
const config = require('../../config');
const EncryptDecrypt = require('../../config/crypto.js');
/**
* We are assuming that the JWT will come in a header with the form
*
* Authorization: Bearer ${JWT}
*
* But it could come in a query parameter with the name that you want like
* GET https://my-bulletproof-api.com/stats?apiKey=${JWT}
* Luckily this API follow _common sense_ ergo a _good design_ and don't allow that ugly stuff
*/
const getTokenFromHeader = async req => {
/**
* @TODO Edge and Internet Explorer do some weird things with the headers
* So I believe that this should handle more 'edge' cases ;)
*/
if (
(req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Token') ||
(req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer')
) {
return await EncryptDecrypt.CareerDecrypt(req.headers.authorization.split(' ')[1]);
}
return null;
};
const isAuth = jwt({
secret: process.env.JWT_SECRET, // The _secret_ to sign the JWTs
algorithms: ['HS256'], // JWT Algorithm
requestProperty: 'token', // Use req.token to store the JWT
getToken: getTokenFromHeader, // How to extract the JWT from the request
});
module.exports = isAuth;
EXPLIANATION
Code Breakdown
JWT Middleware Initialization:
javascript
Copy code
const jwt = require('express-jwt').expressjwt;
Uses express-jwt to validate incoming requests by checking the JWT in the headers.
This middleware decodes and verifies the token based on the secret and algorithm specified.
Custom Token Extraction Logic:
javascript
Copy code
const getTokenFromHeader = async req => {
if (
(req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Token') ||
(req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer')
) {
return await EncryptDecrypt.CareerDecrypt(req.headers.authorization.split(' ')[1]);
}
return null;
};
Purpose: Extracts the token from the Authorization header.
Details:
Supports tokens prefixed with Bearer or Token.
Uses EncryptDecrypt.CareerDecrypt to decrypt the token, assuming it's encrypted.
Edge Cases:
No Authorization header → returns null.
Handles multiple token formats (Bearer, Token).
JWT Middleware Configuration:
javascript
Copy code
const isAuth = jwt({
secret: process.env.JWT_SECRET, // Secret key for JWT verification
algorithms: ['HS256'], // Supported JWT signing algorithms
requestProperty: 'token', // Places decoded JWT on req.token
getToken: getTokenFromHeader, // Custom token extraction logic
});
Validates the token using HS256 and a secret key from the environment.
Stores the verified JWT payload in req.token.
Exporting Middleware:
javascript
Copy code
module.exports = isAuth;
Allows the middleware to be imported and used in route handlers.
Strengths
Custom Token Handling:
Supports encrypted tokens with EncryptDecrypt.CareerDecrypt.
Adapts to various header formats (Bearer or Token).
Secure JWT Validation:
Uses a secret key from environment variables for security.
Specifies a strong hashing algorithm (HS256).
Extendable Middleware:
Decoded token payload is available via req.token, making it easy to integrate with subsequent middleware or route handlers.
Potential Improvements
Error Handling:
If EncryptDecrypt.CareerDecrypt fails, it should log the error and return null to avoid crashing the app.
javascript
Copy code
try {
return await EncryptDecrypt.CareerDecrypt(req.headers.authorization.split(' ')[1]);
} catch (error) {
console.error('Token decryption failed:', error);
return null;
}
Validation for Authorization Header:
Add a more robust check for the presence of a valid Authorization header to avoid splitting undefined values.
javascript
Copy code
if (req.headers.authorization) {
const parts = req.headers.authorization.split(' ');
if ((parts[0] === 'Token' || parts[0] === 'Bearer') && parts[1]) {
return await EncryptDecrypt.CareerDecrypt(parts[1]);
}
}
return null;
Security Best Practices:
Validate the decrypted token before passing it to the jwt middleware.
Use rate limiting and IP whitelisting to minimize abuse.
Environment Configuration:
Ensure process.env.JWT_SECRET is securely managed (e.g., in a .env file).
Usage Example
Here's how you might use the isAuth middleware in a route:
javascript
Copy code
const express = require('express');
const isAuth = require('./middleware/auth');
const app = express();
app.get('/protected-route', isAuth, (req, res) => {
res.json({
message: 'You are authenticated!',
user: req.token, // Contains the decoded JWT payload
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Testing Suggestions
Valid Token:
Test with a valid encrypted JWT in the Authorization header.
Confirm that req.token contains the expected decoded payload.
Invalid Token:
Test with an invalid or tampered token and verify that the middleware returns a 401 error.
No Authorization Header:
Ensure the middleware gracefully handles requests without an Authorization header.
Decryption Errors:
Simulate a failure in EncryptDecrypt.CareerDecrypt to confirm that errors are logged without crashing the app.
Top comments (0)