Skip to content

Instantly share code, notes, and snippets.

@philwilliammee
Last active July 27, 2023 10:48
Show Gist options
  • Save philwilliammee/12ce15c4501b938e8c77c40bd13009e3 to your computer and use it in GitHub Desktop.
Save philwilliammee/12ce15c4501b938e8c77c40bd13009e3 to your computer and use it in GitHub Desktop.
JWT-based email authentication middleware for Express.js
import SimpleEmailService from '../amazon/SimpleEmailService';
import { Express, Request, Response, NextFunction } from 'express';
import { sign, verify } from 'jsonwebtoken';
const simpleEmailService = new SimpleEmailService();
/**
* Middleware function to verify a JWT token.
*/
export async function verifyToken(
req: Request,
res: Response,
next: NextFunction
) {
const { token } = req.body;
const decodedToken = decodeURIComponent(token);
try {
const jwtSecret = process.env.JWT_SECRET || 'secret';
const decoded = verify(decodedToken, jwtSecret) as {
email: string;
};
// Set the email in the request body so it can be used by the next middleware function.
req.body.email = decoded.email;
next(); // Continue to the next middleware function.
} catch (error) {
console.log(error);
return res.status(401).json({
status: 401, // Unauthorized
message: 'Invalid or expired token',
});
}
}
/**
* Middleware function to create a JWT token and send an email with a link to verify the email address.
*/
export function createTokenAndSendEmail(htmlTemplate: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const { email } = req.body;
const jwtSecret = process.env.JWT_SECRET || 'secret';
const expiresIn = process.env.JWT_EXPIRES_IN || '24h';
const token = sign({ email }, jwtSecret, { expiresIn });
const encodedToken = encodeURIComponent(token);
const mailOptions = {
from: 'ssit-devstatus@cornell.edu',
to: email,
subject: 'SSIT Dev Status: Verify your email address',
html: htmlTemplate.replace('${token}', encodedToken),
};
await simpleEmailService.send(mailOptions);
next();
};
}
// Example Middleware Usage.
function requestAuthJwtController(req: Request, res: Response) {
return res.json({
status: 200,
message: 'success',
});
}
function verifyEmailController(req: Request, res: Response) {
// The email was set in the request body by the verifyToken middleware.
const { email } = req.body;
return res.json({
status: 200,
message: 'success',
data: { email },
});
}
export function tokenAuthRoutes(app: Express) {
const tokenAuthUrl = process.env.TOKEN_AUTH_URL || 'https://localhost:4200/home';
const htmlTemplate = `
<p>Click the link below to verify your email address.</p>
<a href="${tokenAuthUrl}?token=\${token}">Verify Email</a>
`;
/**
* Step 1. The client requests a JWT token and an email is sent to the user.
* @todo this should have an async error handler.
*/
app
.route('/api/auth/request-jwt-auth')
.post(createTokenAndSendEmail(htmlTemplate), requestAuthJwtController);
/**
* Step 2. The client clicks the link in the email and the email address is verified.
* Note: The verifyToken middleware verifies the JWT token.
* If the token is valid, the email address is set in the request body and the
* email address is returned in the response.
* If the token is invalid, a 401 response is returned.
*/
app.route('/api/auth/verify-email').post(verifyToken, verifyEmailController);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment