Skip to content

Commit

Permalink
feat: config auth module
Browse files Browse the repository at this point in the history
Move JWT options into config
Move cookie options into config
  • Loading branch information
leosuncin committed Sep 5, 2022
1 parent 2250718 commit 0fb2001
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 12 deletions.
11 changes: 7 additions & 4 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';

import { auth, AuthConfig } from '~auth/config/auth';
import { AuthController } from '~auth/controllers/auth.controller';
import { User } from '~auth/entities/user.entity';
import { AuthenticationService } from '~auth/services/authentication.service';
Expand All @@ -12,11 +14,12 @@ import { ValidateCredentialConstraint } from '~auth/validators/validate-credenti

@Module({
imports: [
ConfigModule.forFeature(auth),
TypeOrmModule.forFeature([User]),
JwtModule.register({
secret: process.env.SECRET,
signOptions: {
expiresIn: '30d',
JwtModule.registerAsync({
inject: [ConfigService],
useFactory(config: ConfigService<AuthConfig>) {
return config.getOrThrow('jwt');
},
}),
PassportModule,
Expand Down
24 changes: 24 additions & 0 deletions src/auth/config/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { JwtModuleOptions } from '@nestjs/jwt';
import type { CookieOptions } from 'express';
import invariant from 'tiny-invariant';

export type AuthConfig = ReturnType<typeof auth>;

export function auth() {
invariant(process.env.SECRET, 'SECRET is missing');

return {
jwt: {
secret: process.env.SECRET,
signOptions: {
expiresIn: '30d',
},
} as JwtModuleOptions,
cookie: {
httpOnly: true,
sameSite: 'strict',
signed: true,
secure: process.env.NODE_ENV === 'production',
} as CookieOptions,
};
}
5 changes: 5 additions & 0 deletions src/auth/controllers/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Test } from '@nestjs/testing';
import { createMock } from 'ts-auto-mock';
Expand Down Expand Up @@ -48,6 +49,10 @@ describe('AuthController', () => {
});
}

if (token === ConfigService) {
return createMock<ConfigService>();
}

return;
})
.compile();
Expand Down
11 changes: 11 additions & 0 deletions src/auth/interceptors/token.interceptor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { CallHandler } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { JwtService } from '@nestjs/jwt';
import { Test } from '@nestjs/testing';
import { createMocks } from 'node-mocks-http';
import { lastValueFrom, of } from 'rxjs';
import { createMock } from 'ts-auto-mock';

import { type AuthConfig, auth } from '~auth/config/auth';
import type { User } from '~auth/entities/user.entity';
import { john as user } from '~auth/fixtures/users';
import { TokenInterceptor } from '~auth/interceptors/token.interceptor';
Expand All @@ -23,6 +25,15 @@ describe('TokenInterceptor', () => {
return createMock<JwtService>();
}

if (token === ConfigService) {
return createMock<ConfigService>({
getOrThrow(propertyPath: keyof AuthConfig) {
// eslint-disable-next-line security/detect-object-injection
return auth()[propertyPath];
},
});
}

return;
})
.compile();
Expand Down
20 changes: 12 additions & 8 deletions src/auth/interceptors/token.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ import {
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import type { Response } from 'express';
import type { CookieOptions, Response } from 'express';
import type { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import type { AuthConfig } from '~auth/config/auth';
import type { User } from '~auth/entities/user.entity';

@Injectable()
export class TokenInterceptor implements NestInterceptor {
constructor(private readonly jwtService: JwtService) {}
private options: CookieOptions;

constructor(
private readonly jwtService: JwtService,
config: ConfigService<AuthConfig, true>,
) {
this.options = config.get('cookie');
}

intercept(
context: ExecutionContext,
Expand All @@ -25,12 +34,7 @@ export class TokenInterceptor implements NestInterceptor {
map((user) => {
const token = this.generateToken(user);

response.cookie('token', token, {
httpOnly: true,
sameSite: 'strict',
signed: true,
secure: process.env.NODE_ENV === 'production',
});
response.cookie('token', token, this.options);

return user;
}),
Expand Down

0 comments on commit 0fb2001

Please sign in to comment.