-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: add jsonwebtoken * feat: add class JWTHelper * feat: add middleware auth-jwt * feat: add middleware auth-jwt * feat: add middleware auth-jwt
- Loading branch information
1 parent
52c2bb2
commit 6bd4331
Showing
8 changed files
with
337 additions
and
7 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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,12 @@ | ||
import { AuthenticationJWT } from './auth-jwt.js'; | ||
import { UserRepository } from '@/features/user/repositories/user-repository/user-repository.js'; | ||
import { JWTHelper } from '@/shared/infra/jwt/jwt.js'; | ||
import env from 'src/config/env.js'; | ||
|
||
export function authJwtFactory() { | ||
const jwt = new JWTHelper(env.SECRET_KEY); | ||
const userRepository = new UserRepository(); | ||
const authJwt = new AuthenticationJWT(jwt, userRepository); | ||
|
||
return { authJwt }; | ||
} |
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,119 @@ | ||
import type { Request, Response } from 'express'; | ||
import { AuthenticationJWT } from './auth-jwt.js'; | ||
import type { UserRepository } from '@/features/user/repositories/user-repository/user-repository.js'; | ||
import { JWTHelper } from '@/shared/infra/jwt/jwt.js'; | ||
|
||
const secretKey = '321'; | ||
|
||
const makeSut = () => { | ||
class UserRepositoryStub implements UserRepository { | ||
create({ email, name, password, username }: any) { | ||
return Promise.resolve({ | ||
createdAt: new Date(2024, 5, 1), | ||
deletedAt: null, | ||
email, | ||
id: 'valid_id', | ||
name, | ||
password, | ||
updatedAt: new Date(2024, 5, 1), | ||
username, | ||
}); | ||
} | ||
|
||
findById(id: string): Promise<{ | ||
email: string; | ||
id: string; | ||
name: null | string; | ||
username: string; | ||
} | null> { | ||
const user = { | ||
email: 'fake@example.com', | ||
id: 'fakeUserId', | ||
name: 'FakeName', | ||
username: 'FakeUserName', | ||
}; | ||
if (id === 'fakeUserId') { | ||
return Promise.resolve(user); | ||
} | ||
return Promise.resolve(null); | ||
} | ||
} | ||
|
||
const userRepository = new UserRepositoryStub(); | ||
const jwtHelper = new JWTHelper(secretKey as string); | ||
const auth = new AuthenticationJWT(jwtHelper, userRepository); | ||
|
||
return { auth, jwtHelper, userRepository }; | ||
}; | ||
|
||
describe('jwtAuth middleware', () => { | ||
let req: Partial<Request>; | ||
let res: Partial<Response>; | ||
let next: ReturnType<typeof vi.fn>; | ||
const { auth, jwtHelper } = makeSut(); | ||
|
||
beforeEach(() => { | ||
req = { headers: { authorization: 'Bearer' } }; | ||
res = { json: vi.fn(), status: vi.fn().mockReturnThis() }; | ||
next = vi.fn(); | ||
}); | ||
|
||
it('should call next if token is valid and user is found', async () => { | ||
const token = jwtHelper.createToken({ userId: 'fakeUserId' }); | ||
|
||
req = { headers: { authorization: `Bearer ${token}` } }; | ||
|
||
await auth.jwtAuth(req as Request, res as Response, next); | ||
|
||
expect(res.json).not.toHaveBeenCalled(); | ||
expect(res.status).not.toHaveBeenCalled(); | ||
expect(next).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should return status code 401 with error message token missing', async () => { | ||
req.headers!.authorization = undefined; | ||
|
||
await auth.jwtAuth(req as Request, res as Response, next); | ||
|
||
expect(res.status).toHaveBeenCalledWith(401); | ||
expect(res.json).toHaveBeenCalledWith({ error: 'Token missing' }); | ||
expect(next).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should return status code 401 with error message invalid token', async () => { | ||
const token = 'invalidToken'; | ||
|
||
req = { headers: { authorization: `Bearer ${token}` } }; | ||
|
||
await auth.jwtAuth(req as Request, res as Response, next); | ||
|
||
expect(res.status).toHaveBeenCalledWith(401); | ||
expect(res.json).toHaveBeenCalledWith({ error: 'Invalid token' }); | ||
expect(next).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should return status code 401 with error message invalid user', async () => { | ||
const token = jwtHelper.createToken({ userId: '2' }); | ||
|
||
req = { headers: { authorization: `Bearer ${token}` } }; | ||
|
||
await auth.jwtAuth(req as Request, res as Response, next); | ||
expect(res.status).toHaveBeenCalledWith(401); | ||
expect(res.json).toHaveBeenCalledWith({ error: 'Invalid user' }); | ||
expect(next).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should handle errors', async () => { | ||
const mockedError = new Error('fakeError'); | ||
vi.spyOn(jwtHelper, 'parseToken').mockImplementation(() => { | ||
throw mockedError; | ||
}); | ||
|
||
req = { headers: { authorization: 'Bearer 23123' } }; | ||
|
||
await auth.jwtAuth(req as Request, res as Response, next); | ||
expect(res.status).toHaveBeenCalledWith(500); | ||
expect(res.json).toHaveBeenCalledWith({ error: 'Internal Server Error' }); | ||
expect(next).not.toHaveBeenCalled(); | ||
}); | ||
}); |
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,35 @@ | ||
import type { NextFunction, Request, Response } from 'express'; | ||
import type { UserRepository } from '@/features/user/repositories/user-repository/user-repository.js'; | ||
import type { JWTHelper } from '@/shared/infra/jwt/jwt.js'; | ||
|
||
export class AuthenticationJWT { | ||
constructor( | ||
private jwtHelper: JWTHelper, | ||
private userRepository: UserRepository | ||
) {} | ||
|
||
async jwtAuth(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const token = req.headers.authorization?.split(' ')[1]; | ||
if (!token) { | ||
return res.status(401).json({ error: 'Token missing' }); | ||
} | ||
|
||
const payload = this.jwtHelper.parseToken(token); | ||
if (payload instanceof Error) { | ||
return res.status(401).json({ error: 'Invalid token' }); | ||
} | ||
|
||
const userId = payload.userId; | ||
const user = await this.userRepository.findById(userId); | ||
if (!user) { | ||
return res.status(401).json({ error: 'Invalid user' }); | ||
} | ||
|
||
next(); | ||
} catch (error) { | ||
console.error('Erro durante a autenticação:', error); | ||
return res.status(500).json({ error: 'Internal Server Error' }); | ||
} | ||
} | ||
} |
Oops, something went wrong.