Skip to content

Commit

Permalink
Merge pull request #70 from piyush-kacha/feature/auth
Browse files Browse the repository at this point in the history
Add authentication module and routes
  • Loading branch information
piyush-kacha authored Apr 30, 2024
2 parents 71fcba5 + ee0502b commit 2a9d5d8
Show file tree
Hide file tree
Showing 35 changed files with 900 additions and 2 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,44 @@ cd nestjs-starter-kit
npm ci
```


Use the copy command to create a copy of the example .env file. Replace `example.env` with the name of your example .env file:

```bash
cp example.env .env
```

## Generating an RSA Key Pair for JWT Authentication with OpenSSL

1. Generate a private key:

```sh
openssl genrsa -out private_key.pem 2048
```
2. Extract the public key from the private key:
```sh
openssl rsa -in private_key.pem -outform PEM -pubout -out public_key.pem
```
This will create two files in the current directory: private_key.pem (the private key) and public_key.pem (the corresponding public key).

Note: The key size (2048 bits in this example) can be adjusted to suit your security requirements.
3. Encode the public key in base64 encoding:
```sh
openssl base64 -A -in public_key.pem -out public_key_base64.txt
```
Copy the contents of the public_key_base64.txt file and paste it into the public_key_base64.txt file in .env JWT_PUBLIC_KEY variable.

4. Encode the private key in base64 encoding:
```sh
openssl base64 -A -in private_key.pem -out private_key_base64.txt
```
Copy the contents of the private_key_base64.txt file and paste it into the private_key_base64.txt file in .env JWT_PRIVATE_KEY variable.

5. Remove the public_key.pem and private_key.pem files.

6. Remove the public_key_base64.txt and private_key_base64.txt files.


## Running the Application
To run the application in development mode, use the following command:
```bash
Expand Down
13 changes: 13 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,16 @@ CLUSTERING=

# The URI for the MongoDB database used by the application
MONGODB_URI=

# The private key for generating JSON Web Tokens (JWTs)
# in README.md for more information how to generate a private key
JWT_PRIVATE_KEY=

# The public key for verifying JSON Web Tokens (JWTs)
# in README.md for more information how to generate a public key
JWT_PUBLIC_KEY=

# The expiration time for JSON Web Tokens (JWTs)
# expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js).
# Eg: 60, "2 days", "10h", "7d"
JWT_EXPIRATION_TIME=
58 changes: 58 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@
"@nestjs/core": "^10.3.8",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mongoose": "^10.0.6",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.3.8",
"@nestjs/swagger": "^7.3.1",
"axios": "^1.6.8",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"mongoose": "^8.3.3",
"nestjs-pino": "^4.0.0",
"passport-jwt": "^4.0.1",
"pino": "^9.0.0",
"pino-http": "^9.0.0",
"reflect-metadata": "^0.1.14",
Expand Down
6 changes: 6 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
UnauthorizedExceptionFilter,
ValidationExceptionFilter,
} from './filters';
import { AuthModule } from './modules/auth/auth.module';
import { UserModule } from './modules/user/user.module';
import { WorkspaceModule } from './modules/workspace/workspace.module';

// Import other modules

Expand All @@ -45,6 +48,9 @@ import {
}),
}),
// Import other modules
AuthModule,
UserModule,
WorkspaceModule,
],
controllers: [AppController], // Define the application's controller
providers: [
Expand Down
3 changes: 3 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IDatabaseConfig, databaseConfig } from './database.config';
import { IJwtConfig, jwtConfig } from './jwt.config';
import { NodeEnv } from '../shared/enums/node-env.enum';

export interface IConfig {
Expand All @@ -8,6 +9,7 @@ export interface IConfig {
logLevel: string;
clustering: string;
database: IDatabaseConfig;
jwt: IJwtConfig;
}

export const configuration = (): Partial<IConfig> => ({
Expand All @@ -17,4 +19,5 @@ export const configuration = (): Partial<IConfig> => ({
logLevel: process.env.LOG_LEVEL,
clustering: process.env.CLUSTERING,
database: databaseConfig(),
jwt: jwtConfig(),
});
11 changes: 11 additions & 0 deletions src/config/jwt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface IJwtConfig {
privateKey: string;
publicKey: string;
expiresIn: string;
}

export const jwtConfig = (): IJwtConfig => ({
privateKey: Buffer.from(process.env.JWT_PRIVATE_KEY, 'base64').toString('utf-8'),
publicKey: Buffer.from(process.env.JWT_PUBLIC_KEY, 'base64').toString('utf-8'),
expiresIn: process.env.JWT_EXPIRATION_TIME || '1h',
});
7 changes: 5 additions & 2 deletions src/exceptions/unauthorized.exception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export class UnauthorizedException extends HttpException {
/** The detailed description of the error. */
@ApiProperty({
description: 'A description of the error message.',
example: 'This error message indicates that the authentication token provided with the request has expired, and therefore the server cannot verify the users identity.',
example:
'This error message indicates that the authentication token provided with the request has expired, and therefore the server cannot verify the users identity.',
})
description: string;

Expand Down Expand Up @@ -180,7 +181,9 @@ export class UnauthorizedException extends HttpException {
*/
static REQUIRED_RE_AUTHENTICATION = (msg?: string) => {
return new UnauthorizedException({
message: msg || 'Your previous login session has been terminated due to a password change or reset. Please log in again with your new password.',
message:
msg ||
'Your previous login session has been terminated due to a password change or reset. Please log in again with your new password.',
code: ExceptionConstants.UnauthorizedCodes.REQUIRED_RE_AUTHENTICATION,
});
};
Expand Down
44 changes: 44 additions & 0 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ApiBadRequestResponse, ApiInternalServerErrorResponse, ApiOkResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { Body, Controller, HttpCode, Post, ValidationPipe } from '@nestjs/common';

import { AuthService } from './auth.service';
import { LoginReqDto, LoginResDto, SignupReqDto, SignupResDto } from './dtos';

import { BadRequestException } from '../../exceptions/bad-request.exception';
import { InternalServerErrorException } from '../../exceptions/internal-server-error.exception';
import { UnauthorizedException } from '../../exceptions/unauthorized.exception';

@ApiBadRequestResponse({
type: BadRequestException,
})
@ApiInternalServerErrorResponse({
type: InternalServerErrorException,
})
@ApiUnauthorizedResponse({
type: UnauthorizedException,
})
@ApiTags('Auth')
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}

// POST /auth/signup
@ApiOkResponse({
type: SignupResDto,
})
@HttpCode(200)
@Post('signup')
async signup(@Body(ValidationPipe) signupReqDto: SignupReqDto): Promise<SignupResDto> {
return this.authService.signup(signupReqDto);
}

// POST /auth/login
@ApiOkResponse({
type: LoginResDto,
})
@HttpCode(200)
@Post('login')
async login(@Body(ValidationPipe) loginReqDto: LoginReqDto): Promise<LoginResDto> {
return this.authService.login(loginReqDto);
}
}
38 changes: 38 additions & 0 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Importing the required libraries
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';

// Importing the required internal files
import { JwtUserStrategy } from './strategies/jwt-user.strategy';

// Importing the required external modules and files
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserModule } from '../user/user.module';
import { WorkspaceModule } from '../workspace/workspace.module';

@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.registerAsync({
inject: [ConfigService],
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
privateKey: configService.get('jwt.privateKey'),
publicKey: configService.get('jwt.publicKey'),
signOptions: { expiresIn: configService.get('jwt.expiresIn'), algorithm: 'RS256' },
verifyOptions: {
algorithms: ['RS256'],
},
}),
}),
UserModule,
WorkspaceModule,
],
providers: [JwtUserStrategy, AuthService],
controllers: [AuthController],
exports: [JwtUserStrategy, PassportModule],
})
export class AuthModule {}
Loading

0 comments on commit 2a9d5d8

Please sign in to comment.