Skip to content

Commit

Permalink
feat: api auth
Browse files Browse the repository at this point in the history
  • Loading branch information
naelob committed Nov 1, 2023
1 parent 7642a3c commit b524480
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 4 deletions.
23 changes: 21 additions & 2 deletions packages/api/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, Request, Post, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { AuthGuard } from '@nestjs/passport';

Check warning on line 3 in packages/api/src/app.controller.ts

View workflow job for this annotation

GitHub Actions / build-api (16.x)

'AuthGuard' is defined but never used
import { LocalAuthGuard } from './auth/guards/local-auth.guard';
import { AuthService } from './auth/auth.service';
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
constructor(
private readonly appService: AppService,
private authService: AuthService,
) {}

@Get()
getHello(): string {
return this.appService.getHello();
}

@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}

@UseGuards(LocalAuthGuard)
@Post('auth/login')
async login(@Request() req) {
return this.authService.login(req.user);
}
}
6 changes: 4 additions & 2 deletions packages/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ContactModule } from './crm/contact/contact.module';

Check warning on line 4 in packages/api/src/app.module.ts

View workflow job for this annotation

GitHub Actions / build-api (16.x)

'ContactModule' is defined but never used
import { CrmModule } from './crm/crm.module';
import { AuthModule } from './auth/auth.module';
import { AuthService } from './auth/auth.service';

@Module({
imports: [CrmModule],
imports: [CrmModule, AuthModule],
controllers: [AppController],
providers: [AppService],
providers: [AppService, AuthService],
})
export class AppModule {}
29 changes: 29 additions & 0 deletions packages/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from './users/users.module';
import { LocalStrategy } from './strategies/local.strategy';
import { PassportModule } from '@nestjs/passport';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { jwtConstants } from './utils/constants';
import { JwtStrategy } from './strategies/jwt.strategy';
import { UsersService } from './users/users.service';

@Module({
providers: [
AuthService,
LocalStrategy,
JwtStrategy,
UsersService,
JwtService,
],
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
],
exports: [UsersService, JwtService],
})
export class AuthModule {}
18 changes: 18 additions & 0 deletions packages/api/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';

describe('AuthService', () => {
let service: AuthService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();

service = module.get<AuthService>(AuthService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
26 changes: 26 additions & 0 deletions packages/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { UsersService } from './users/users.service';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}

async validateUser(username: string, pass: string): Promise<any> {
const user = await this.usersService.findOne(username);
if (user && user.password === pass) {
const { password, ...result } = user;

Check warning on line 15 in packages/api/src/auth/auth.service.ts

View workflow job for this annotation

GitHub Actions / build-api (16.x)

'password' is assigned a value but never used
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}
5 changes: 5 additions & 0 deletions packages/api/src/auth/guards/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
5 changes: 5 additions & 0 deletions packages/api/src/auth/guards/local-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
19 changes: 19 additions & 0 deletions packages/api/src/auth/strategies/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from '../utils/constants';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}

async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
19 changes: 19 additions & 0 deletions packages/api/src/auth/strategies/local.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}

async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
8 changes: 8 additions & 0 deletions packages/api/src/auth/users/users.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
18 changes: 18 additions & 0 deletions packages/api/src/auth/users/users.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';

describe('UsersService', () => {
let service: UsersService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();

service = module.get<UsersService>(UsersService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
24 changes: 24 additions & 0 deletions packages/api/src/auth/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';

// This should be a real class/interface representing a user entity
export type User = any;

@Injectable()
export class UsersService {
private readonly users = [
{
userId: 1,
username: 'john',
password: 'changeme',
},
{
userId: 2,
username: 'maria',
password: 'guess',
},
];

async findOne(username: string): Promise<User | undefined> {
return this.users.find((user) => user.username === username);
}
}
3 changes: 3 additions & 0 deletions packages/api/src/auth/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const jwtConstants = {
secret: process.env.JWT_SECRET,
};

0 comments on commit b524480

Please sign in to comment.