-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
32 changed files
with
457 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
ENV=dev | ||
DATABASE_URL= | ||
JWT_SECRET= | ||
<<<<<<< HEAD | ||
JWT_SECRET= | ||
======= | ||
JWT_SECRET="SECRET" | ||
>>>>>>> feat/fix-auth |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 8 additions & 9 deletions
17
packages/api/src/auth/auth.module.ts → packages/api/src/@core/auth/auth.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,28 @@ | ||
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'; | ||
import { ApiKeyStrategy } from './strategies/auth-header-api-key.strategy'; | ||
import { PrismaService } from '../prisma/prisma.service'; | ||
import { ConfigService } from '@nestjs/config'; | ||
|
||
@Module({ | ||
providers: [ | ||
AuthService, | ||
LocalStrategy, | ||
JwtStrategy, | ||
UsersService, | ||
JwtService, | ||
ApiKeyStrategy, | ||
PrismaService, | ||
ConfigService, | ||
], | ||
imports: [ | ||
UsersModule, | ||
PassportModule, | ||
JwtModule.register({ | ||
secret: jwtConstants.secret, | ||
signOptions: { expiresIn: '60s' }, | ||
secret: process.env.JWT_SECRET, | ||
}), | ||
], | ||
exports: [UsersService, JwtService], | ||
exports: [PrismaService, JwtService], | ||
}) | ||
export class AuthModule {} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { JwtService } from '@nestjs/jwt'; | ||
import { CreateUserDto, LoginCredentials } from './dto/create-user.dto'; | ||
import { PrismaService } from '../prisma/prisma.service'; | ||
import * as bcrypt from 'bcrypt'; | ||
import * as crypto from 'crypto'; | ||
|
||
//TODO: Ensure the JWT is used for user session authentication and that it's short-lived. | ||
@Injectable() | ||
export class AuthService { | ||
constructor(private prisma: PrismaService, private jwtService: JwtService) {} | ||
|
||
async register(user: CreateUserDto) { | ||
try { | ||
// Generate a salt and hash the password | ||
const salt = await bcrypt.genSalt(); | ||
const hashedPassword = await bcrypt.hash(user.password_hash, salt); | ||
|
||
const res = await this.prisma.users.create({ | ||
data: { | ||
email: user.email, | ||
password_hash: hashedPassword, | ||
first_name: user.first_name, | ||
last_name: user.last_name, | ||
}, | ||
}); | ||
if (!res) { | ||
throw new UnauthorizedException('registering issue'); | ||
} | ||
const { password_hash, ...resp_user } = res; | ||
return resp_user; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} | ||
|
||
async login(user: LoginCredentials): Promise<{ access_token: string }> { | ||
try { | ||
const foundUser = await this.prisma.users.findUnique({ | ||
where: { id_user: user.id_user }, | ||
}); | ||
//TODO: if not founder | ||
if ( | ||
foundUser && | ||
(await bcrypt.compare(user.password_hash, foundUser.password_hash)) | ||
) { | ||
const { password_hash, ...result } = user; | ||
|
||
if (!result) { | ||
throw new UnauthorizedException('Invalid credentials.'); | ||
} | ||
} | ||
const payload = { | ||
email: foundUser.email, | ||
sub: foundUser.id_user, | ||
}; | ||
|
||
return { | ||
access_token: this.jwtService.sign(payload, { | ||
secret: process.env.JWT_SECRET, | ||
}), // token used to generate api keys | ||
}; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} | ||
|
||
hashApiKey(apiKey: string): string { | ||
console.log('hey hashing...'); | ||
return crypto.createHash('sha256').update(apiKey).digest('hex'); | ||
} | ||
|
||
/*hashApiKey(apiKey: string): string { | ||
return crypto.createHash('sha256').update(apiKey).digest('hex'); | ||
} | ||
async generateApiKey1(projectId: number): Promise<string> { | ||
const api_key = 'PROD_' + crypto.randomUUID(); | ||
// Store the API key in the database associated with the user | ||
const new_api_key = await this.prisma.api_keys.create({ | ||
data: { | ||
api_key_hash: this.hashApiKey(api_key), | ||
id_project: projectId, | ||
}, | ||
}); | ||
return api_key; | ||
} | ||
async validateApiKey1(apiKey: string): Promise<boolean> { | ||
const hash = this.hashApiKey(apiKey); | ||
const api_key = await this.prisma.api_keys.findUnique({ | ||
where: { | ||
api_key_hash: hash, | ||
}, | ||
}); | ||
return Boolean(api_key); | ||
}*/ | ||
|
||
async generateApiKey( | ||
projectId: number, | ||
userId: number, | ||
): Promise<{ access_token: string }> { | ||
console.log("'ddddd"); | ||
const secret = process.env.JWT_SECRET; | ||
const jwtPayload = { | ||
sub: userId, | ||
projectId: projectId, | ||
}; | ||
return { | ||
access_token: this.jwtService.sign(jwtPayload, { | ||
secret: process.env.JWT_SECRET, | ||
expiresIn: '1y', | ||
}), | ||
}; | ||
} | ||
|
||
async generateApiKeyForUser( | ||
userId: number, | ||
projectId: number, | ||
): Promise<{ api_key: string }> { | ||
try { | ||
console.log('here is my userId ', userId); | ||
//tmp: | ||
const resp = await this.prisma.organizations.create({ | ||
data: { | ||
name: 'org1', | ||
stripe_customer_id: 'oneone', | ||
}, | ||
}); | ||
await this.prisma.projects.create({ | ||
data: { | ||
name: 'proj', | ||
id_organization: resp.id_organization, | ||
}, | ||
}); | ||
//TODO: CHECK IF PROJECT_ID IS EXISTENT | ||
//fetch user_id | ||
const foundUser = await this.prisma.users.findUnique({ | ||
where: { id_user: userId }, | ||
}); | ||
if (!foundUser) { | ||
throw new UnauthorizedException( | ||
'user not found inside api key function generation', | ||
); | ||
} | ||
//TODO: check if user is indeed inside the project | ||
|
||
// Generate a new API key (use a secure method for generation) | ||
const { access_token } = await this.generateApiKey(projectId, userId); | ||
console.log('hey'); | ||
// Store the API key in the database associated with the user | ||
const hashed_token = this.hashApiKey(access_token); | ||
console.log('hey2'); | ||
const new_api_key = await this.prisma.api_keys.create({ | ||
data: { | ||
api_key_hash: hashed_token, | ||
id_project: projectId, | ||
id_user: userId, | ||
}, | ||
}); | ||
if (!new_api_key) { | ||
throw new UnauthorizedException('api keys issue to add to db'); | ||
} | ||
console.log('.....'); | ||
|
||
return { api_key: access_token }; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} | ||
|
||
async validateApiKey(apiKey: string): Promise<boolean> { | ||
try { | ||
// Decode the JWT to verify if it's valid and get the payload | ||
const decoded = this.jwtService.verify(apiKey, { | ||
secret: process.env.JWT_SECRET, | ||
}); | ||
|
||
const hashed_api_key = this.hashApiKey(apiKey); | ||
const saved_api_key = await this.prisma.api_keys.findUnique({ | ||
where: { | ||
api_key_hash: hashed_api_key, | ||
}, | ||
}); | ||
if (!saved_api_key) { | ||
throw new UnauthorizedException('Failed to fetch API key from DB'); | ||
} | ||
|
||
if (decoded.projectId !== saved_api_key.id_project) { | ||
throw new UnauthorizedException( | ||
'Failed to validate API key: projectId invalid.', | ||
); | ||
} | ||
|
||
// Validate that the JWT payload matches the provided userId and projectId | ||
if (decoded.sub !== saved_api_key.id_user) { | ||
throw new UnauthorizedException( | ||
'Failed to validate API key: userId invalid.', | ||
); | ||
} | ||
return true; | ||
} catch (error) { | ||
console.error('validateApiKey error:', error); | ||
throw new UnauthorizedException('Failed to validate API key.'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export class CreateUserDto { | ||
first_name: string; | ||
last_name: string; | ||
email: string; | ||
password_hash: string; | ||
} | ||
|
||
export type LoginCredentials = { | ||
id_user?: number; | ||
email?: string; | ||
password_hash: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ApiKeyAuthGuard extends AuthGuard('api-key') {} |
File renamed without changes.
File renamed without changes.
33 changes: 33 additions & 0 deletions
33
packages/api/src/@core/auth/strategies/auth-header-api-key.strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { AuthService } from '../auth.service'; | ||
|
||
@Injectable() | ||
export class ApiKeyStrategy extends PassportStrategy( | ||
HeaderAPIKeyStrategy, | ||
'api-key', | ||
) { | ||
constructor(private authService: AuthService) { | ||
super( | ||
{ header: 'Authorization', prefix: '' }, | ||
true, | ||
async (apikey: string, done, req) => { | ||
try { | ||
const isValid = await this.authService.validateApiKey(apikey); | ||
if (!isValid) { | ||
return done(new UnauthorizedException('Invalid API Key'), null); | ||
} | ||
console.log('validating api request... : ' + req.user); | ||
// If the API key is valid, attach the user to the request object | ||
req.user = { ...req.user, apiKeyValidated: true }; | ||
|
||
// If valid, we now have the user info from the API key validation process | ||
return done(null, req.user); | ||
} catch (error) { | ||
return done(error, false); | ||
} | ||
}, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface ApiKey { | ||
projectId: number; | ||
api_key_name: string; | ||
token: string; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { OrganisationsService } from './organisations.service'; | ||
|
||
@Module({ | ||
providers: [OrganisationsService], | ||
}) | ||
export class OrganisationsModule {} |
18 changes: 18 additions & 0 deletions
18
packages/api/src/@core/organisations/organisations.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { OrganisationsService } from './organisations.service'; | ||
|
||
describe('OrganisationsService', () => { | ||
let service: OrganisationsService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [OrganisationsService], | ||
}).compile(); | ||
|
||
service = module.get<OrganisationsService>(OrganisationsService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
9 changes: 9 additions & 0 deletions
9
packages/api/src/@core/organisations/organisations.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
@Injectable() | ||
export class OrganisationsService { | ||
//TODO | ||
async createOrganization() { | ||
return; | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.