Nest Js аутентификация с использованием jwt и private и publi c ключа - PullRequest
0 голосов
/ 05 мая 2020

Я пытаюсь понять jwt и аутентификацию с помощью nest JS. Я создал два отдельных микросервиса, один из которых является службой аутентификации, после успешного входа в систему клиент получает токен jwt, и с этим токеном он может получить доступ к другому микросервису.

Вот код JwtStrategy и AuthModule службы auth:

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

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secretKey'
    });
  }

  async validate(payload: any) {
    return payload;
  }
}
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { AuthController } from './auth.controller';
import * as fs from 'fs';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: 'secretKey',
      signOptions: { expiresIn: '1h' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}

А вот код другой службы:

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

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secretKey',
    });
  }

  async validate(payload: any) {
    return payload;
  }
}

Я понял, что нет смысла использовать один и тот же секретный ключ для обеих служб (потому что, если я создам 10 микросервисов, я не буду использовать один и тот же ключ для всех), поэтому я создаю закрытый ключ и ключ publi c с помощью openssl. В AuthModule я копирую закрытый ключ вместо строки secretKey, а в другом сервисе я копирую ключ publi c вместо строки secretKey, но получаю 401, несанкционированную ошибку. Что я здесь пропустил? почему JwtStrategy не проверяет ключ publi c?

1 Ответ

2 голосов
/ 27 мая 2020

Прошло много дней, я думаю, это было решено. Я просто добавляю сюда свои два цента для будущих читателей.

Проблема заключается в экземплярах JwtModule и JwtStrategy. Они неправильно настроены. Вам необходимо передать алгоритмы, которые вы использовали бы для подписи и проверки токенов, вместе с ключами. Чтобы проверить, действительно ли токены генерируются с помощью RS256 al go, проверьте заголовок в токене по адресу https://jwt.io/. Вероятно, он покажет HS256, и поскольку ваш код не использовал правильный алгоритм для подписи токена. И это не удается, пока токен проверяется с использованием ключа publi c.

Чтобы правильно сгенерировать подписанные токены с помощью пары ключей RSA:

  • Вам необходимо добавить алгоритм в signOptions как RS256 и передайте ключи publi c и private в конфигурации JwtModule.
  • Тогда в рамках вашей службы вы должны сгенерируйте токен с помощью PRIVATE_KEY при подписании.
  • JwtStrategy используется как Guard. Все, что он делает, это проверяет JWT на основе конфигурации. Он ожидает, что будет проверяться либо ключ симметрия c «секрет» , либо «publi c часть» асимметричного c ключа . Мы должны использовать PUBLIC_KEY. Здесь также необходимо указать алгоритмы проверки для проверки. Здесь мы также должны использовать RS256 , поскольку мы использовали его для генерации токена.

Auth Module

@Module({
  imports: [
    ConfigModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => {
        const options: JwtModuleOptions = {
          privateKey: configService.get('JWT_PRIVATE_KEY'),
          publicKey: configService.get('JWT_PUB LIC_KEY'),
          signOptions: {
            expiresIn: '3h',
            issuer: '<Your Auth Service here>',
            algorithm: 'RS256',
          },
        };
        return options;
      },
      inject: [ConfigService],
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}

Auth Service

@Injectable()
export class AuthService {
  constructor(
    private jwtService: JwtService,
  ) {}

  async generateToken(
    user: User,
    signOptions: jwt.SignOptions = {},
  ): Promise<AuthJwtToken> {
    const payload = { sub: user.id, email: user.email, scopes: user.roles };
    return {
      accessToken: this.jwtService.sign(payload, signOptions),
    };
  }
}

JwtStrategy

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get('JWT_PUBLIC_KEY'),
      algorithms: ['RS256'],
    });
  }

  async validate(payload: any) {
    const { sub: userId, email, scopes: roles } = payload;
    return {
      id: userId,
      email,
      roles,
    };
  }
}

В других ваших микросервисах вы можете использовать ту же JwtStrategy, которую мы использовали в Модуль аутентификации.

Поскольку вы создаете распределенное приложение, вам необходимо поделиться PUBLIC_KEY с другими микросервисами, вручную добавив ключ или открыв его с помощью какой-либо конечной точки API. В любом случае вы должны использовать PUBLIC_KEY , чтобы другие службы могли проверить. Вы не должны делиться или предоставлять PRIVATE_KEY .

ПРИМЕЧАНИЕ : следующий код предполагает ConfigService, который предоставит пару ключей RSA в форме env . Настоятельно рекомендуется не проверять ключи в коде.

...