Reputation: 462
I like to implement more than one named passport-JWT strategy, each with its own secret
. Is there any way it can be implemented?
From what I can understand from the documentation, only one secret can be registered during module initialization:
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy],
exports: [AuthService, JwtModule],
})
Upvotes: 12
Views: 10828
Reputation: 1
Add a guard as much you need like this AuthGuard(['jwt', 'jwt_2'])
, then strategies like this:
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {}
export class JwtStrategy2 extends PassportStrategy(Strategy, 'jwt2') {}
Upvotes: 0
Reputation: 1198
Basically when you create your strategy you can add a second argument for the strategy name. Then you can specify which JWT strategy you require on your AuthGuard
.
// auth/strategy/jwt-access.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class AccessTokenStrategy extends PassportStrategy(Strategy, 'jwt-access-token') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'access-token-secret',
});
}
async validate(payload: any) {
// your validate implementation here
return {};
}
}
// auth/guard/jwt-access-auth.guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class AccessTokenAuthGuard extends AuthGuard('jwt-access-token') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
You can keep your auth.module.ts
as is, and in your service you can use the options param of jwtService.sign
as in the code below
@Injectable()
export class AuthService {
...
login(user: User) {
return {
access_token: this.jwtService.sign({ /* object to sign */ }),
refresh_token: this.jwtService.sign(
{ /* object to sign */ }
{ secret: 'refresh-token-secret', expiresIn: '14d' },
),
};
}
}
Upvotes: 3
Reputation: 57
I did the same thing a few days ago. I created refresh token and access token. here is the app module:
imports: [..., JwtModule.register({})]
I registered JwtModule just like this. if you want to create access token: access token strategy:
export class AccessStrategy extends PassportStrategy(Strategy, 'access') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: JwtConstants.access_token_secret,
});
}
validate(payload: any) {
return payload;
}
}
and if you want to create access token:
accessToken(userId: number, username: string) {
const token = this.jwtService.signAsync(
{
sub: userId,
username: username,
},
{
secret: JwtConstants.access_token_secret,
expiresIn: 60,
},
);
if (token) {
return token;
}
return null;
}
you can do same thing for refresh token or your another tokens
Upvotes: 2
Reputation: 149
I recently created a package for managing this, extending passport-jwt to allow an array of passport-jwt configurations to be passed to the constructor. When a request arrives, each config is checked in parallel using the standard passport-jwt code to see if the JWT should be authorised.
Here is the package: https://www.npmjs.com/package/@mestrak/passport-multi-jwt.
In NestJS you would do something like this in jwt.strategy.ts
(or whatever your strategy setup file is called.
import { ExtractJwt, Strategy } from '@mestrak/passport-multi-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: 'a_secret_key',
},
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'another_secret_key',
}]);
}
async validate(payload: any) {
return payload;
}
}
Upvotes: 1
Reputation: 70061
To allow for the registration of multiple variants of the same service, you're going to need to use a custom provider and wrapper module around the JwtModule
. It would probably look something like this:
@Module({
imports: [JwtModule.register({
secret: secret1,
signOptions: { expiresIn: '60s' },
})],
providers: [{
provide: 'JwtSecret1Service',
useExisting: JwtService,
}],
exports: ['JwtSecret1Service'],
})
export class JwtSecret1Module {}
Now you can use @Inject('JwtSecret1Service')
to use this specific configuration so long as JwtSecret1Module
has been added to the imports
of the consuming module. You can do this with as many variants of the JwtService
as you want, and each one will hold it's own configuration
Upvotes: 12