Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: Unknown authentication strategy "jwt" for Project with multi apps projects #13282

Closed
3 of 15 tasks
thedarkknight197 opened this issue Mar 1, 2024 · 11 comments
Closed
3 of 15 tasks
Labels
needs triage This issue has not been looked into

Comments

@thedarkknight197
Copy link

thedarkknight197 commented Mar 1, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Hello i have creared a nest projects and registered some apps now my folders look like:

apps
|-app-1
  |--src
|-app-2
  |--src
|-.env
|-nest-cli.json
|-package.json
-tsconfig.json

the passport and jwt strategy at this time is used only by one app. so i installed globally the dependencies

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    PostsModule,
    PrismaModule,
    AuthModule,
    UsersModule,
  ],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TenantGuardMiddleware).forRoutes('*'); // Applica il middleware della guardia a tutte le rotte
  }
}

auth module:

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import * as process from 'process';
import { PrismaModule } from '../prisma/prisma.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { UsersService } from '../users/users.service';

const jwtSecret = process.env.ACCESS_TOKEN_SECRET;

@Module({
  imports: [
    PrismaModule,
    PassportModule,
    JwtModule.register({
      secret: jwtSecret,
      signOptions: { expiresIn: '5m' }, // e.g. 30s, 7d, 24h
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy, UsersService],
})
export class AuthModule {}

user module:

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { PrismaModule } from '../prisma/prisma.module';
import { JwtService } from '@nestjs/jwt';

@Module({
  imports: [PrismaModule],
  controllers: [UsersController],
  providers: [UsersService, JwtService],
})
export class UsersModule {}

jwt strategy:

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { map } from 'rxjs';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor(
    private userService: UsersService,
    private readonly configService: ConfigService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get<string>('ACCESS_TOKEN_SECRET'),
    });
  }

  async validate(payload: { sub: number }) {
    return await this.userService
      .findOne(payload.sub)
      .pipe(
        map((user) => {
          if (!user) throw new UnauthorizedException();
          return user;
        }),
      )
      .toPromise();
  }
}

jwt auth guard

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

Minimum reproduction code

not have

Steps to reproduce

No response

Expected behavior

creating a normal project with this import structure no error show. what is wrong in my previus configuration?
app module:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { TokenService } from './token/token.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    AuthModule,
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService, TokenService],
})
export class AppModule {}

auth module:

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import * as process from 'process';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { UsersService } from '../users/users.service';

const jwtSecret = process.env.ACCESS_TOKEN_SECRET;

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: jwtSecret,
      signOptions: { expiresIn: '5m' }, // e.g. 30s, 7d, 24h
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy, UsersService],
})
export class AuthModule {}

user module:

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { JwtService } from '@nestjs/jwt';

@Module({
  imports: [],
  controllers: [UsersController],
  providers: [UsersService, JwtService],
})
export class UsersModule {}

jwt strategy (is only a test)

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor(
    private userService: UsersService,
    private readonly configService: ConfigService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get<string>('ACCESS_TOKEN_SECRET'),
    });
  }

  async validate(payload: { sub: number }) {
    return {};
  }
}

jwt auth guard

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

Package

  • I don't know. Or some 3rd-party package
  • @nestjs/common
  • @nestjs/core
  • @nestjs/microservices
  • @nestjs/platform-express
  • @nestjs/platform-fastify
  • @nestjs/platform-socket.io
  • @nestjs/platform-ws
  • @nestjs/testing
  • @nestjs/websockets
  • Other (see below)

Other package

@nestjs/jwt

NestJS version

10.2.0

Packages versions


 _   _             _      ___  _____  _____  _     _____
| \ | |           | |    |_  |/  ___|/  __ \| |   |_   _|
|  \| |  ___  ___ | |_     | |\ `--. | /  \/| |     | |
| . ` | / _ \/ __|| __|    | | `--. \| |    | |     | |
| |\  ||  __/\__ \| |_ /\__/ //\__/ /| \__/\| |_____| |_
\_| \_/ \___||___/ \__|\____/ \____/  \____/\_____/\___/


[System Information]
OS Version     : Windows 10.0.19045
NodeJS Version : v20.11.0
NPM Version    : 10.2.4 

[Nest CLI]
Nest CLI Version : 10.3.2

[Nest Platform Information]
platform-express version : 10.3.3
microservices version    : 10.3.3
schematics version       : 10.1.1
passport version         : 10.0.3
swagger version          : 7.3.0
testing version          : 10.3.3
common version           : 10.3.3
config version           : 3.2.0
core version             : 10.3.3
jwt version              : 10.2.0
cli version              : 10.3.2

Node.js version

20.11.0

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

if could help:
I readed: #1868
and https://stackoverflow.com/questions/60405308/nestjs-passport-jwt-unknown-strategy

@thedarkknight197 thedarkknight197 added the needs triage This issue has not been looked into label Mar 1, 2024
@thedarkknight197
Copy link
Author

Any help here?

@micalevisk
Copy link
Member

Please provide a minimum reproduction repository. You can start one by running npm init nest in your terminal

why reproductions are required

@thedarkknight197
Copy link
Author

Please provide a minimum reproduction repository. You can start one by running npm init nest in your terminal

why reproductions are required

it's dine, but how can i do? Any online tools to build nestjs application and share here?

@micalevisk
Copy link
Member

micalevisk commented Mar 4, 2024

@thedarkknight197 you can use https://codesandbox.io or any other tool. Just share some full code ;)

@jmcdo29
Copy link
Member

jmcdo29 commented Mar 4, 2024

nest new jwt-reproduction
cd jwt-reproduction
# make code changes
git add .
git commit -m "feat: changes necessary for reproduction"
# make GitHub/GitLab repo
git remote add origin <origin>
git push

@thedarkknight197
Copy link
Author

thedarkknight197 commented Mar 4, 2024

I know how create a repo @jmcdo29 my question was about online tools, i have created a new repo for it! :D

@micalevisk, @jmcdo29 i finded the problem: .env file is not readed by config module when use multi app structure if is placed inside apps/name-app/src folder.

How is possible configure it?

You can see the repo here: https://github.com/DigitalNextBusiness/working-jwt/tree/multi-app

@jmcdo29
Copy link
Member

jmcdo29 commented Mar 4, 2024

You can specify a custom env file path. Just keep in mind that nest build does not move the .env files by default.

I figured you knew how to make a repo, but honestly, a repo is usually the quickest and easiest way to share a reproduction as it also means immediate access to modify and tinker with as necessary in a familiar environment. Otherwise, Stackblitz and codesandbox do both work.

@jmcdo29 jmcdo29 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 4, 2024
@thedarkknight197
Copy link
Author

Thank you for your support @jmcdo29 and your helpfullexplaination very apreciated!

@thedarkknight197
Copy link
Author

thedarkknight197 commented Mar 4, 2024

@jmcdo29 Is possible use a Passport Strategy with Scoped Request?

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor(
    private usersService: UsersService, // "Error: Unknown authentication strategy "jwt" if i remove this line error gone
    private readonly configService: ConfigService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get<string>('ACCESS_TOKEN_SECRET'),
    });
  }

  async validate(payload: { sub: number }) {
    const user = await this.usersService.findOne(payload.sub);

    if (!user) {
      throw new UnauthorizedException();
    }

    return user;
  }
}

My backend is a multitenancy prisma connection with db.

this is my user service code:

@Injectable({ scope: Scope.REQUEST })
export class UsersService {
  @Inject(PrismaService) private readonly service: PrismaService;

  async create(createUserDto: CreateUserDto) {
    const hashedPassword = await bcrypt.hash(
      createUserDto.password,
      roundsOfHashing,
    );

    createUserDto.password = hashedPassword;

    return this.service.user.create({
      data: createUserDto,
    });
  }
}

Same request here: #1870

@jmcdo29
Copy link
Member

jmcdo29 commented Mar 4, 2024

@thedarkknight197 we have documentation on this

@thedarkknight197
Copy link
Author

@jmcdo29 i have updated the code in this way as menthioned in the doc you posted, could you control it?
It's working now:

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor(
    private moduleRef: ModuleRef,
    private readonly configService: ConfigService,
  ) {
    super({
      passReqToCallback: true,
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get<string>('ACCESS_TOKEN_SECRET'),
    });
  }

  async validate(request: Request, payload: { sub: number }) {
    const contextId = ContextIdFactory.getByRequest(request);
    // "UsersService" is a request-scoped provider
    const usersService = await this.moduleRef.resolve(UsersService, contextId);
    const user = await usersService.findOne(payload.sub);

    if (!user) {
      throw new UnauthorizedException();
    }

    return user;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs triage This issue has not been looked into
Projects
None yet
Development

No branches or pull requests

3 participants