Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
Update tokens format (#107)
Browse files Browse the repository at this point in the history
* Update tokens format

* Rename interface

* Diffrent keys for each tokens & cleanup

* Fix error handling & response format

* Update src/services/auth.service.ts
  • Loading branch information
celian-rib authored Jul 29, 2022
1 parent 7b4f643 commit ca7750c
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 50 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ NODE_ENV=<production|development>
API_PORT=3000
SOCKET_API=4000
SECRET_KEY=<secret>
ACCESS_SECRET_KEY=<secret>
REFRESH_SECRET_KEY=<secret>
SECRET_ENCRYPTION_KEY=<encryption key>
CHAT_ENCRYPTION_SECRET_KEY=<encryption key>
#################-> POSTGRES
Expand Down
5 changes: 2 additions & 3 deletions src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export async function logIn(req: Request, res: Response, next: NextFunction): Pr
utils.throwIfNotString(uid);

const authData = await authService.login(uid);

res.status(200).json(authData);
} catch (error) {
next(error);
Expand All @@ -53,8 +52,8 @@ export async function refreshAuthToken(req: Request, res: Response, next: NextFu
const { refreshToken } = req.body;
utils.throwIfNotString(refreshToken);

const { token } = await authService.refreshAuthToken(refreshToken);
res.status(200).json(token);
const tokens = await authService.refreshAuthToken(refreshToken);
res.status(200).json(tokens);
} catch (error) {
next(error);
}
Expand Down
7 changes: 4 additions & 3 deletions src/interfaces/auth.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ export interface DataStoredInToken {
userId: number;
}

export interface TokenData {
token: string;
expiresIn: number;
export interface UserTokens {
accessToken: string;
refreshToken: string;
expires: number;
}

export interface AuthenticatedRequest extends Request {
Expand Down
4 changes: 2 additions & 2 deletions src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const authMiddleware = async (req: AuthenticatedRequest | AuthenticatedSocket, r
if (isSocket) {
authorization = req.handshake.headers.authorization;
} else {
authorization = req.header('Authorization');
authorization = req.header('Authorization').replace('Bearer ', '');
}

if (authorization == null) {
if (!isSocket) errorMiddleware(HttpException.INVALID_TOKEN, req, res, next);
return;
}

const secretKey = process.env.SECRET_KEY;
const secretKey = process.env.ACCESS_SECRET_KEY;
const { userId } = (await verify(authorization, secretKey)) as DataStoredInToken;

const user = await client.user.findUnique({ where: { id: userId } });
Expand Down
3 changes: 3 additions & 0 deletions src/middlewares/error.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { NextFunction, Request, Response } from 'express';
import { HttpException } from '@exceptions/HttpException';
import { JsonWebTokenError } from 'jsonwebtoken';

const errorMiddleware = (error: any, req: Request, res: Response, next: NextFunction) => {
try {
if (error instanceof HttpException) {
res.status(error.status).json(error.message);
} else if (error instanceof JsonWebTokenError) {
res.status(403).json(error.message);
} else {
console.error(`[${req.method}] ${req.path} >> SERVER SIDE ERROR`, error);
res.status(500).json('Something went wrong');
Expand Down
46 changes: 8 additions & 38 deletions src/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import client from '@/prisma/client';
import jwt from 'jsonwebtoken';
import { User } from '@prisma/client';
import { HttpException } from '@exceptions/HttpException';
import { DataStoredInToken, TokenData } from '@interfaces/auth.interface';
import { DataStoredInToken, UserTokens } from '@interfaces/auth.interface';
import { createUserToken, displayNameToUsername } from '@/utils/user.utils';

const ONE_MONTH_IN_SECONDS = 2592000;

const ONE_DAY_IN_SECONDS = 86400;

export async function register(uid, displayName): Promise<User> {
export async function register(uid: string, displayName: string): Promise<User> {
const findUser: User = await client.user.findUnique({ where: { uid: uid } });
if (findUser) throw new HttpException(409, `This uid is already registered`);

Expand All @@ -17,44 +14,17 @@ export async function register(uid, displayName): Promise<User> {
return createUserData;
}

export async function displayNameToUsername(displayName: string): Promise<string> {
const username: string = displayName.toLowerCase().trim().normalize('NFD');
const cleanedUsername = username
.replace(/[\u0300-\u036f]/g, '')
.replace(/\s/g, '_')
.replace(/[^\w\s]/gi, '');

let count = 0;
let uniqueUsername = cleanedUsername;
while (await client.user.findUnique({ where: { username: uniqueUsername } })) {
uniqueUsername = username + count.toString();
count++;
}
return uniqueUsername;
}

export async function login(uid: string) {
export async function login(uid: string): Promise<UserTokens & { user: User }> {
const user: User = await client.user.findUnique({ where: { uid } });
if (!user) throw new HttpException(409, 'No user found with this uid');

const authTokenData = createUserToken(user, ONE_DAY_IN_SECONDS);
const refreshTokenData = createUserToken(user, ONE_MONTH_IN_SECONDS);

return { authTokenData, refreshTokenData, user };
return { ...createUserToken(user), user };
}

export async function refreshAuthToken(refreshToken: string) {
const secretKey = process.env.SECRET_KEY;
export async function refreshAuthToken(refreshToken: string): Promise<UserTokens> {
const secretKey = process.env.REFRESH_SECRET_KEY;
const { userId } = (await jwt.verify(refreshToken, secretKey)) as DataStoredInToken;

const user = await client.user.findUnique({ where: { id: userId } });
const token = createUserToken(user, ONE_DAY_IN_SECONDS);

return { token };
}

function createUserToken(user: User, expiresIn: number): TokenData {
const dataStoredInToken: DataStoredInToken = { userId: user.id };
const secretKey = process.env.SECRET_KEY;
return { expiresIn, token: jwt.sign(dataStoredInToken, secretKey, { expiresIn }) };
return createUserToken(user);
}
4 changes: 2 additions & 2 deletions src/utils/encrypt.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import crypto from 'crypto-js';

export const encryptMessage = (text: string) => {
return crypto.AES.encrypt(text, process.env.SECRET_ENCRYPTION_KEY).toString();
return crypto.AES.encrypt(text, process.env.CHAT_ENCRYPTION_SECRET_KEY).toString();
};

export const decryptMessage = (text: string) => {
return crypto.AES.decrypt(text, process.env.SECRET_ENCRYPTION_KEY).toString(crypto.enc.Utf8);
return crypto.AES.decrypt(text, process.env.CHAT_ENCRYPTION_SECRET_KEY).toString(crypto.enc.Utf8);
};
39 changes: 39 additions & 0 deletions src/utils/user.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import jwt from 'jsonwebtoken';
import client from '@/prisma/client';

import { DataStoredInToken, UserTokens } from '@/interfaces/auth.interface';
import { User } from '@prisma/client';

const ONE_DAY_IN_SECONDS = 24 * 60 * 60;
const ONE_MONTH_IN_SECONDS = 30 * 24 * 60 * 60;

export async function displayNameToUsername(displayName: string): Promise<string> {
const username: string = displayName.toLowerCase().trim().normalize('NFD');
const cleanedUsername = username
.replace(/[\u0300-\u036f]/g, '')
.replace(/\s/g, '_')
.replace(/[^\w\s]/gi, '');

let count = 0;
let uniqueUsername = cleanedUsername;
while (await client.user.findUnique({ where: { username: uniqueUsername } })) {
uniqueUsername = username + count.toString();
count++;
}
return uniqueUsername;
}

export function createUserToken(user: User): UserTokens {
const dataStoredInToken: DataStoredInToken = { userId: user.id };
const accessKey = process.env.ACCESS_SECRET_KEY;
const refreshKey = process.env.REFRESH_SECRET_KEY;

const accessToken = jwt.sign(dataStoredInToken, accessKey, { expiresIn: ONE_DAY_IN_SECONDS });
const refreshToken = jwt.sign(dataStoredInToken, refreshKey, { expiresIn: ONE_MONTH_IN_SECONDS });

return {
accessToken,
refreshToken,
expires: ONE_DAY_IN_SECONDS,
};
}

0 comments on commit ca7750c

Please sign in to comment.