Skip to content

Commit

Permalink
Merge pull request #5 from Eccoar/165_login_user
Browse files Browse the repository at this point in the history
Solve #165 Autenticação de usuário
  • Loading branch information
devsalula authored May 8, 2021
2 parents 34b3978 + 1f6faff commit d8a76ed
Show file tree
Hide file tree
Showing 13 changed files with 869 additions and 95 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ test-report.xml
lint-report.html
.eslintcache

service-account.json
service-account.json
firebase-account.json
602 changes: 537 additions & 65 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"firebase": "^8.4.2",
"firebase-admin": "^9.6.0",
"morgan": "^1.10.0"
},
Expand Down
51 changes: 47 additions & 4 deletions src/controllers/ControllerUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default class ControllerUser {
'password',
'cpf',
'cep',
'adress',
'address',
];
const missingFields = CheckFields(fields, req.body);

Expand All @@ -52,19 +52,62 @@ export default class ControllerUser {
const uuidAuth = await this.userService.createUserAuth(userAuth);

const user = {
userAuthId: uuidAuth,
email: req.body.email,
lastName: req.body.lastName,
name: req.body.name,
cpf: req.body.cpf,
cep: req.body.cep,
adress: req.body.adress,
address: req.body.address,
} as User;

await this.userService.createUser(user);
const resolvePromise = [];

const createUser = this.userService.createUser(user, uuidAuth);
const sendMail = this.userService.signInAfterCreate(
req.body.email,
req.body.password,
);
resolvePromise.push(createUser, sendMail);

Promise.all(resolvePromise);

return res.sendStatus(201);
} catch (error) {
next(error);
}
}

async signin(
req: Request,
res: Response,
next: NextFunction,
): Promise<Response> {
try {
const { email, password } = req.body;

await this.userService.getUserAuthInstanceByEmail(email);

const jwt = await this.userService.signIn(email, password);

return res.status(200).json({ token: jwt });
} catch (error) {
next(error);
}
}

async authorizationHandler(
req: Request,
res: Response,
next: NextFunction,
): Promise<Response> {
try {
const userId = await this.userService.authorization(
req.headers.authorization,
);

return res.status(200).json(userId);
} catch (error) {
next(error);
}
}
}
7 changes: 6 additions & 1 deletion src/firebaseDB.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import * as admin from 'firebase-admin';
import firebase from 'firebase';
import { ServiceAccount } from 'firebase-admin';

import serviceAccount from '../service-account.json';
import firebaseAccount from '../firebase-account.json';

const firebaseCred = serviceAccount as ServiceAccount;
const firebaseApp = firebaseAccount;

admin.initializeApp({
credential: admin.credential.cert(firebaseCred),
});

export default admin;
firebase.initializeApp(firebaseApp);

export { admin, firebase };
14 changes: 14 additions & 0 deletions src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,18 @@ routers.post(
},
);

routers.post(
'/api/signin',
async (req: Request, res: Response, next: NextFunction) => {
controller.signin(req, res, next);
},
);

routers.get(
'/api/authorization',
async (req: Request, res: Response, next: NextFunction) => {
controller.authorizationHandler(req, res, next);
},
);

export default routers;
3 changes: 1 addition & 2 deletions src/schemas/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ export interface User {
email: string;
cpf: string;
cep: string;
adress: string;
userAuthId: string;
address: string;
}
2 changes: 2 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import express from 'express';
import cors from 'cors';
import routers from './routes';
import handleErrors from '@utils/ErrorHandler';
import morgan from 'morgan';

const app = express();
const PORT = process.env.APP_PORT || 5000;

app.use(express.json());
app.use(morgan('combined'));
app.use(cors());
app.use(routers);
app.use(handleErrors);
Expand Down
64 changes: 60 additions & 4 deletions src/services/UserService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { User } from '@schemas/User';
import { UserAuth } from '@schemas/UserAuth';
import admin from '../firebaseDB';
import {
BadRequest,
Forbidden,
NotFound,
Unauthorized,
} from '@utils/ErrorHandler';
import { admin, firebase } from '../firebaseDB';

export default class UserService {
async createUserAuth(userAuth: UserAuth): Promise<string> {
Expand All @@ -16,12 +22,62 @@ export default class UserService {
}
}

async createUser(user: User): Promise<string> {
async createUser(user: User, userId: string): Promise<void> {
try {
const resp = await admin.firestore().collection('users').add(user);
return resp.id;
await admin.firestore().collection('users').doc(userId).set(user);
} catch (error) {
throw new Error(error.message);
}
}

async getUserAuthInstanceByEmail(email: string): Promise<string> {
try {
const userRecord = await admin.auth().getUserByEmail(email);
return userRecord.uid;
} catch (error) {
throw new NotFound('User not found');
}
}

async signInAfterCreate(email: string, password: string): Promise<void> {
try {
const { user } = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
user.sendEmailVerification();
} catch (error) {
throw new BadRequest('Failed to send email');
}
}

async signIn(email: string, password: string): Promise<string> {
try {
const { user } = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
if (!user.emailVerified) {
user.sendEmailVerification();
throw new Error('User not verified');
}
return await user.getIdToken(true);
} catch (error) {
if (
error.message == 'User not verified' ||
error.message ==
'The password is invalid or the user does not have a password.'
)
throw new Unauthorized(error.message);

throw new BadRequest(error.message);
}
}

async authorization(jwt: string): Promise<string> {
try {
const user = await admin.auth().verifyIdToken(jwt);
return user.uid;
} catch (error) {
throw new Forbidden('Access denied');
}
}
}
10 changes: 9 additions & 1 deletion src/utils/ErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@ export class GeneralError {
if (this instanceof NotFound) {
return 404;
}
if (this instanceof Unauthorized) {
return 401;
}
if (this instanceof Forbidden) {
return 403;
}
}
}

export class BadRequest extends GeneralError {}
export class NotFound extends GeneralError {}
export class Unauthorized extends GeneralError {}
export class Forbidden extends GeneralError {}

const handleErrors = (
err: Error | GeneralError,
_req: Request,
resp: Response,
_next: NextFunction,
): Response => {
if (err instanceof BadRequest) {
if (err instanceof GeneralError) {
return resp.status(err.getCode()).json({
status: 'error',
message: err.message,
Expand Down
19 changes: 18 additions & 1 deletion test/ErrorHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { NextFunction, Response, Request } from 'express';
import handleErrors, { NotFound, BadRequest } from '@utils/ErrorHandler';
import handleErrors, {
NotFound,
BadRequest,
Unauthorized,
Forbidden,
} from '@utils/ErrorHandler';

const mockResponse = () => {
const res = {} as Response;
Expand All @@ -25,6 +30,18 @@ describe('Error handling', () => {
expect(error.message).toBe('error');
expect(error.getCode()).toBe(404);
});

test('Return Unauthorized', () => {
const error = new Unauthorized('error');
expect(error.message).toBe('error');
expect(error.getCode()).toBe(401);
});

test('Return Forbidden', () => {
const error = new Forbidden('error');
expect(error.message).toBe('error');
expect(error.getCode()).toBe(403);
});
});

describe('Error handler middleware', () => {
Expand Down
Loading

0 comments on commit d8a76ed

Please sign in to comment.