UNPKG

@blenz/middy-authorizer-middleware

Version:

JWT token validation

109 lines (93 loc) 4.39 kB
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;