Skip to content

Commit

Permalink
Merge pull request #51 from panoratech/feat/passport-auth
Browse files Browse the repository at this point in the history
feat: api auth
  • Loading branch information
naelob authored Nov 1, 2023
2 parents 7642a3c + 7af35d2 commit 3658e69
Show file tree
Hide file tree
Showing 16 changed files with 336 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.1.1",
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^10.0.2",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.1.14",
"@prisma/client": "^5.4.2",
"axios": "^1.5.1",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
Expand All @@ -42,6 +44,7 @@
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^3.0.12",
"@types/passport-local": "^1.0.37",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.59.11",
Expand Down
23 changes: 16 additions & 7 deletions packages/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ model jobs {
}

model jobs_status_history {
id Int @id(map: "pk_1") @default(autoincrement())
timestamp DateTime @default(now()) @db.Timestamp(6)
previous_status String
new_status String
id_job Int @default(autoincrement())
jobs jobs @relation(fields: [id_job], references: [id_job], onDelete: NoAction, onUpdate: NoAction, map: "fk_4")
id_jobs_status_history Int @id(map: "pk_1") @default(autoincrement())
timestamp DateTime @default(now()) @db.Timestamp(6)
previous_status String
new_status String
id_job Int @default(autoincrement())
jobs jobs @relation(fields: [id_job], references: [id_job], onDelete: NoAction, onUpdate: NoAction, map: "fk_4")
@@index([id_job], map: "id_job_jobs_status_history")
}
Expand All @@ -63,7 +63,6 @@ model organizations {
name String
stripe_customer_id String
timezone String
logo_url String
projects projects[]
users users[]
}
Expand All @@ -86,7 +85,17 @@ model users {
created_at DateTime @default(now()) @db.Timestamp(6)
modified_at DateTime @default(now()) @db.Timestamp(6)
id_organization BigInt
api_keys api_keys[]
organizations organizations @relation(fields: [id_organization], references: [id_organization], onDelete: NoAction, onUpdate: NoAction, map: "fk_5")
@@index([id_organization], map: "fk_1_users")
}

model api_keys {
id_api_key BigInt @id(map: "id_") @default(autoincrement())
api_key String @unique(map: "unique_api_keys")
id_user Int
users users @relation(fields: [id_user], references: [id_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_7")
@@index([id_user], map: "fk_1")
}
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,
};
Loading

0 comments on commit 3658e69

Please sign in to comment.