-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
349 additions
and
1 deletion.
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 |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Response } from 'express' | ||
|
||
function createResponseObject (): Response { | ||
return { | ||
status: function (code) { | ||
this.statusCode = code | ||
return this | ||
}, | ||
json: function () { | ||
return { | ||
status: this.statusCode | ||
} | ||
} | ||
} as Response | ||
} | ||
|
||
export default createResponseObject |
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,84 @@ | ||
import request from 'supertest' | ||
|
||
import app from '../../../src/app' | ||
import MongooseConnection from '../../helpers/MongooseConnection' | ||
import factory from '../../factories' | ||
|
||
describe('AuthController', () => { | ||
beforeAll(async () => { | ||
await MongooseConnection.connect('AuthController') | ||
}) | ||
|
||
afterEach(async () => { | ||
await MongooseConnection.truncate() | ||
}) | ||
|
||
afterAll(async () => { | ||
await MongooseConnection.disconnect() | ||
}) | ||
|
||
describe('login', () => { | ||
it('should authenticate with valid credentials', async () => { | ||
const user = await factory.attrs('User') | ||
|
||
await request(app) | ||
.post('/register') | ||
.send(user) | ||
|
||
const response = await request(app) | ||
.post('/login') | ||
.send({ | ||
email: user.email, | ||
password: user.password | ||
}) | ||
|
||
expect(response.body).toHaveProperty('token') | ||
}) | ||
}) | ||
|
||
it('should not authenticate with invalid credentials', async () => { | ||
const user = (await factory.create('User', { password: '123456' })).toObject() | ||
|
||
const response = await request(app) | ||
.post('/login') | ||
.send({ | ||
email: user.email, | ||
password: '123' | ||
}) | ||
|
||
expect(response.status).toBe(401) | ||
}) | ||
|
||
it('should not authenticate with nonexistent user', async () => { | ||
const user = await factory.attrs('User', { email: '[email protected]' }) | ||
|
||
const response = await request(app) | ||
.post('/login') | ||
.send({ | ||
email: '[email protected]', | ||
password: user.password | ||
}) | ||
|
||
expect(response.status).toBe(401) | ||
}) | ||
|
||
it('should not be able to authenticate with invalid args', async () => { | ||
const user = await factory.attrs('User') | ||
|
||
const responseWithoutEmail = await request(app) | ||
.post('/login') | ||
.send({ | ||
password: user.password | ||
}) | ||
|
||
expect(responseWithoutEmail.status).toBe(400) | ||
|
||
const responseWithoutPassword = await request(app) | ||
.post('/login') | ||
.send({ | ||
email: user.email | ||
}) | ||
|
||
expect(responseWithoutPassword.status).toBe(400) | ||
}) | ||
}) |
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,55 @@ | ||
import jwt from 'jsonwebtoken' | ||
|
||
import { AuthRequest } from '../../../src/types' | ||
import createResponseObject from '../../helpers/response' | ||
import authMiddleware from '../../../src/middlewares/auth' | ||
import authConfig from '../../../src/config/auth' | ||
|
||
describe('authMiddleware', () => { | ||
it('should append the userId property in the req object when a valid header is provided', async () => { | ||
const token = jwt.sign({ _id: 'qwert' }, authConfig.secret, { | ||
expiresIn: authConfig.expiresIn | ||
}) | ||
|
||
const req = { | ||
headers: { | ||
authorization: `Bearer ${token}` | ||
}, | ||
userId: '' | ||
} as AuthRequest | ||
|
||
const response = createResponseObject() | ||
|
||
await authMiddleware(req, response, () => {}) | ||
|
||
expect(req.userId).toBe('qwert') | ||
}) | ||
|
||
it('should return an error when a token is not provided', async () => { | ||
const req = { | ||
headers: {}, | ||
userId: '' | ||
} as AuthRequest | ||
|
||
const response = createResponseObject() | ||
|
||
await authMiddleware(req, response, () => {}) | ||
|
||
expect(response.statusCode).toBe(401) | ||
}) | ||
|
||
it('should return an error when the token provided is invalid', async () => { | ||
const req = { | ||
headers: { | ||
authorization: 'Bearer qwert' | ||
}, | ||
userId: '' | ||
} as AuthRequest | ||
|
||
const response = createResponseObject() | ||
|
||
await authMiddleware(req, response, () => {}) | ||
|
||
expect(response.statusCode).toBe(401) | ||
}) | ||
}) |
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 |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
"version": "1.0.0", | ||
"description": "An awesome post API", | ||
"main": "index.js", | ||
"repository": "https://github.com/matheuspiment/alumnet-developer.git", | ||
"repository": "https://github.com/matheuspiment/post-api.git", | ||
"author": "Matheus Ribeiro Pimenta Nunes <[email protected]>", | ||
"license": "MIT", | ||
"private": true, | ||
|
@@ -28,6 +28,7 @@ | |
"cors": "^2.8.5", | ||
"dotenv": "^8.1.0", | ||
"express": "^4.17.1", | ||
"jsonwebtoken": "^8.5.1", | ||
"lodash": "^4.17.15", | ||
"mongoose": "^5.7.3", | ||
"yup": "^0.27.0" | ||
|
@@ -38,6 +39,7 @@ | |
"@types/cors": "^2.8.6", | ||
"@types/express": "^4.17.1", | ||
"@types/jest": "^24.0.18", | ||
"@types/jsonwebtoken": "^8.3.4", | ||
"@types/mongoose": "^5.5.19", | ||
"@types/supertest": "^2.0.8", | ||
"@typescript-eslint/eslint-plugin": "^2.3.2", | ||
|
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,6 @@ | ||
import '../bootstrap' | ||
|
||
export default { | ||
secret: process.env.JWT_SECRET, | ||
expiresIn: '7d' | ||
} |
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,51 @@ | ||
import { Request, Response } from 'express' | ||
import * as Yup from 'yup' | ||
import jwt from 'jsonwebtoken' | ||
import bcrypt from 'bcryptjs' | ||
|
||
import User from '../schemas/User' | ||
import authConfig from '../config/auth' | ||
|
||
class AuthController { | ||
async login (req: Request, res: Response): Promise<Response> { | ||
const schema = Yup.object().shape({ | ||
email: Yup.string().email().required(), | ||
password: Yup.string().required() | ||
}) | ||
|
||
if (!(await schema.isValid(req.body))) { | ||
return res.status(400).json({ error: 'Validation fails' }) | ||
} | ||
|
||
try { | ||
const { email, password } = req.body | ||
|
||
const user = await User.findOne({ email }) | ||
|
||
if (!user) { | ||
return res.status(401).send({ error: 'User not found' }) | ||
} | ||
|
||
if (!(await bcrypt.compare(password, user.password))) { | ||
return res.status(401).send({ error: 'Password does not match' }) | ||
} | ||
|
||
const { _id, name } = user | ||
|
||
return res.json({ | ||
user: { | ||
_id, | ||
name, | ||
}, | ||
token: jwt.sign({ _id }, authConfig.secret, { | ||
expiresIn: authConfig.expiresIn | ||
}) | ||
}) | ||
} catch (err) { | ||
return res.status(400).send({ error: 'Login failed' }) | ||
} | ||
} | ||
} | ||
|
||
export default new AuthController() |
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,26 @@ | ||
import { Response, NextFunction } from 'express' | ||
import { promisify } from 'util' | ||
import jwt from 'jsonwebtoken' | ||
|
||
import { AuthRequest } from '../types' | ||
import authConfig from '../config/auth' | ||
|
||
export default async (req: AuthRequest, res: Response, next: NextFunction): Promise<Response | void> => { | ||
const authHeader = req.headers.authorization | ||
|
||
if (!authHeader) { | ||
return res.status(401).json({ error: 'Token not provided' }) | ||
} | ||
|
||
const [, token] = authHeader.split(' ') | ||
|
||
try { | ||
const decoded = await promisify(jwt.verify)(token, authConfig.secret) as unknown as { _id: string } | ||
|
||
req.userId = decoded._id | ||
|
||
return next() | ||
} catch (error) { | ||
return res.status(401).json({ error: 'Token invalid' }) | ||
} | ||
} |
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,9 +1,24 @@ | ||
import { Router } from 'express' | ||
|
||
import authMiddleware from './middlewares/auth' | ||
|
||
import UserController from './controllers/UserController' | ||
import AuthController from './controllers/AuthController' | ||
|
||
const routes = Router() | ||
|
||
// Auth | ||
routes.post('/login', AuthController.login) | ||
|
||
// User | ||
routes.post('/register', UserController.register) | ||
|
||
// Auth Middleware | ||
routes.use(authMiddleware) | ||
|
||
// Post | ||
routes.post('/create/post', (req, res) => { | ||
return res.json({ post: 'Oi' }) | ||
}) | ||
|
||
export default routes |
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 { Request } from 'express' | ||
|
||
export interface AuthRequest extends Request { | ||
userId: string | ||
} |
Oops, something went wrong.