“NestJS verify AWS Cognito JWT: ‘TypeError: applicationRef.isHeadersSent is not a function'”

huangapple go评论65阅读模式
英文:

NestJS verify AWS Cognito JWT: "TypeError: applicationRef.isHeadersSent is not a function"

问题

I am using AWS Cognito as the Authentication Service of my NestJS app. However, when I hit the endpoint without a JWT (not authenticated), the server always crashes and throws this error: TypeError: applicationRef.isHeadersSent is not a function, but it works well when a valid JWT is included, i.e., the correct data is returned by the API Endpoint protected by the auth guard. Below is how I constructed the authentication configuration and auth guard. Can someone please have a look? Thanks in advance!

src/authz/authz.module.ts


import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { passportJwtSecret } from 'jwks-rsa';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      secretOrKeyProvider: passportJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `https://cognito-idp.us-east-1.amazonaws.com/xxxxx/.well-known/jwks.json`,
      }),
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      issuer: 'https://cognito-idp.us-east-1.amazonaws.com/xxxxx',
      algorithms: ['RS256'],
    });
  }

  validate(payload: unknown): unknown {
    return payload;
  }
}

src/authz/jwt.strategy.ts


import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [PassportModule.register({ defaultStrategy: 'jwt' })],
  providers: [JwtStrategy],
  exports: [PassportModule],
})
export class AuthzModule {}

src/app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { MomentModule } from './moment/moment.module';
import { graphqlConfigOptions } from './config/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeormConfigOptions } from './config/data-source';
import { CharacterModule } from './character/character.module';
import { AuthzModule } from './authz/authz.module';

@Module({
  imports: [
    GraphQLModule.forRoot(graphqlConfigOptions),
    TypeOrmModule.forRoot(typeormConfigOptions),
    MomentModule,
    CharacterModule,
    AuthzModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

src/app.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { ApiBearerAuth, ApiResponse, ApiTags } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';

@ApiTags('System')
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('health')
  @ApiResponse({ status: 200, type: String })
  getHello(): string {
    return this.appService.getHello();
  }

  @UseGuards(AuthGuard('jwt'))
  @Get('secure-message')
  @ApiResponse({ status: 200, type: String })
  @ApiResponse({ status: 401 })
  @ApiBearerAuth()
  getSecureMessage(): string {
    return this.appService.getSecureMessage();
  }
}

package.json

{
  "name": "moment-share-service",
  "version": "0.0.1",
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "typeorm": "nest build && npx typeorm -d dist/src/config/data-source.js",
    "typeorm:migration:generate": "npm run typeorm -- migration:generate",
    "migration:run": "npm run typeorm -- migration:run",
    "migration:generate": "npm run typeorm:migration:generate -- migrations/migration"
  },
  "dependencies": {
    "@apollo/server": "^4.7.0",
    "@nestjs/apollo": "^11.0.5",
    "@nestjs/common": "^9.4.0",
    "@nestjs/config": "^2.3.1",
    "@nestjs/core": "^9.4.0",
    "@nestjs/graphql": "^11.0.5",
    "@nestjs/mapped-types": "*",
    "@nestjs/passport": "^9.0.3",
    "@nestjs/platform-express": "^8.0.0",
    "@nestjs/swagger": "^6.3.0",
    "@nestjs/typeorm": "^9.0.1",
    "dotenv": "^16.0.3",
    "graphql": "^16.6.0",
    "jwks-rsa": "^3.0.1",
    "passport": "^0.6.0",
    "passport-jwt": "^4.0.1",
    "pg": "^8.10.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0",
    "swagger-ui-express": "^4.6.2",
    "ts-morph": "^18.0.0",
    "typeorm": "^0.3.15"
  },
  "devDependencies": {
    "@nestjs/cli": "^8.0.0",
    "@nestjs/schematics": "^8.0.0",
    "@nestjs/testing": "^8.0.0",
    "@types/express": "^4.17.13",
    "@types/jest": "27.4.1",
    "@types/node": "^16.0.0",
    "@types/supertest": "^2.0.11",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "eslint": "^8.0.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
   

<details>
<summary>英文:</summary>

I am using AWS Cognito as the Authentication Service of my NestJS app. However, when I hit the endpoint without a JWT (not authenticated), the server always crashes and throws this error: `TypeError: applicationRef.isHeadersSent is not a function`, but it works well when a valid JWT is included, i.e., the correct data is returned by the API Endpoint protected by the auth guard. Below is how I constructed the authentication configuration and auth guard. Can someone please have a look? Thanks in advance!


*src/authz/authz.module.ts*
``` js

import { Injectable } from &#39;@nestjs/common&#39;;
import { PassportStrategy } from &#39;@nestjs/passport&#39;;
import { ExtractJwt, Strategy } from &#39;passport-jwt&#39;;
import { passportJwtSecret } from &#39;jwks-rsa&#39;;

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      secretOrKeyProvider: passportJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `https://cognito-idp.us-east-1.amazonaws.com/xxxxx/.well-known/jwks.json`,
      }),
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      issuer: &#39;https://cognito-idp.us-east-1.amazonaws.com/xxxxx&#39;,
      algorithms: [&#39;RS256&#39;],
    });
  }

  validate(payload: unknown): unknown {
    return payload;
  }
}

src/authz/jwt.strategy.ts


import { Module } from &#39;@nestjs/common&#39;;
import { PassportModule } from &#39;@nestjs/passport&#39;;
import { JwtStrategy } from &#39;./jwt.strategy&#39;;

@Module({
  imports: [PassportModule.register({ defaultStrategy: &#39;jwt&#39; })],
  providers: [JwtStrategy],
  exports: [PassportModule],
})
export class AuthzModule {}

src/app.module.ts

import { Module } from &#39;@nestjs/common&#39;;
import { AppController } from &#39;./app.controller&#39;;
import { AppService } from &#39;./app.service&#39;;
import { GraphQLModule } from &#39;@nestjs/graphql&#39;;
import { MomentModule } from &#39;./moment/moment.module&#39;;
import { graphqlConfigOptions } from &#39;./config/graphql&#39;;
import { TypeOrmModule } from &#39;@nestjs/typeorm&#39;;
import { typeormConfigOptions } from &#39;./config/data-source&#39;;
import { CharacterModule } from &#39;./character/character.module&#39;;
import { AuthzModule } from &#39;./authz/authz.module&#39;;

@Module({
  imports: [
    GraphQLModule.forRoot(graphqlConfigOptions),
    TypeOrmModule.forRoot(typeormConfigOptions),
    MomentModule,
    CharacterModule,
    AuthzModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

src/app.controller.ts

import { Controller, Get, UseGuards } from &#39;@nestjs/common&#39;;
import { AppService } from &#39;./app.service&#39;;
import { ApiBearerAuth, ApiResponse, ApiTags } from &#39;@nestjs/swagger&#39;;
import { AuthGuard } from &#39;@nestjs/passport&#39;;

@ApiTags(&#39;System&#39;)
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get(&#39;health&#39;)
  @ApiResponse({ status: 200, type: String })
  getHello(): string {
    return this.appService.getHello();
  }

  @UseGuards(AuthGuard(&#39;jwt&#39;))
  @Get(&#39;secure-message&#39;)
  @ApiResponse({ status: 200, type: String })
  @ApiResponse({ status: 401 })
  @ApiBearerAuth()
  getSecureMessage(): string {
    return this.appService.getSecureMessage();
  }
}

package.json

{
  &quot;name&quot;: &quot;moment-share-service&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;author&quot;: &quot;&quot;,
  &quot;private&quot;: true,
  &quot;license&quot;: &quot;UNLICENSED&quot;,
  &quot;scripts&quot;: {
    &quot;prebuild&quot;: &quot;rimraf dist&quot;,
    &quot;build&quot;: &quot;nest build&quot;,
    &quot;format&quot;: &quot;prettier --write \&quot;src/**/*.ts\&quot; \&quot;test/**/*.ts\&quot;&quot;,
    &quot;start&quot;: &quot;nest start&quot;,
    &quot;start:dev&quot;: &quot;nest start --watch&quot;,
    &quot;start:debug&quot;: &quot;nest start --debug --watch&quot;,
    &quot;start:prod&quot;: &quot;node dist/main&quot;,
    &quot;lint&quot;: &quot;eslint \&quot;{src,apps,libs,test}/**/*.ts\&quot; --fix&quot;,
    &quot;test&quot;: &quot;jest&quot;,
    &quot;test:watch&quot;: &quot;jest --watch&quot;,
    &quot;test:cov&quot;: &quot;jest --coverage&quot;,
    &quot;test:debug&quot;: &quot;node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand&quot;,
    &quot;test:e2e&quot;: &quot;jest --config ./test/jest-e2e.json&quot;,
    &quot;typeorm&quot;: &quot;nest build &amp;&amp; npx typeorm -d dist/src/config/data-source.js&quot;,
    &quot;typeorm:migration:generate&quot;: &quot;npm run typeorm -- migration:generate&quot;,
    &quot;migration:run&quot;: &quot;npm run typeorm -- migration:run&quot;,
    &quot;migration:generate&quot;: &quot;npm run typeorm:migration:generate -- migrations/migration&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@apollo/server&quot;: &quot;^4.7.0&quot;,
    &quot;@nestjs/apollo&quot;: &quot;^11.0.5&quot;,
    &quot;@nestjs/common&quot;: &quot;^9.4.0&quot;,
    &quot;@nestjs/config&quot;: &quot;^2.3.1&quot;,
    &quot;@nestjs/core&quot;: &quot;^9.4.0&quot;,
    &quot;@nestjs/graphql&quot;: &quot;^11.0.5&quot;,
    &quot;@nestjs/mapped-types&quot;: &quot;*&quot;,
    &quot;@nestjs/passport&quot;: &quot;^9.0.3&quot;,
    &quot;@nestjs/platform-express&quot;: &quot;^8.0.0&quot;,
    &quot;@nestjs/swagger&quot;: &quot;^6.3.0&quot;,
    &quot;@nestjs/typeorm&quot;: &quot;^9.0.1&quot;,
    &quot;dotenv&quot;: &quot;^16.0.3&quot;,
    &quot;graphql&quot;: &quot;^16.6.0&quot;,
    &quot;jwks-rsa&quot;: &quot;^3.0.1&quot;,
    &quot;passport&quot;: &quot;^0.6.0&quot;,
    &quot;passport-jwt&quot;: &quot;^4.0.1&quot;,
    &quot;pg&quot;: &quot;^8.10.0&quot;,
    &quot;reflect-metadata&quot;: &quot;^0.1.13&quot;,
    &quot;rimraf&quot;: &quot;^3.0.2&quot;,
    &quot;rxjs&quot;: &quot;^7.2.0&quot;,
    &quot;swagger-ui-express&quot;: &quot;^4.6.2&quot;,
    &quot;ts-morph&quot;: &quot;^18.0.0&quot;,
    &quot;typeorm&quot;: &quot;^0.3.15&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@nestjs/cli&quot;: &quot;^8.0.0&quot;,
    &quot;@nestjs/schematics&quot;: &quot;^8.0.0&quot;,
    &quot;@nestjs/testing&quot;: &quot;^8.0.0&quot;,
    &quot;@types/express&quot;: &quot;^4.17.13&quot;,
    &quot;@types/jest&quot;: &quot;27.4.1&quot;,
    &quot;@types/node&quot;: &quot;^16.0.0&quot;,
    &quot;@types/supertest&quot;: &quot;^2.0.11&quot;,
    &quot;@typescript-eslint/eslint-plugin&quot;: &quot;^5.0.0&quot;,
    &quot;@typescript-eslint/parser&quot;: &quot;^5.0.0&quot;,
    &quot;eslint&quot;: &quot;^8.0.1&quot;,
    &quot;eslint-config-prettier&quot;: &quot;^8.3.0&quot;,
    &quot;eslint-plugin-prettier&quot;: &quot;^4.0.0&quot;,
    &quot;jest&quot;: &quot;^27.2.5&quot;,
    &quot;prettier&quot;: &quot;^2.3.2&quot;,
    &quot;source-map-support&quot;: &quot;^0.5.20&quot;,
    &quot;supertest&quot;: &quot;^6.1.3&quot;,
    &quot;ts-jest&quot;: &quot;^27.0.3&quot;,
    &quot;ts-loader&quot;: &quot;^9.2.3&quot;,
    &quot;ts-node&quot;: &quot;^10.0.0&quot;,
    &quot;tsconfig-paths&quot;: &quot;^3.10.1&quot;,
    &quot;typescript&quot;: &quot;^4.3.5&quot;
  },
  &quot;jest&quot;: {
    &quot;moduleFileExtensions&quot;: [
      &quot;js&quot;,
      &quot;json&quot;,
      &quot;ts&quot;
    ],
    &quot;rootDir&quot;: &quot;src&quot;,
    &quot;testRegex&quot;: &quot;.*\\.spec\\.ts$&quot;,
    &quot;transform&quot;: {
      &quot;^.+\\.(t|j)s$&quot;: &quot;ts-jest&quot;
    },
    &quot;collectCoverageFrom&quot;: [
      &quot;**/*.(t|j)s&quot;
    ],
    &quot;coverageDirectory&quot;: &quot;../coverage&quot;,
    &quot;testEnvironment&quot;: &quot;node&quot;
  }
}

Stack Trace

        if (!applicationRef.isHeadersSent(response)) {
^
TypeError: applicationRef.isHeadersSent is not a function
at ExceptionsHandler.catch (D:\self-study\GitHub Repo\Social Media App Suite\social-media-app-suite\moment-share-service\node_modules\@nestjs\core\exceptions\base-exception-filter.js:27:29)
at ExceptionsHandler.next (D:\self-study\GitHub Repo\Social Media App Suite\social-media-app-suite\moment-share-service\node_modules\@nestjs\core\exceptions\exceptions-handler.js:16:20)
at D:\self-study\GitHub Repo\Social Media App Suite\social-media-app-suite\moment-share-service\node_modules\@nestjs\core\router\router-proxy.js:13:35
at processTicksAndRejections (node:internal/process/task_queues:95:5)

result of npx nest info

[System Information]
OS Version     : Windows 10
NodeJS Version : v18.14.1
YARN Version    : 1.22.17 
[Nest CLI]
Nest CLI Version : 8.2.8 
[Nest Platform Information]
platform-express version : 8.4.7
mapped-types version     : 1.2.2
schematics version       : 8.0.11
passport version         : 9.0.3
graphql version          : 11.0.5
swagger version          : 6.3.0
typeorm version          : 9.0.1
testing version          : 8.4.7
apollo version           : 11.0.5
common version           : 9.4.0
config version           : 2.3.1
core version             : 9.4.0
cli version              : 8.2.8

答案1

得分: 4

请确保所有与 NestJS 相关的包都使用相同的主要版本;例如,以下组合会触发相同的问题:

"@nestjs/core": "^9.4.2"`
"@nestjs/platform-express": "^8.0.0",

在您的情况下,要么使用 9.*.*,要么使用 8.*.*

[Nest CLI]
Nest CLI 版本 : 8.2.8 
[Nest 平台信息]
platform-express 版本 : 8.4.7  <----- 8.*.*
mapped-types 版本 : 1.2.2
schematics 版本 : 8.0.11
passport 版本 : 9.0.3
graphql 版本 : 11.0.5
swagger 版本 : 6.3.0
typeorm 版本 : 9.0.1
testing 版本 : 8.4.7
apollo 版本 : 11.0.5
common 版本 : 9.4.0  <----- 9.*.*
config 版本 : 2.3.1
core 版本 : 9.4.0  <----- 9.*.*
cli 版本 : 8.2.8
英文:

Make sure that all NestJS related packages are using the same major versions; for ex. this combination triggers the same issue for me:

&quot;@nestjs/core&quot;: &quot;^9.4.2&quot;`
&quot;@nestjs/platform-express&quot;: &quot;^8.0.0&quot;,

In your case, either use 9.*.* or 8.*.*:

[Nest CLI]
Nest CLI Version : 8.2.8 
[Nest Platform Information]
platform-express version : 8.4.7  &lt;----- 8.*.*
mapped-types version     : 1.2.2
schematics version       : 8.0.11
passport version         : 9.0.3
graphql version          : 11.0.5
swagger version          : 6.3.0
typeorm version          : 9.0.1
testing version          : 8.4.7
apollo version           : 11.0.5
common version           : 9.4.0  &lt;----- 9.*.*
config version           : 2.3.1
core version             : 9.4.0  &lt;----- 9.*.*
cli version              : 8.2.8

huangapple
  • 本文由 发表于 2023年5月6日 15:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76187640.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定