@blenz/middy-authorizer-middleware
Version:
JWT token validation
109 lines (93 loc) • 4.39 kB
text/typescript
import createError from 'http-errors';
import jwt, { TokenExpiredError, NotBeforeError, VerifyOptions } from 'jsonwebtoken';
import middy from '@middy/core';
import jwksRsa from 'jwks-rsa';
import { AuthMiddlewareConfig } from './auth.config';
import { AuthEvent } from './auth.response';
import authConfig from '../auth.config.json';
const authClient = jwksRsa({
cache: true,
rateLimit: true,
jwksUri: `https://${authConfig.domain}/.well-known/jwks.json`,
});
/**
* Middy middleware for token authentication
*
* ```
* handler.use(AuthorizeMiddleware({...}))
* ```
*/
export const AuthMiddleware = (config: AuthMiddlewareConfig) => {
return ({
before: async (handler: any) => {
// Authorization not required
if (!config.credentialsRequired) {
return;
}
// Get token from auth header and verify it exists
const authHeader = handler.event.headers.authorization;
if (!authHeader) {
throw new createError.Forbidden('Missing authorization header');
}
// Validate auth header format
const authHeaderParts = authHeader.split(' ');
if (authHeaderParts.length !== 2 || authHeaderParts[0] !== 'Bearer') {
throw new createError.Forbidden('Authorization header malformed - expected `Bearer token`');
}
// Get the token
const authToken = authHeaderParts[1];
if (authToken === undefined) {
throw new createError.Forbidden('Authorization token undefined');
}
// Decode the token
let decodedToken: any;
try {
decodedToken = jwt.decode(authToken, { complete: true });
} catch (err) {
throw new createError.Forbidden(err.message);
}
// Validate token is not malformed
if (!decodedToken || !decodedToken.header || !decodedToken.header.kid) {
throw new createError.Forbidden('JWT token is malformed');
}
const kid = decodedToken.header.kid;
authClient.getSigningKey(kid, (err, key) => {
if (err) {
throw new createError.Forbidden(err.message);
}
const jwtOptions: VerifyOptions = {
algorithms: ['RS256'],
audience: authConfig.audience,
issuer: `https://${authConfig.domain}`
};
try {
jwt.verify(authToken, key.getPublicKey(), jwtOptions, (err, decoded: any) => {
if (err) {
throw new createError.Forbidden(err.message);
}
// TODO: Don't use auth0 roles, instead design our own role structure
if (config.acceptedRoles) {
const roles: string[] = decoded[`https://${process.env.URL}/roles`];
if (!roles || !roles.length || !config.acceptedRoles.some(x => roles.includes(x))) {
throw new createError.Forbidden('User is not permitted to access the requested resource');
}
}
// Attach decoded token to auth event
handler.event.auth = { payload: decoded, token: authToken};
console.log("SDFSDZFF");
console.log(handler.event.auth);
});
} catch (err) {
if (err instanceof TokenExpiredError) {
throw new createError.Forbidden(`Token expired: ${new Date(err.expiredAt).toUTCString()}`);
} else if (err instanceof NotBeforeError) {
throw new createError.Forbidden(`Token not valid until: ${err.date.toUTCString()}`);
} else {
throw new createError.Forbidden('Invalid token');
}
}
});
},
});
}
export default AuthMiddleware;