-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat upgrade api keys #601
Changes from 4 commits
3c28276
43a98e5
02d75ba
d5c8fc2
8d1bf6a
4f4d42d
7230b49
a77e17d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { LoggerService } from '@@core/@core-services/logger/logger.service'; | ||
import { ProjectsService } from '@@core/projects/projects.service'; | ||
import { AuthError } from '@@core/utils/errors'; | ||
import { Injectable, BadRequestException } from '@nestjs/common'; | ||
import { JwtService } from '@nestjs/jwt'; | ||
import * as bcrypt from 'bcrypt'; | ||
|
@@ -120,7 +120,7 @@ | |
to: email, | ||
subject: 'Panora | Password Reset Request', | ||
text: `You requested a password reset. Click the following link within one hour from now to reset your password: ${resetLink}`, | ||
html: `<p>You requested a password reset. Click the link to reset your password:</p><a href="${resetLink}">${resetLink}</a> <p>The link will expire after one hour</p>`, | ||
}); | ||
|
||
this.logger.log(`Send reset email to ${email} with token ${resetToken}`); | ||
|
@@ -324,30 +324,36 @@ | |
keyName: string, | ||
): Promise<{ api_key: string }> { | ||
try { | ||
|
||
// Check project & User exist | ||
const foundProject = await this.prisma.projects.findUnique({ | ||
where: { id_project: projectId }, | ||
}); | ||
if (!foundProject) { | ||
throw new ReferenceError('project undefined'); | ||
throw new ReferenceError('Project not found'); | ||
} | ||
const foundUser = await this.prisma.users.findUnique({ | ||
where: { id_user: userId }, | ||
}); | ||
if (!foundUser) { | ||
throw new ReferenceError('user undefined'); | ||
throw new ReferenceError('User Not Found'); | ||
} | ||
|
||
/*if (foundProject.id_organization !== foundUser.id_organization) { | ||
throw new ReferenceError('User is not inside the project'); | ||
}*/ | ||
// Generate a new API key (use a secure method for generation) | ||
const { access_token } = await this.generateApiKey(projectId, userId); | ||
//const { access_token } = await this.generateApiKey(projectId, userId); | ||
// Store the API key in the database associated with the user | ||
//const hashed_token = this.hashApiKey(access_token); | ||
//const hashed_token = this.hashApiKey(access_token);" | ||
|
||
const base_key = `sk_${process.env.ENV}_${uuidv4()}`; | ||
const hashed_key = crypto.createHash('sha256').update(base_key).digest('hex'); | ||
|
||
const new_api_key = await this.prisma.api_keys.create({ | ||
data: { | ||
id_api_key: uuidv4(), | ||
api_key_hash: access_token, | ||
api_key_hash: hashed_key, | ||
name: keyName, | ||
id_project: projectId as string, | ||
id_user: userId as string, | ||
|
@@ -357,7 +363,7 @@ | |
throw new ReferenceError('api key undefined'); | ||
} | ||
|
||
return { api_key: access_token, ...new_api_key }; | ||
return { api_key: base_key, ...new_api_key }; | ||
} catch (error) { | ||
throw error; | ||
} | ||
|
@@ -377,17 +383,11 @@ | |
|
||
|
||
|
||
async getProjectIdForApiKey(apiKey: string) { | ||
async getProjectIdForApiKey(hashed_apiKey: string) { | ||
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: apiKey, | ||
api_key_hash: hashed_apiKey, | ||
}, | ||
}); | ||
|
||
|
@@ -399,33 +399,43 @@ | |
|
||
async validateApiKey(apiKey: string): Promise<boolean> { | ||
try { | ||
|
||
// TO DO : add Expiration in part 3 | ||
|
||
// 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 decoded = this.jwtService.verify(apiKey, { | ||
// secret: process.env.JWT_SECRET, | ||
// }); | ||
|
||
|
||
//const hashed_api_key = this.hashApiKey(apiKey); | ||
// pseudo-code: | ||
// 1 - SHA256 the API key from the header | ||
const hashed_key = crypto.createHash('sha256').update(apiKey).digest('hex'); | ||
|
||
|
||
// 2- check against DB | ||
// if not found, return false | ||
const saved_api_key = await this.prisma.api_keys.findUnique({ | ||
where: { | ||
api_key_hash: apiKey, | ||
api_key_hash: hashed_key, | ||
}, | ||
}); | ||
|
||
if (!saved_api_key) { | ||
throw new ReferenceError('Api Key undefined'); | ||
} | ||
if (String(decoded.project_id) !== String(saved_api_key.id_project)) { | ||
throw new ReferenceError( | ||
'Failed to validate API key: projectId mismatch.', | ||
); | ||
} | ||
|
||
// Validate that the JWT payload matches the provided userId and projectId | ||
if (String(decoded.sub) !== String(saved_api_key.id_user)) { | ||
throw new ReferenceError( | ||
'Failed to validate API key: userId mismatch.', | ||
); | ||
throw new ReferenceError('API Key not found.'); | ||
} | ||
// if (String(decoded.project_id) !== String(saved_api_key.id_project)) { | ||
// throw new ReferenceError( | ||
// 'Failed to validate API key: projectId mismatch.', | ||
// ); | ||
// } | ||
|
||
// // Validate that the JWT payload matches the provided userId and projectId | ||
// if (String(decoded.sub) !== String(saved_api_key.id_user)) { | ||
// throw new ReferenceError( | ||
// 'Failed to validate API key: userId mismatch.', | ||
// ); | ||
// } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve logging, error handling, and remove commented-out code. Add logging for validation steps, improve error messages, and remove commented-out code for clarity. + this.logger.log(`Validating API key`);
const hashed_key = crypto.createHash('sha256').update(apiKey).digest('hex');
const saved_api_key = await this.prisma.api_keys.findUnique({
where: {
api_key_hash: hashed_key,
},
});
if (!saved_api_key) {
this.logger.error(`API key not found: ${hashed_key}`);
throw new ReferenceError('API key not found');
}
this.logger.log(`API key validated successfully`);
return true;
|
||
return true; | ||
} catch (error) { | ||
throw error; | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,6 +2,7 @@ import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; | |||||
import { PassportStrategy } from '@nestjs/passport'; | ||||||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||||||
import { AuthService } from '../auth.service'; | ||||||
import * as crypto from 'crypto'; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the Using the - import * as crypto from 'crypto';
+ import * as crypto from 'node:crypto'; Committable suggestion
Suggested change
ToolsBiome
|
||||||
|
||||||
@Injectable() | ||||||
export class ApiKeyStrategy extends PassportStrategy( | ||||||
|
@@ -10,16 +11,17 @@ export class ApiKeyStrategy extends PassportStrategy( | |||||
) { | ||||||
constructor(private authService: AuthService) { | ||||||
super( | ||||||
{ header: 'Authorization', prefix: 'Bearer ' }, | ||||||
{ header: 'x-api-key', 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); | ||||||
} | ||||||
const hashed_api_key = crypto.createHash('sha256').update(apikey).digest('hex'); | ||||||
const projectId = await this.authService.getProjectIdForApiKey( | ||||||
apikey, | ||||||
hashed_api_key, | ||||||
); | ||||||
//console.log('validating api request... : ' + req.user); | ||||||
// If the API key is valid, attach the user to the request object | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -16,6 +16,10 @@ export class ConnectionUtils { | |||||||||||||
token: string, | ||||||||||||||
): Promise<ConnectionMetadata> { | ||||||||||||||
try { | ||||||||||||||
console.log('**********') | ||||||||||||||
console.log(token); | ||||||||||||||
console.log('**********') | ||||||||||||||
Comment on lines
+19
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging sensitive information. Logging sensitive information such as tokens can pose a security risk. Consider removing or obfuscating the token in the logs. - console.log('**********')
- console.log(token);
- console.log('**********')
+ console.log('**********')
+ console.log('Token received');
+ console.log('**********') Committable suggestion
Suggested change
|
||||||||||||||
|
||||||||||||||
const res = await this.prisma.connections.findFirst({ | ||||||||||||||
where: { | ||||||||||||||
connection_token: token, | ||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add logging and improve error handling.
Consider adding logging for successful retrieval and handling cases where the API key is not found.
+ this.logger.log(`Retrieving project ID for API key`); const saved_api_key = await this.prisma.api_keys.findUnique({ where: { api_key_hash: hashed_apiKey, }, }); if (!saved_api_key) { this.logger.error(`API key not found: ${hashed_apiKey}`); throw new ReferenceError('API key not found'); } this.logger.log(`Project ID retrieved successfully for API key`); return saved_api_key.id_project;
Committable suggestion