vzakharchenko/keycloak-lambda-authorizer

View on GitHub
src/adapters/MiddlewareAdapter.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {decode} from 'jsonwebtoken';

import {AdapterContent, EnforcerFunction, RequestContent} from '../Options';
import {decodeToken} from '../utils/TokenUtils';

type MiddlewareFunction = (request:any, response:any, next:any)=>Promise<void>

export interface MiddlewareAdapter {
    middleware(enforcer?:EnforcerFunction):MiddlewareFunction
}

export class DefaultMiddlewareAdapter implements MiddlewareAdapter {
  constructor(options: AdapterContent) {
    this.options = options;
  }

  options:AdapterContent;

  jwksRoute = new RegExp('(^)(\\/|)(/service/jwks)(/$|(\\?|$))', 'g');


  isJwksRoute(req:any):boolean {
    return (req.baseUrl || req.originalUrl).match(this.jwksRoute);
  }

  getTokenString(req:any) {
    const tokenString = req.headers.authorization;
    if (!tokenString) {
      throw new Error('Expected \'headers.authorization\' parameter to be set');
    }
    const match = tokenString.match(/^Bearer (.*)$/i);
    if (!match || match.length < 2) {
      throw new Error(`Invalid Authorization token - '${tokenString}' does not match 'Bearer .*'`);
    }
    req.jwt = {token: match[1], payload: decode(match[1])};
    return match[1];
  }

  middleware(enforcer?:EnforcerFunction): MiddlewareFunction {
    const {securityAdapter} = this.options;
    return async (request:any, response:any, next:any) => {
      if (this.options.keys && this.options.keys.publicKey && this.isJwksRoute(request)) {
        response.json(await this.options.jwks.json(this.options.keys.publicKey));
        return;
      }
      try {
        const tokenString = this.getTokenString(request);
        const requestContent:RequestContent = {
          tokenString,
          token: decodeToken(tokenString),
          request,
        };
        await securityAdapter
          .validate(requestContent, enforcer);
        const {serviceAccount} = this.options;
        request.serviceAccountJWT = async () => await serviceAccount
            .getServiceAccountToken(requestContent);
        next();
      } catch (e) {
        this.options.logger.log(`Authorization error ${e}`);
        response.status(403).end();
      }
    };
  }
}