DEV Community

KISHAN RAMLAKHAN NISHAD
KISHAN RAMLAKHAN NISHAD

Posted on

A middleware function for authenticating requests using JSON Web Tokens (JWTs) in an Express application

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;

Enter fullscreen mode Exit fullscreen mode

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)