Skip to content

Commit dd92cc1

Browse files
author
DylanBulmer
committed
encrypt tokens
1 parent d3d89b3 commit dd92cc1

File tree

4 files changed

+92
-65
lines changed

4 files changed

+92
-65
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codrjs/core",
3-
"version": "1.0.3",
3+
"version": "1.0.4",
44
"description": "An open-sourced customizable annotation tool",
55
"main": "index.js",
66
"repository": "git@github.com:CodrJS/Core.git",
@@ -9,7 +9,7 @@
99
"private": false,
1010
"scripts": {
1111
"test": "jest --config jest.config.json --passWithNoTests --coverage",
12-
"build": "yarn clean && swc src -d lib && tsc && cp package.json lib/",
12+
"build": "yarn clean && swc src -d lib && tsc && cp package.json README.md lib/",
1313
"clean": "rm -rf ./lib",
1414
"format": "prettier --write \"src/**/*.(ts|js)\"",
1515
"lint": "eslint -c .eslintrc.json --ignore-path .eslintignore --ext .ts src",

src/classes/Mail/Template/Generic.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ export default class GenericTemplate<T extends string>
9292
*/
9393
private wrapperHTML({ content }: { content: string }) {
9494
return `<body
95-
style="background: #f3f4f6; padding: 2em; font-size:16px; font-family:source-sans-pro, Roboto, sans-serif;"
95+
style="background: #f3f4f6; padding: 2em 1em; font-size:16px; font-family:source-sans-pro, Roboto, sans-serif;"
9696
>
9797
<div style=" text-align: center;">
9898
<div
99-
style="background: white; padding: 2em; border-radius: 0.5em; max-width: 500px; text-align: left; margin: auto;"
99+
style="background: white; padding: 2em; border-radius: 0.5em; max-width: 65ch; text-align: left; margin: auto;"
100100
>
101101
${content}
102-
<br />
102+
<br /><br />
103103
Best,<br />
104104
Your Codr Team<br />
105105
support@codrjs.com<br />
@@ -114,7 +114,7 @@ export default class GenericTemplate<T extends string>
114114
*
115115
* <img
116116
* src="cid:logo"
117-
* alt="TrustedRentr Logo"
117+
* alt="Codr Logo"
118118
* style="display: block; width: 100%; height: 48px; margin-bottom: 2em; object-fit: contain;"
119119
* />
120120
*/

src/models/User.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,13 @@
11
import { EmailRegex } from "../classes/Email";
22
import { Schema, model } from "mongoose";
3-
import { v4 as uuidv4 } from "uuid";
4-
// import { sign } from "jsonwebtoken";
53

64
const UserProvider = new Schema({
75
photo: { type: String },
86
phone: { type: String },
9-
email: { type: String },
7+
email: { type: String, required: true },
108
uid: { type: String, required: [true, "Provider's user id is required"] },
119
});
1210

13-
const UserAccessToken = new Schema({
14-
value: {
15-
type: String,
16-
required: false,
17-
unique: false,
18-
default: uuidv4,
19-
},
20-
createdAt: {
21-
type: String,
22-
required: false,
23-
unique: false,
24-
default: new Date().toISOString(),
25-
},
26-
used: {
27-
type: Boolean,
28-
default: false,
29-
},
30-
});
31-
3211
/* UserSchema will correspond to a collection in your MongoDB database. */
3312
const UserSchema = new Schema(
3413
{
@@ -52,7 +31,8 @@ const UserSchema = new Schema(
5231
unique: true,
5332
index: true,
5433
},
55-
accessToken: UserAccessToken,
34+
accessToken: { type: String },
35+
refreshToken: { type: String },
5636
providers: {
5737
type: [UserProvider],
5838
},

src/services/auth.ts

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ import User from "../models/User";
1111
import Response from "classes/Response";
1212
import { generateToken } from "classes/JWT";
1313
import Error from "../classes/Error";
14+
import crypto from "crypto";
15+
16+
const md5 = (text: string) => {
17+
return crypto.createHash("md5").update(text).digest();
18+
};
19+
20+
interface IAccessCode {
21+
email: string;
22+
token: string;
23+
}
1424

1525
class Authentication {
1626
private app: App;
@@ -22,26 +32,44 @@ class Authentication {
2232

2333
// }
2434

25-
async signinWithEmail({
26-
email,
27-
accessToken,
28-
}: {
29-
email: Email;
30-
accessToken?: string;
31-
}): Promise<Response<undefined | { token: string }>> {
35+
// for encrypting AccessCode object into a string
36+
private encrypt(text: string) {
37+
let secretKey = md5(process.env.SECRET as string);
38+
secretKey = Buffer.concat([secretKey, secretKey.subarray(0, 8)]);
39+
const cipher = crypto.createCipheriv("des-ede3", secretKey, "");
40+
const encrypted = cipher.update(text, "utf8", "hex");
41+
return encrypted + cipher.final("hex");
42+
}
43+
44+
// for decrypting AccessCode string into an object
45+
private decrypt(text: string) {
46+
let secretKey = md5(process.env.SECRET as string);
47+
secretKey = Buffer.concat([secretKey, secretKey.subarray(0, 8)]);
48+
const decipher = crypto.createDecipheriv("des-ede3", secretKey, "");
49+
let decrypted = decipher.update(text, "utf8", "hex");
50+
decrypted += decipher.final();
51+
return decrypted;
52+
}
53+
54+
async signinWithEmail(
55+
accessCode: string,
56+
): Promise<Response<undefined | { token: string }>> {
57+
const { email, token: accessToken } = JSON.parse(
58+
this.decrypt(accessCode),
59+
) as IAccessCode;
3260
if (!email)
3361
throw new Error({
3462
status: 400,
3563
message: "No email address was received",
3664
});
37-
if (!email.isValid)
65+
if (!new Email(email).isValid)
3866
throw new Error({
3967
status: 400,
4068
message: "Invalid email address provided",
4169
});
4270

4371
try {
44-
const user = await User.findOne({ email: email.email });
72+
const user = await User.findOne({ email });
4573
if (!user) {
4674
// is user cannot be found, then they are not allowed in.
4775
throw new Error({
@@ -53,28 +81,25 @@ class Authentication {
5381
try {
5482
// init access token
5583
const accessToken = uuidv4();
56-
await User.findOneAndUpdate(
57-
{ email: email.email },
58-
{
59-
accessToken: {
84+
await user.update({
85+
accessToken: this.encrypt(
86+
JSON.stringify({
6087
value: accessToken,
6188
createdAt: new Date().toISOString(),
62-
used: false,
63-
},
64-
},
65-
);
89+
exprired: false,
90+
}),
91+
),
92+
});
6693

6794
// send email with magic link
6895
const link =
6996
process.env["DOMAIN"] +
70-
"/auth/email/verify?email=" +
71-
email.email +
72-
"&token=" +
73-
accessToken;
97+
"/auth/email/verify?token=" +
98+
this.encrypt(JSON.stringify({ email: email, token: accessToken }));
7499
const template = new SigninTemplate();
75100
await Mail.send(await template.html({ link }), {
76101
...template.config,
77-
to: email.email,
102+
to: email,
78103
});
79104
return new Response({
80105
message: "An email has been sent to your inbox.",
@@ -85,20 +110,42 @@ class Authentication {
85110
message: e?.message || "An unknown error occured",
86111
});
87112
}
88-
} else if (
89-
user.accessToken?.value == accessToken &&
90-
new Date().getTime() <
91-
new Date(user.accessToken?.createdAt).getTime() + 5 * 60 * 1000
92-
) {
93-
const token = generateToken(user.toJSON());
94-
await User.findOneAndUpdate(
95-
{ email: email.email },
96-
{ "accessToken.used": true },
97-
);
98-
return new Response<{ token: string }>({
99-
message: `Welcome, ${user.name}`,
100-
details: { token },
101-
});
113+
} else if (user.accessToken) {
114+
const accessCode = JSON.parse(this.decrypt(user.accessToken)) as {
115+
value: string;
116+
createdAt: string;
117+
expired: boolean;
118+
};
119+
if (
120+
accessCode?.value == accessToken &&
121+
new Date().getTime() <
122+
new Date(accessCode?.createdAt).getTime() + 5 * 60 * 1000
123+
) {
124+
// generate JWT token
125+
const token = generateToken(user.toJSON());
126+
127+
// update user
128+
await user.updateOne({
129+
accessToken: this.encrypt(
130+
JSON.stringify({ ...accessCode, expired: true }),
131+
),
132+
refreshToken: this.encrypt(
133+
JSON.stringify({
134+
value: uuidv4(),
135+
createdAt: new Date().toISOString(),
136+
expired: false,
137+
}),
138+
),
139+
});
140+
return new Response<{ token: string }>({
141+
message: `Login successful.`,
142+
details: { token },
143+
});
144+
} else
145+
throw new Error({
146+
status: 500,
147+
message: "Login link expired or is invalid.",
148+
});
102149
} else
103150
throw new Error({
104151
status: 500,

0 commit comments

Comments
 (0)