Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aneudysamparo/30d0c4d7f82cd089753dcda56de040b0 to your computer and use it in GitHub Desktop.
Save aneudysamparo/30d0c4d7f82cd089753dcda56de040b0 to your computer and use it in GitHub Desktop.
NestJs authentication using JWT and private and public key
Since it's been days, I am guessing this was solved. I am just adding my two cents here for future readers.
The problem lies with the JwtModule and the JwtStrategy instantiation. They aren't configured properly.
You need to pass in the algorithms you'd use for signing and verifying the tokens, along with the keys.
To verify whether the tokens are actually getting generated with the RS256 algo, check the header in the token at https://jwt.io/.
It would probably show HS256, and since your code didn't use the correct algorithm to sign the token.
And it fails while token gets verified using the public key.
To generate signed tokens properly with the RSA key pair:
--You need to add algorithm in the signOptions as RS256 and pass in the the public and private keys in the JwtModule configuration.
--Then within your service, you'd generate the token with the PRIVATE_KEY when you sign.
--JwtStrategy is used as a Guard. All it does is verify the JWT based on configuration.
It expects either the symmetric key "secret" or the "public part" of the asymmetric key to verify.
We have to use the PUBLIC_KEY. You also have to specify the algorithms to check for verifying here.
We have to use RS256 here as well, since we used that to generate the token.
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,
};
}
}
In your other micro-services, You can use the same JwtStrategy we used in the Auth module.
Since you're creating a distributed app, you need to share the PUBLIC_KEY with the other micro-services by manually adding the key or by exposing it using some API endpoint. Either way, you have to use the PUBLIC_KEY for the other services to verify. You must not share or expose the PRIVATE_KEY.
NOTE: The following code assumes a ConfigService which would provide the RSA key pair form env. It's strongly suggested not to check in the keys in the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment