@nestjs-cognito/testing provides comprehensive testing utilities for NestJS applications using AWS Cognito authentication. It offers both real AWS Cognito integration for E2E tests and mock authentication capabilities for unit and integration tests, making it easy to test your authentication and authorization logic.
- Real E2E Testing - Authenticate against actual AWS Cognito services for realistic testing
- Mock Authentication - Generate mock JWT tokens for unit and integration tests without AWS connectivity
- Test Helpers - Convenient methods for obtaining access and ID tokens
- Dynamic Configuration - Update mock configuration at runtime during tests
- JWT Verifier Override - Seamless integration with existing
@nestjs-cognito/authmodule - Type Safety - Full TypeScript support with typed test utilities
npm install -D @nestjs-cognito/testingimport { Test } from "@nestjs/testing";
import { CognitoTestingModule } from "@nestjs-cognito/testing";
import { COGNITO_JWT_VERIFIER_INSTANCE_TOKEN } from "@nestjs-cognito/core";
import { AppModule } from "../src/app.module";
describe("Auth Tests", () => {
let app;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoTestingModule.register(
{},
{
enabled: true,
user: {
username: "test-user",
email: "test@example.com",
groups: ["users"],
},
}
),
AppModule,
],
})
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory,
})
.compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it("should authenticate with mock token", async () => {
const response = await request(app.getHttpServer())
.post("/cognito-testing-login")
.send({
username: "test-user",
password: "any-password",
clientId: "mock-client-id",
})
.expect(200);
expect(response.body.token).toBeDefined();
});
});import { Test } from "@nestjs/testing";
import { CognitoTestingModule, CognitoTestingService } from "@nestjs-cognito/testing";
import { CognitoAuthModule } from "@nestjs-cognito/auth";
describe("Auth E2E Tests", () => {
let cognitoTestingService: CognitoTestingService;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoAuthModule.register({
jwtVerifier: {
userPoolId: process.env.COGNITO_USER_POOL_ID,
clientId: process.env.COGNITO_CLIENT_ID,
tokenUse: "access",
},
}),
CognitoTestingModule.register({
identityProvider: {
region: process.env.COGNITO_REGION,
},
}),
],
}).compile();
cognitoTestingService = moduleFixture.get(CognitoTestingService);
});
it("should authenticate with real Cognito", async () => {
const token = await cognitoTestingService.getAccessToken(
{
username: "test@example.com",
password: "TestPassword123!",
},
"your-client-id"
);
expect(token).toBeDefined();
});
});The testing module consists of three main components:
Provides HTTP endpoints for test configuration and authentication:
POST /cognito-testing-login- Authenticate test users (both real and mock)POST /config- Update mock configuration during tests
Manages authentication flows and token handling:
- Real AWS Cognito authentication
- Mock authentication delegation
- Token retrieval and verification
Provides mock authentication capabilities:
- JWT token generation
- Mock user management
- Token verification without AWS
Mock testing allows you to test authentication flows without connecting to AWS Cognito, making your tests faster and more reliable.
Important: When using mock testing with
CognitoAuthModuleorCognitoCoreModule, you MUST override the JWT verifier provider to ensure proper mock functionality.
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import { CognitoTestingModule } from "@nestjs-cognito/testing";
import { COGNITO_JWT_VERIFIER_INSTANCE_TOKEN } from "@nestjs-cognito/core";
import { AppModule } from "../src/app.module";
import * as request from "supertest";
describe("Authentication (Mock)", () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
CognitoTestingModule.register(
{},
{
enabled: true,
user: {
username: "john.doe",
email: "john@example.com",
sub: "123e4567-e89b-12d3-a456-426614174000",
groups: ["users", "editors"],
},
}
),
AppModule,
],
})
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory,
})
.compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it("should authenticate with mock credentials", async () => {
const response = await request(app.getHttpServer())
.post("/cognito-testing-login")
.send({
username: "john.doe",
password: "any-password", // Password is not validated in mock mode
clientId: "mock-client-id",
})
.expect(200);
expect(response.body.token).toBeDefined();
});
it("should access protected route with mock token", async () => {
const loginResponse = await request(app.getHttpServer())
.post("/cognito-testing-login")
.send({
username: "john.doe",
password: "any-password",
clientId: "mock-client-id",
});
const response = await request(app.getHttpServer())
.get("/protected-route")
.set("Authorization", `Bearer ${loginResponse.body.token}`)
.expect(200);
expect(response.body.username).toBe("john.doe");
});
});The JWT verifier override is essential for mock testing. Without it, your application will attempt to verify tokens against real AWS Cognito:
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoTestingModule.register({}, { enabled: true, user: mockUser }),
AppModule,
],
})
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory,
})
.compile();You can update the mock user configuration during tests:
import { CognitoTestingService } from "@nestjs-cognito/testing";
describe("Dynamic User Tests", () => {
let cognitoTestingService: CognitoTestingService;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoTestingModule.register({}, { enabled: true, user: defaultUser }),
AppModule,
],
})
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory,
})
.compile();
cognitoTestingService = moduleFixture.get(CognitoTestingService);
});
it("should update mock user at runtime", async () => {
// Update the mock user configuration
await cognitoTestingService.setMockConfig({
enabled: true,
user: {
username: "admin-user",
email: "admin@example.com",
groups: ["admin", "superadmin"],
},
});
// Test with new configuration
const response = await request(app.getHttpServer())
.post("/cognito-testing-login")
.send({
username: "admin-user",
password: "any-password",
clientId: "mock-client-id",
})
.expect(200);
expect(response.body.token).toBeDefined();
});
});Real E2E testing authenticates against actual AWS Cognito services, providing the most realistic test environment.
import { Test } from "@nestjs/testing";
import { CognitoAuthModule } from "@nestjs-cognito/auth";
import { CognitoTestingModule, CognitoTestingService } from "@nestjs-cognito/testing";
describe("Real Cognito E2E", () => {
let cognitoTestingService: CognitoTestingService;
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoAuthModule.register({
jwtVerifier: {
userPoolId: "us-east-1_xxxxxx",
clientId: "your-client-id",
tokenUse: "access",
},
}),
CognitoTestingModule.register({
identityProvider: {
region: "us-east-1",
},
}),
],
}).compile();
app = moduleFixture.createNestApplication();
cognitoTestingService = moduleFixture.get(CognitoTestingService);
await app.init();
});
it("should get access token from real Cognito", async () => {
const accessToken = await cognitoTestingService.getAccessToken(
{
username: "test@example.com",
password: "TestPassword123!",
},
"your-client-id"
);
expect(accessToken).toBeDefined();
expect(typeof accessToken).toBe("string");
});
it("should access protected route with real token", async () => {
const accessToken = await cognitoTestingService.getAccessToken(
{
username: "test@example.com",
password: "TestPassword123!",
},
"your-client-id"
);
const response = await request(app.getHttpServer())
.get("/protected-route")
.set("Authorization", `Bearer ${accessToken}`)
.expect(200);
expect(response.body).toBeDefined();
});
});import { ConfigModule, ConfigService } from "@nestjs/config";
import { CognitoAuthModule } from "@nestjs-cognito/auth";
import { CognitoTestingModule } from "@nestjs-cognito/testing";
describe("Real Cognito E2E (Async)", () => {
let cognitoTestingService: CognitoTestingService;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
ConfigModule.forRoot(),
CognitoAuthModule.registerAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
jwtVerifier: {
userPoolId: config.get("COGNITO_USER_POOL_ID"),
clientId: config.get("COGNITO_CLIENT_ID"),
tokenUse: "access",
},
}),
inject: [ConfigService],
}),
CognitoTestingModule.registerAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
identityProvider: {
region: config.get("COGNITO_REGION"),
},
}),
inject: [ConfigService],
}),
],
}).compile();
cognitoTestingService = moduleFixture.get(CognitoTestingService);
});
it("should authenticate with environment configuration", async () => {
const token = await cognitoTestingService.getAccessToken(
{
username: process.env.TEST_USER_EMAIL,
password: process.env.TEST_USER_PASSWORD,
},
process.env.COGNITO_CLIENT_ID
);
expect(token).toBeDefined();
});
});The main module for configuring testing utilities.
Static Methods:
register(identityProviderOptions?, mockOptions?)
Synchronously register the testing module.
CognitoTestingModule.register(
{
identityProvider: {
region: "us-east-1",
},
},
{
enabled: true,
user: {
username: "test-user",
email: "test@example.com",
groups: ["users"],
},
}
);Parameters:
identityProviderOptions- AWS Cognito configuration for real E2E testingregion- AWS region (e.g., "us-east-1")
mockOptions- Mock testing configurationenabled- Enable mock modeuser- Mock user properties (username, email, sub, groups, etc.)
registerAsync(options)
Asynchronously register the testing module with dynamic configuration.
CognitoTestingModule.registerAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
identityProvider: {
region: config.get("COGNITO_REGION"),
},
}),
inject: [ConfigService],
});createJwtVerifierFactory()
Factory function for overriding the JWT verifier provider in mock mode.
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory
})Service for managing test authentication.
Methods:
getAccessToken(credentials, clientId): Promise<string>
Obtain an access token for testing (works with both real and mock modes).
const accessToken = await cognitoTestingService.getAccessToken(
{
username: "test@example.com",
password: "TestPassword123!",
},
"your-client-id"
);getIdToken(credentials, clientId): Promise<string>
Obtain an ID token for testing (works with both real and mock modes).
const idToken = await cognitoTestingService.getIdToken(
{
username: "test@example.com",
password: "TestPassword123!",
},
"your-client-id"
);setMockConfig(mockOptions): Promise<void>
Update the mock configuration at runtime.
await cognitoTestingService.setMockConfig({
enabled: true,
user: {
username: "new-user",
email: "new@example.com",
groups: ["admin"],
},
});Low-level service for mock JWT token generation and verification.
Methods:
generateToken(user): string
Generate a mock JWT token for the specified user.
verify(token): Promise<any>
Verify and decode a mock JWT token.
describe("Role-based Authorization", () => {
let cognitoTestingService: CognitoTestingService;
it("should allow admin access", async () => {
await cognitoTestingService.setMockConfig({
enabled: true,
user: {
username: "admin",
email: "admin@example.com",
groups: ["admin"],
},
});
const token = await getLoginToken("admin");
const response = await request(app.getHttpServer())
.delete("/admin/users/123")
.set("Authorization", `Bearer ${token}`)
.expect(200);
});
it("should deny regular user access", async () => {
await cognitoTestingService.setMockConfig({
enabled: true,
user: {
username: "user",
email: "user@example.com",
groups: ["users"],
},
});
const token = await getLoginToken("user");
await request(app.getHttpServer())
.delete("/admin/users/123")
.set("Authorization", `Bearer ${token}`)
.expect(403);
});
});describe("Token Expiration", () => {
it("should reject expired tokens", async () => {
// Generate token with custom expiration
const expiredToken = cognitoMockService.generateToken({
username: "test-user",
email: "test@example.com",
exp: Math.floor(Date.now() / 1000) - 3600, // 1 hour ago
});
await request(app.getHttpServer())
.get("/protected-route")
.set("Authorization", `Bearer ${expiredToken}`)
.expect(401);
});
});import { CognitoTestingModule } from "@nestjs-cognito/testing";
describe("GraphQL Authentication", () => {
it("should authenticate GraphQL queries", async () => {
const token = await getLoginToken("test-user");
const response = await request(app.getHttpServer())
.post("/graphql")
.set("Authorization", `Bearer ${token}`)
.send({
query: `
query {
getCurrentUser {
username
email
}
}
`,
})
.expect(200);
expect(response.body.data.getCurrentUser.username).toBe("test-user");
});
});- @nestjs-cognito/auth - Authentication and authorization for REST APIs
- @nestjs-cognito/graphql - GraphQL support for Cognito authentication
- @nestjs-cognito/core - Core functionality and AWS Cognito integration
@nestjs-cognito/testing is MIT licensed.