Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit bb27ec7

Browse files
✨ Support for refresh tokens
1 parent f1b93fe commit bb27ec7

File tree

6 files changed

+70
-20
lines changed

6 files changed

+70
-20
lines changed

src/helpers/jwt.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { sign, verify } from "jsonwebtoken";
22
import { JWT_ISSUER, JWT_SECRET } from "../config";
33
import { User } from "../interfaces/tables/user";
4+
import { Tokens } from "../interfaces/enum";
45

56
export const generateToken = (
67
payload: string | object | Buffer,
@@ -33,9 +34,13 @@ export const verifyToken = (token: string, subject: string) =>
3334
});
3435

3536
export const emailVerificationToken = (id: number) =>
36-
generateToken({ id }, "7d", "email-verify");
37+
generateToken({ id }, "7d", Tokens.EMAIL_VERIFY);
3738

3839
export const passwordResetToken = (id: number) =>
39-
generateToken({ id }, "1d", "password-reset");
40+
generateToken({ id }, "1d", Tokens.PASSWORD_RESET);
4041

41-
export const loginToken = (user: User) => generateToken(user, "1d", "auth");
42+
export const loginToken = (user: User) =>
43+
generateToken(user, "1d", Tokens.LOGIN);
44+
45+
export const refreshToken = (id: number) =>
46+
generateToken({ id }, "30d", Tokens.REFRESH);

src/helpers/middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Request, Response, NextFunction } from "express";
22
import { safeError, sendError } from "./errors";
33
import { verifyToken } from "./jwt";
4-
import { ErrorCode } from "../interfaces/enum";
4+
import { ErrorCode, Tokens } from "../interfaces/enum";
55

66
export const errorHandler = (
77
error: any,
@@ -41,7 +41,7 @@ export const authHandler = async (
4141
}
4242
if (token.startsWith("Bearer ")) token = token.replace("Bearer ", "");
4343
try {
44-
res.locals.token = await verifyToken(token, "auth");
44+
res.locals.token = await verifyToken(token, Tokens.LOGIN);
4545
next();
4646
} catch (e) {
4747
const error = sendError(ErrorCode.INVALID_TOKEN);

src/interfaces/enum.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export enum EventType {
2323
USER_CREATED = "user.created",
2424
USER_UPDATED = "user.updated",
2525
USER_DELETED = "user.deleted",
26+
AUTH_REFRESH = "auth.refresh",
2627
AUTH_LOGIN = "auth.login",
2728
AUTH_LOGIN_BACKUP_CODE = "auth.login_backupCode",
2829
AUTH_LOGIN_GOOGLE = "auth.login_google",
@@ -54,3 +55,10 @@ export enum Templates {
5455
EMAIL_VERIFY = "email-verify",
5556
PASSWORD_RESET = "password-reset"
5657
}
58+
59+
export enum Tokens {
60+
LOGIN = "auth",
61+
REFRESH = "refresh",
62+
PASSWORD_RESET = "password-reset",
63+
EMAIL_VERIFY = "email-verify"
64+
}

src/rest/auth.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,52 @@
11
import { User } from "../interfaces/tables/user";
22
import { createUser, updateUser, getUserByEmail, getUser } from "../crud/user";
33
import { InsertResult } from "../interfaces/mysql";
4-
import {
5-
createEmail,
6-
updateEmail,
7-
getEmail,
8-
sendEmailVerification
9-
} from "../crud/email";
4+
import { createEmail, updateEmail, getEmail } from "../crud/email";
105
import { mail } from "../helpers/mail";
11-
import { verifyToken, loginToken, passwordResetToken } from "../helpers/jwt";
6+
import {
7+
verifyToken,
8+
loginToken,
9+
passwordResetToken,
10+
refreshToken
11+
} from "../helpers/jwt";
1212
import { KeyValue, Locals } from "../interfaces/general";
1313
import { createEvent } from "../crud/event";
1414
import {
1515
EventType,
1616
ErrorCode,
1717
MembershipRole,
18-
Templates
18+
Templates,
19+
Tokens
1920
} from "../interfaces/enum";
2021
import { compare, hash } from "bcrypt";
2122
import { deleteSensitiveInfoUser } from "../helpers/utils";
2223
import { createMembership } from "../crud/membership";
2324

25+
export const validateRefreshToken = async (token: string, locals: Locals) => {
26+
const data = <User>await verifyToken(token, Tokens.REFRESH);
27+
if (!data.id) throw new Error(ErrorCode.USER_NOT_FOUND);
28+
const user = await getUser(data.id);
29+
await createEvent(
30+
{
31+
userId: user.id,
32+
type: EventType.AUTH_REFRESH
33+
},
34+
locals
35+
);
36+
return {
37+
token: await loginToken(deleteSensitiveInfoUser(user)),
38+
refresh: await refreshToken(data.id)
39+
};
40+
};
41+
2442
export const login = async (
2543
email: string,
2644
password: string,
2745
locals: Locals
2846
) => {
2947
const user = await getUserByEmail(email, true);
3048
if (!user.password) throw new Error(ErrorCode.MISSING_PASSWORD);
49+
if (!user.id) throw new Error(ErrorCode.USER_NOT_FOUND);
3150
const correctPassword = await compare(password, user.password);
3251
if (correctPassword) {
3352
await createEvent(
@@ -38,7 +57,10 @@ export const login = async (
3857
},
3958
locals
4059
);
41-
return await loginToken(deleteSensitiveInfoUser(user));
60+
return {
61+
token: await loginToken(deleteSensitiveInfoUser(user)),
62+
refresh: await refreshToken(user.id)
63+
};
4264
}
4365
throw new Error(ErrorCode.INVALID_LOGIN);
4466
};
@@ -88,7 +110,7 @@ export const sendPasswordReset = async (email: string, locals: Locals) => {
88110
};
89111

90112
export const verifyEmail = async (token: string, locals: Locals) => {
91-
const emailId = (<KeyValue>await verifyToken(token, "email-verify")).id;
113+
const emailId = (<KeyValue>await verifyToken(token, Tokens.EMAIL_VERIFY)).id;
92114
const email = await getEmail(emailId);
93115
await createEvent(
94116
{
@@ -106,7 +128,7 @@ export const updatePassword = async (
106128
password: string,
107129
locals: Locals
108130
) => {
109-
const userId = (<KeyValue>await verifyToken(token, "password-reset")).id;
131+
const userId = (<KeyValue>await verifyToken(token, Tokens.PASSWORD_RESET)).id;
110132
const hashedPassword = await hash(password || "", 8);
111133
await updateUser(userId, { password: hashedPassword });
112134
await createEvent(

src/routes/auth.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
sendPasswordReset,
55
login,
66
updatePassword,
7-
register
7+
register,
8+
validateRefreshToken
89
} from "../rest/auth";
910
import { verifyToken } from "../helpers/jwt";
1011

@@ -26,13 +27,25 @@ export const routeAuthLogin = async (req: Request, res: Response) => {
2627
const password = req.body.password;
2728
if (!email || !password) throw new Error(ErrorCode.MISSING_FIELD);
2829
try {
29-
const token = await login(email, password, res.locals);
30-
res.json({ token });
30+
const tokens = await login(email, password, res.locals);
31+
res.json(tokens);
3132
} catch (error) {
3233
throw new Error(ErrorCode.INVALID_LOGIN);
3334
}
3435
};
3536

37+
export const routeAuthRefresh = async (req: Request, res: Response) => {
38+
const token =
39+
req.body.token || (req.get("Authorization") || "").replace("Bearer ", "");
40+
if (!token) throw new Error(ErrorCode.MISSING_TOKEN);
41+
try {
42+
const tokens = await validateRefreshToken(token, res.locals);
43+
res.json(tokens);
44+
} catch (error) {
45+
throw new Error(ErrorCode.INVALID_TOKEN);
46+
}
47+
};
48+
3649
export const routeAuthRegister = async (req: Request, res: Response) => {
3750
const email = req.body.email;
3851
const user = req.body;

src/routes/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import {
1919
routeAuthLogin,
2020
routeAuthResetPasswordRequest,
2121
routeAuthResetPasswordRecover,
22-
routeAuthRegister
22+
routeAuthRegister,
23+
routeAuthRefresh
2324
} from "./auth";
2425
import { routeMembershipGet, routeMembershipCreate } from "./membership";
2526

@@ -37,6 +38,7 @@ export const router = (app: Application) => {
3738

3839
const routesAuth = (app: Application) => {
3940
app.post("/auth/login", asyncHandler(routeAuthLogin));
41+
app.post("/auth/refresh", asyncHandler(routeAuthRefresh));
4042
app.post("/auth/verify-token", asyncHandler(routeAuthVerifyToken));
4143
app.post(
4244
"/auth/reset-password/request",

0 commit comments

Comments
 (0)