Skip to content

Instantly share code, notes, and snippets.

@mtander
Created May 24, 2024 15:18
Show Gist options
  • Save mtander/ce4c989d4291aced61822e70e1fdd3fa to your computer and use it in GitHub Desktop.
Save mtander/ce4c989d4291aced61822e70e1fdd3fa to your computer and use it in GitHub Desktop.

my HelloModule:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { HelloService } from './hello.service';
import { HelloController } from './hello.controller';
import { AuthGuard } from '../guards/auth.guard';

@Module({
  controllers: [HelloController],
  providers: [
    HelloService,
    {
      provide: APP_GUARD,
      useExisting: AuthGuard,
    },
    AuthGuard,
  ],
})
export class HelloModule {}

my e2e test:

import { INestApplication } from '@nestjs/common';
import { TestingModule, Test } from '@nestjs/testing';
import * as request from 'supertest';
import { AuthGuard } from '../src/guards/auth.guard';
import { AuthGuardMock } from './authguard.mock';
import { HelloModule } from '../src/hello/hello.module';

describe('app e2e via HelloController', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [HelloModule],
    })
      .overrideProvider(AuthGuard)
      .useClass(AuthGuardMock)
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('GET request to /hello returns Hello!', () => {
    try {
      return request(app.getHttpServer())
        .get('/hello')
        .set('authorization', 'Bearer abc123')
        .expect(200)
        .expect('Hello!');
    } catch (err) {
      console.log(err);
      throw err;
    }
  });

  afterAll(async () => {
    await app.close();
  });
});

my AuthGuard class:

import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtRsaVerifier } from 'aws-jwt-verify';
import { validateCognitoJwtFields } from 'aws-jwt-verify/cognito-verifier';
import { getJWTFromRequest } from '../utils/utils';

@Injectable()
export class AuthGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    console.log('canActivate from auth guard');
    const request = context.switchToHttp().getRequest();

    const jwt = getJWTFromRequest(request);

    return await this.validateJwtFields(jwt);
  }

  async validateJwtFields(jwt: string): Promise<boolean> {
    // if (process.env.NODE_ENV === 'test') {
    //   return Promise.resolve(true);
    // }
    console.log('validateJwtFields:');
    const verifier = JwtRsaVerifier.create([
      {
        issuer: `https://cognito-idp.us-east-1.amazonaws.com/${process.env.COGNITO_USER_POOL_ID}`,
        audience: null, // audience (~clientId) is checked instead, by the Cognito specific checks below
        customJwtCheck: ({ payload }) =>
          validateCognitoJwtFields(payload, {
            tokenUse: 'access', // set to "id" or "access" (or null if both are fine)
            clientId: `${process.env.COGNITO_CLIENT_ID}`, // provide the client id, or an array of client ids (or null if you do not want to check client id)
          }),
      },
      //TODO: change below when adding Okta or other IDP
      {
        issuer: 'https://example.com/my/other/idp',
        audience: 'myaudience',
      },
    ]);
    try {
      await verifier.verify(jwt);

      return true;
    } catch (err) {
      console.log(err);
      throw new UnauthorizedException('Invalid authentication token');
    }
  }
}

my authGuardMock:

import { CanActivate, ExecutionContext } from '@nestjs/common';
import { getJWTFromRequest } from '../src/utils/utils';

export class AuthGuardMock implements CanActivate {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  canActivate = jest.fn(async (context: ExecutionContext) => {
    console.log('AuthGuardMock - canActivate');

    const request = context.switchToHttp().getRequest();

    const jwt = getJWTFromRequest(request);

    console.log('AuthGuardMock - after getJWTFromRequest');

    return true;
  });
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  validateJwtFields = jest.fn((jwt: string) => Promise.resolve(true));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment