Skip to content

Commit 6db8a8b

Browse files
author
DylanBulmer
committed
cleanup auth
1 parent ff325a4 commit 6db8a8b

File tree

7 files changed

+233
-140
lines changed

7 files changed

+233
-140
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# @codrjs/core
22

3+
[![npm version](https://badge.fury.io/js/@codrjs%2Fcore.svg)](https://www.npmjs.com/package/@codrjs/core)
34
[![CodeQL](https://github.com/CodrJS/Core/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/CodrJS/Core/actions/workflows/codeql.yml)
45

56
This is an open-sourced customizable annotation tool for researchers and industry.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codrjs/core",
3-
"version": "1.0.4-patch1",
3+
"version": "1.0.5",
44
"description": "An open-sourced customizable annotation tool",
55
"main": "index.js",
66
"repository": "[email protected]:CodrJS/Core.git",

src/classes/AccessToken.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { v4 } from "uuid";
2+
import crypto from "crypto";
3+
import Error from "./Error";
4+
5+
const md5 = (text: string) => {
6+
return crypto.createHash("md5").update(text).digest();
7+
};
8+
9+
interface IAccessToken {
10+
uuid: typeof v4;
11+
createdAt: string;
12+
expired: boolean;
13+
}
14+
15+
export function isAccessToken(obj: any): obj is IAccessToken {
16+
return "uuid" in obj;
17+
}
18+
19+
// for encrypting AccessCode object into a string
20+
export function encrypt(text: string) {
21+
let secretKey = md5(process.env.SECRET as string);
22+
secretKey = Buffer.concat([secretKey, secretKey.subarray(0, 8)]);
23+
const cipher = crypto.createCipheriv("des-ede3", secretKey, "");
24+
const encrypted = cipher.update(text, "utf8", "hex");
25+
return encrypted + cipher.final("hex");
26+
}
27+
28+
// for decrypting AccessCode string into an object
29+
export function decrypt<T>(text: string) {
30+
let secretKey = md5(process.env.SECRET as string);
31+
secretKey = Buffer.concat([secretKey, secretKey.subarray(0, 8)]);
32+
const decipher = crypto.createDecipheriv("des-ede3", secretKey, "");
33+
let decrypted = decipher.update(text, "hex");
34+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
35+
// @ts-ignore
36+
decrypted += decipher.final();
37+
return JSON.parse(decrypted as unknown as string) as T;
38+
}
39+
40+
class AccessToken {
41+
private uuid: typeof v4;
42+
private createdAt: string;
43+
private expired: boolean;
44+
45+
public constructor(uuid: typeof v4);
46+
public constructor(encoded: string);
47+
public constructor(token: IAccessToken);
48+
public constructor(arg: typeof v4 | string | IAccessToken) {
49+
if (typeof arg === "string" || typeof arg === typeof v4) {
50+
if (
51+
(<string>arg).match(
52+
/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
53+
)
54+
) {
55+
this.uuid = <typeof v4>arg;
56+
this.createdAt = new Date().toISOString();
57+
this.expired = false;
58+
} else {
59+
const code = decrypt<AccessToken>(<string>arg);
60+
this.uuid = code.uuid;
61+
this.createdAt = code.createdAt;
62+
this.expired = code.expired;
63+
}
64+
} else if (isAccessToken(arg)) {
65+
this.uuid = arg.uuid;
66+
this.createdAt = arg.createdAt;
67+
this.expired = arg.expired;
68+
}
69+
70+
throw new Error({ status: 400, message: "Invailid input given" });
71+
}
72+
73+
use() {
74+
this.expired = true;
75+
}
76+
77+
toJSON() {
78+
return {
79+
value: this.uuid,
80+
createdAt: this.createdAt,
81+
expired: this.expired,
82+
};
83+
}
84+
85+
encode() {
86+
return encrypt(JSON.stringify(this.toJSON()));
87+
}
88+
}
89+
90+
export default AccessToken;

src/classes/JWT.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,65 @@
1-
import jwt, { Algorithm } from "jsonwebtoken";
1+
import jwt, { Algorithm, JwtPayload } from "jsonwebtoken";
2+
import type { IUser } from "../models/User";
23
import Error from "./Error";
34

5+
const isPayload = function isPayload(obj: any): obj is JwtPayload {
6+
return "iss" in obj;
7+
};
8+
49
export function verifyToken(token: string) {
510
const bearerRegex = /^Bearer\s/;
611

7-
if (token && bearerRegex.test(token)) {
8-
const newToken = token.replace(bearerRegex, "");
9-
jwt.verify(
10-
newToken,
11-
"secretKey",
12-
{
12+
if (token) {
13+
if (bearerRegex.test(token)) {
14+
token = token.replace(bearerRegex, "");
15+
}
16+
17+
try {
18+
const decoded = jwt.verify(token, <string>process.env.JWT_SECRET, {
1319
issuer: process.env.JWT_ISSUER,
14-
},
15-
(error, decoded) => {
16-
if (error === null && decoded) {
17-
return true;
18-
}
19-
return false;
20-
},
21-
);
20+
});
21+
return decoded;
22+
} catch (e) {
23+
throw new Error({ status: 401, message: "Could not verify the JWT." });
24+
}
25+
} else {
26+
throw new Error({ status: 400, message: "JWT is missing." });
27+
}
28+
}
29+
30+
/**
31+
* @TODO Finish writing the refresh function.
32+
*/
33+
export function refreshToken(token: string) {
34+
const bearerRegex = /^Bearer\s/;
35+
36+
if (token) {
37+
if (bearerRegex.test(token)) {
38+
token = token.replace(bearerRegex, "");
39+
}
40+
41+
try {
42+
const decoded = jwt.decode(token);
43+
44+
if (isPayload(decoded)) {
45+
return decoded;
46+
}
47+
48+
throw new Error({ status: 401, message: "Could not decode the JWT." });
49+
} catch (e) {
50+
throw new Error({ status: 401, message: "Could not decode the JWT." });
51+
}
2252
} else {
23-
return false;
53+
throw new Error({ status: 400, message: "JWT is missing." });
2454
}
2555
}
2656

27-
export function generateToken(payload: jwt.JwtPayload) {
57+
export function generateToken(payload: IUser & { _id: string }) {
2858
try {
2959
const signOpts: jwt.SignOptions = {
3060
issuer: process.env.JWT_ISSUER,
3161
algorithm: <Algorithm>process.env.JWT_ALGORITHM,
62+
subject: payload._id,
3263
};
3364
return jwt.sign(payload, <string>process.env.JWT_SECRET, signOpts);
3465
} catch (err) {

src/classes/Mail/Template/Generic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default class GenericTemplate<T extends string>
102102
<br /><br />
103103
Best,<br />
104104
Your Codr Team<br />
105-
105+
106106
</div>
107107
</div>
108108
</body>`.replace(/[\n]*/g, "");

src/models/User.ts

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
11
import { EmailRegex } from "../classes/Email";
22
import { Schema, model } from "mongoose";
33

4-
const UserProvider = new Schema({
4+
interface IUserProvider {
5+
photo?: string;
6+
phone?: string;
7+
email: string;
8+
uid: string;
9+
}
10+
11+
export interface IUser {
12+
name?: string;
13+
email: string;
14+
accessToken: string;
15+
refreshToken: string;
16+
providers?: IUserProvider;
17+
isAdmin: boolean;
18+
}
19+
20+
const UserProvider = new Schema<IUserProvider>({
521
photo: { type: String },
622
phone: { type: String },
723
email: { type: String, required: true },
824
uid: { type: String, required: [true, "Provider's user id is required"] },
925
});
1026

1127
/* UserSchema will correspond to a collection in your MongoDB database. */
12-
const UserSchema = new Schema(
28+
const UserSchema = new Schema<IUser>(
1329
{
1430
name: {
1531
type: String,
@@ -43,60 +59,6 @@ const UserSchema = new Schema(
4359
},
4460
{
4561
timestamps: true,
46-
virtuals: {
47-
firstName: {
48-
get() {
49-
const displayNameRegex =
50-
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
51-
const result = displayNameRegex.exec(<string>this.name);
52-
53-
// set name data
54-
if (result?.groups) {
55-
const { firstName } = result.groups;
56-
return firstName;
57-
}
58-
},
59-
},
60-
lastName: {
61-
get() {
62-
const displayNameRegex =
63-
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
64-
const result = displayNameRegex.exec(<string>this.name);
65-
66-
// set name data
67-
if (result?.groups) {
68-
const { lastName } = result.groups;
69-
return lastName;
70-
}
71-
},
72-
},
73-
preferredName: {
74-
get() {
75-
const displayNameRegex =
76-
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
77-
const result = displayNameRegex.exec(<string>this.name);
78-
79-
// set name data
80-
if (result?.groups) {
81-
const { preferredName } = result.groups;
82-
return preferredName;
83-
}
84-
},
85-
},
86-
fullname: {
87-
get() {
88-
const displayNameRegex =
89-
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
90-
const result = displayNameRegex.exec(<string>this.name);
91-
92-
// set name data
93-
if (result?.groups) {
94-
const { firstName, lastName } = result.groups;
95-
return firstName + " " + lastName;
96-
}
97-
},
98-
},
99-
},
10062
methods: {
10163
// generateJWT: {
10264
// get() {
@@ -117,6 +79,54 @@ const UserSchema = new Schema(
11779
},
11880
);
11981

82+
UserSchema.virtual("firstName").get(function get() {
83+
const displayNameRegex =
84+
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
85+
const result = displayNameRegex.exec(<string>this.name);
86+
87+
// set name data
88+
if (result?.groups) {
89+
const { firstName } = result.groups;
90+
return firstName;
91+
}
92+
});
93+
94+
UserSchema.virtual("lastName").get(function get() {
95+
const displayNameRegex =
96+
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
97+
const result = displayNameRegex.exec(<string>this.name);
98+
99+
// set name data
100+
if (result?.groups) {
101+
const { lastName } = result.groups;
102+
return lastName;
103+
}
104+
});
105+
106+
UserSchema.virtual("perferredName").get(function get() {
107+
const displayNameRegex =
108+
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
109+
const result = displayNameRegex.exec(<string>this.name);
110+
111+
// set name data
112+
if (result?.groups) {
113+
const { preferredName } = result.groups;
114+
return preferredName;
115+
}
116+
});
117+
118+
UserSchema.virtual("fullName").get(function get() {
119+
const displayNameRegex =
120+
/(?<lastName>\w+),(?<firstName>\w+)<?(?<preferredName>\w+)?>?/gm;
121+
const result = displayNameRegex.exec(<string>this.name);
122+
123+
// set name data
124+
if (result?.groups) {
125+
const { firstName, lastName } = result.groups;
126+
return firstName + " " + lastName;
127+
}
128+
});
129+
120130
// exports User model.
121-
const User = model("User", UserSchema);
131+
const User = model<IUser>("User", UserSchema);
122132
export default User;

0 commit comments

Comments
 (0)