I want to validate the JWT token for all the controller access points buy using KeyCloack.
Sometimes I get this error:
WARN [Keycloak] Cannot validate access token: Error: Grant validation failed. Reason: invalid token (wrong audience)
or
WARN [Keycloak] Cannot validate access token: Error: Grant validation failed. Reason: invalid token (wrong ISS)
I'm sending the token as a bearer token in the header
I'm new with NestJS and Keycloak, please guide me if I missed something.
My approach:
env variable:
They keyCloak is deployed on a server whose URL is below:
## Authentication-related values
PEM_KEY_PATH='./src/auth/user-service.pem'
KEYCLOAK_AUTH_SERVER_URL='http://service.serveraws.io/realms/user-service'
KEYCLOAK_AUTH_SERVER_URL='http://service.serveraws.io/'
KEYCLOAK_REALM='user-service'
KEYCLOAK_CLIENT_ID='user-kundali'
KEYCLOAK_SECRET='test'
auth.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import {
KeycloakConnectModule,
RoleGuard,
AuthGuard,
PolicyEnforcementMode,
TokenValidation,
} from 'nest-keycloak-connect';
import * as dotenv from "dotenv";
dotenv.config();
@Module({
imports: [ KeycloakConnectModule.register({
authServerUrl: process.env.KEYCLOAK_AUTH_SERVER_URL,
realm: process.env.KEYCLOAK_REALM,
// clientId: process.env.KEYCLOAK_CLIENT_ID,
resource: process.env.KEYCLOAK_CLIENT_ID,
verifyTokenAudience: true,
secret: process.env.KEYCLOAK_SECRET,
policyEnforcement: PolicyEnforcementMode.PERMISSIVE,
tokenValidation: TokenValidation.ONLINE,
}), ],
controllers: [],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: RoleGuard,
}
],
})
export class KeyCloakModule {
}
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import validationSchema from './config.schema';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
import { KeyCloakModule } from './auth/auth.module';
@Module({
imports: [
TypeOrmModule.forRoot({
name: 'default',
type: 'mongodb',
database: 'test-db',
//url: 'mongodb://localhost:27017',
useNewUrlParser: true,
autoLoadEntities: true,
useUnifiedTopology: true,
synchronize: true,
entities: [],
ssl: true,
retryWrites: false
}),
ConfigModule.forRoot({
isGlobal: true,
validationSchema,
}),
UserModule,
KeyCloakModule
],
controllers: [],
providers: [],
})
export class AppModule {}
my custom Jwt Auth instead of using authGuard: jwtGuard.service.ts
I'm using pem key instead of the secret phrase, but I have tried with both the secret phrase and pem key, getting the same error.
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { readFileSync } from 'fs';
import path from 'path';
import * as dotenv from "dotenv";
dotenv.config();
const filePath = process.cwd();
const key = readFileSync(path.resolve(filePath, process.env.PEM_KEY_PATH));
@Injectable()
export class JwtAuthGuard extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: key,
});
}
async validate(payload: any) {
console.log(" payload ::: ",payload)
return { ...payload.user };
}
}
my user controller: user.controller.ts
import { Response } from 'express';
import {
Res, HttpStatus,
Controller, Get, Param, Query, UseGuards
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiCreatedResponse,
ApiOperation,
ApiTags,
ApiParam,
ApiQuery,
} from '@nestjs/swagger';
import { UserServices } from './users.services';
import { UserModel } from './entity/user.entity';
import { AuthGuard, Roles, Unprotected } from 'nest-keycloak-connect';
import { JwtAuthGuard } from '../auth/jwtAuthGuard.service';
@ApiBearerAuth()
@ApiTags('Reports')
@Controller('reports')
export class UserController {
constructor(
private readonly userServices: UserServices
) { }
// @Unprotected()
@Get('/user-data/:username')
// @Roles({ roles: ['admin', 'other', 'realm:sysadmin'] })
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Fetch kundali of a user.' })
@ApiParam({
name: 'username',
description: 'username',
required: true,
})
@ApiCreatedResponse({ type: UserModel, description: `fetch all the the data` })
async getUserKundali(
@Res() res: Response,
@Param("username") username: string
): Promise<any> {
const response = await this.userServices.getUserKundali(username);
if (response && response.data) {
return res.status(HttpStatus.OK).send(response);
} else {
return res.status(HttpStatus.BAD_REQUEST).send(response);
}
}
}
Change your KEYCLOAK_AUTH_SERVER_URL to 'http://service.serveraws.io/auth/'.
It must end with /auth/.