Skip to content

Commit

Permalink
Added JWT functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
AydanPirani committed Aug 17, 2023
1 parent 0d2fd2c commit d71e9e1
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 116 deletions.
19 changes: 11 additions & 8 deletions src/services/auth/auth-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ export async function getJwtPayloadFromProfile(provider: string, data: ProfileDa
if (userRoles.length) {
payload.roles = userRoles;
}
}).catch((error: Error) => {
console.log("get function failed!");
}).catch((error: string) => {
console.log("get function failed inside getRoles!");
console.log(error);
});

// No roles found for user -> initialize them
if (!payload.roles.length) {
await initializeRoles(userId, provider as Provider, email).then((newRoles: Role[]) => {
payload.roles = newRoles;
}).catch((error: Error) => {
console.log("get function failed!");
}).catch((error: string) => {
console.log("get function failed inside initializeRoles!");
console.log(error);
});
}
Expand Down Expand Up @@ -117,7 +117,8 @@ export function generateJwtToken(payload?: JwtPayload, expiration?: string): str

// // Appends an expiry field to the JWT token
const options: SignOptions = { };
payload.exp = ms(expiration ?? "7d");
payload.exp = Math.floor(Date.now() + ms(expiration ?? "7d")) / Constants.MILLISECONDS_PER_SECOND;
console.log(payload.exp);

// Generate a token, and return it
const token: string = jsonwebtoken.sign(payload, secret, options);
Expand Down Expand Up @@ -175,6 +176,7 @@ export async function getAuthInfo(id: string): Promise<RolesSchema> {

// Null check to ensure that we're not returning anything null
if (!info) {
console.log("rejecting...");
return Promise.reject("User ID does not exist in the database!");
}

Expand All @@ -186,14 +188,15 @@ export async function getAuthInfo(id: string): Promise<RolesSchema> {


export async function getRoles(id: string): Promise<Role[]> {
let roles: Role[] | undefined;
// Call helper function to get auth info, and just return data from there
getAuthInfo(id).then((info: RolesSchema) => {
return info.roles as Role[];
await getAuthInfo(id).then((user: RolesSchema) => {
roles = user.roles as Role[];
}).catch((error: string) => {
return Promise.reject(error);
});

return Promise.reject("Unknown error!");
return roles ?? Promise.reject("Unknown error!");
}


Expand Down
171 changes: 76 additions & 95 deletions src/services/auth/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { Profile as GoogleProfile, Strategy as GoogleStrategy } from "passport-g

import { Role } from "../../models.js";
import Constants from "../../constants.js";
import { verifyJwt } from "../../middleware/verify-jwt.js";
import { SelectAuthProvider } from "../../middleware/select-auth.js";

import { ModifyRoleRequest } from "./auth-formats.js";
import { JwtPayload, ProfileData, Provider, RoleOperation } from "./auth-models.js";
import { decodeJwtToken, generateJwtToken, getJwtPayloadFromProfile, getRoles, hasElevatedPerms, updateRoles, verifyFunction } from "./auth-lib.js";
import { ModifyRoleRequest } from "./auth-formats.js";


passport.use(Provider.GITHUB, new GitHubStrategy({
Expand Down Expand Up @@ -71,7 +73,7 @@ authRouter.get("/:PROVIDER/callback/", (req: Request, res: Response, next: NextF
});


authRouter.get("/roles/:USERID", async (req: Request, res: Response) => {
authRouter.get("/roles/:USERID", verifyJwt, async (req: Request, res: Response) => {
const targetUser: string | undefined = req.params.USERID;

// Check if we have a user to get roles for - if not, get roles for current user
Expand All @@ -80,31 +82,34 @@ authRouter.get("/roles/:USERID", async (req: Request, res: Response) => {
return;
}

try {
const payload: JwtPayload = decodeJwtToken(req.headers.authorization);

// Cases: Target user already logged in, auth user is admin
if (payload.id == targetUser) {
res.status(Constants.SUCCESS).send({id: payload.id, roles: payload.roles});
} else if (hasElevatedPerms(payload)) {
let roles: Role[] = [];
await getRoles(targetUser).then((targetRoles: Role[]) => {
roles = targetRoles;
}).catch((error: Error) => {
throw error;
});
console.log(roles);
const payload: JwtPayload = res.locals.payload as JwtPayload;

// Cases: Target user already logged in, auth user is admin
if (payload.id == targetUser) {
res.status(Constants.SUCCESS).send({id: payload.id, roles: payload.roles});
} else if (hasElevatedPerms(payload)) {
let roles: Role[] = [];
await getRoles(targetUser).then((targetRoles: Role[]) => {
roles = targetRoles;
res.status(Constants.SUCCESS).send({id: targetUser, roles: roles});
} else {
res.status(Constants.FORBIDDEN).send("not authorized to perform this operation!");
}
} catch (error) {
res.status(Constants.FORBIDDEN).send(error);
}).catch((error: Error) => {
console.log(error);
res.status(Constants.INTERNAL_ERROR).send(error);
});
} else {
res.status(Constants.FORBIDDEN).send("not authorized to perform this operation!");
}
});


authRouter.put("/roles/:OPERATION/", async (req: Request, res: Response) => {
authRouter.put("/roles/:OPERATION/", verifyJwt, async (req: Request, res: Response) => {
const payload: JwtPayload = res.locals.payload as JwtPayload;

// Not authenticated with modify roles perms
if (!hasElevatedPerms(payload)) {
res.status(Constants.FORBIDDEN).send({error: "not permitted to modify roles!"});
}

// Parse to get operation type
const op: RoleOperation | undefined = RoleOperation[req.params.operation as keyof typeof RoleOperation];

Expand All @@ -114,95 +119,71 @@ authRouter.put("/roles/:OPERATION/", async (req: Request, res: Response) => {
return;
}

try {
const payload: JwtPayload = decodeJwtToken(req.headers.authorization);

// Not authenticated with modify roles perms
if (!hasElevatedPerms(payload)) {
res.status(Constants.FORBIDDEN).send({error: "not permitted to modify roles!"});
}

// Check if role to add/remove actually exists
const data: ModifyRoleRequest = req.body as ModifyRoleRequest;
const role: Role | undefined = Role[data.role as keyof typeof Role];
if (!role) {
res.status(Constants.BAD_REQUEST).send({error: "invalid role passed in!"});
return;
}
// Check if role to add/remove actually exists
const data: ModifyRoleRequest = req.body as ModifyRoleRequest;
const role: Role | undefined = Role[data.role as keyof typeof Role];
if (!role) {
res.status(Constants.BAD_REQUEST).send({error: "invalid role passed in!"});
return;
}

// Try to update roles, if possible
await updateRoles(data.id, role, op).catch((error: string) => {
console.log(error);
res.status(Constants.INTERNAL_ERROR).send({error: error});
});
// Try to update roles, if possible
await updateRoles(data.id, role, op).catch((error: string) => {
console.log(error);
res.status(Constants.INTERNAL_ERROR).send({error: error});
});

// Get new roles for the current user, and return them
await getRoles(data.id).then((roles: Role[]) => {
res.status(Constants.SUCCESS).send({id: data.id, roles: roles});
}).catch((error: string) => {
console.log(error);
res.status(Constants.INTERNAL_ERROR).send({error: error});
});
} catch (error) {
res.status(Constants.FORBIDDEN).send(error);
}
// Get new roles for the current user, and return them
await getRoles(data.id).then((roles: Role[]) => {
res.status(Constants.SUCCESS).send({id: data.id, roles: roles});
}).catch((error: string) => {
console.log(error);
res.status(Constants.INTERNAL_ERROR).send({error: error});
});
});


authRouter.get("/list/roles/", (req: Request, res: Response) => {
try {
const payload: JwtPayload = decodeJwtToken(req.headers.authorization);
authRouter.get("/list/roles/", verifyJwt, (_: Request, res: Response) => {
const payload: JwtPayload = res.locals.payload as JwtPayload;

// Check if current user should be able to access all roles
if (!hasElevatedPerms(payload)) {
res.status(Constants.FORBIDDEN).send({error: "not authorized to perform this operation!"});
return;
}
// Check if current user should be able to access all roles
if (!hasElevatedPerms(payload)) {
res.status(Constants.FORBIDDEN).send({error: "not authorized to perform this operation!"});
return;
}

// Filter enum to get all possible string keys
const roles: string[] = Object.keys(Role).filter((item: string) => {
return isNaN(Number(item));
});
// Filter enum to get all possible string keys
const roles: string[] = Object.keys(Role).filter((item: string) => {
return isNaN(Number(item));
});

res.status(Constants.SUCCESS).send({roles: roles});
} catch (error) {
res.status(Constants.FORBIDDEN).send({error: error as string});
}
res.status(Constants.SUCCESS).send({roles: roles});
});


authRouter.get("/roles/", (req: Request, res: Response) => {
try {
const payload: JwtPayload = decodeJwtToken(req.headers.authorization);
res.status(Constants.SUCCESS).send({id: payload.id, roles: payload.roles});
} catch (error) {
res.status(Constants.FORBIDDEN).send({error: error as string});
}
authRouter.get("/roles/", verifyJwt, (req: Request, res: Response) => {
const payload: JwtPayload = decodeJwtToken(req.headers.authorization) ;
res.status(Constants.SUCCESS).send({id: payload.id, roles: payload.roles});
});


authRouter.get("/token/refresh", async (req: Request, res: Response) => {
try {
// Get old data from token
const oldPayload: JwtPayload = decodeJwtToken(req.headers.authorization);
const data: ProfileData = {
id: oldPayload.id,
email: oldPayload.email,
};
authRouter.get("/token/refresh", verifyJwt, async (_: Request, res: Response) => {
// Get old data from token
const oldPayload: JwtPayload = res.locals.payload as JwtPayload;
const data: ProfileData = {
id: oldPayload.id,
email: oldPayload.email,
};

// Generate a new payload for the token
let newPayload: JwtPayload | undefined;
await getJwtPayloadFromProfile(oldPayload.provider, data).then((payload: JwtPayload) => {
newPayload = payload;
});

// Create and return a new token with the payload
const newToken: string = generateJwtToken(newPayload);
res.status(Constants.SUCCESS).send({token: newToken});
// Generate a new payload for the token
let newPayload: JwtPayload | undefined;
await getJwtPayloadFromProfile(oldPayload.provider, data).then((payload: JwtPayload) => {
newPayload = payload;
});

} catch (error) {
res.status(Constants.FORBIDDEN).send({error: error as string});
}
// Create and return a new token with the payload
const newToken: string = generateJwtToken(newPayload);
res.status(Constants.SUCCESS).send({token: newToken});
});


Expand Down
22 changes: 9 additions & 13 deletions src/services/user/user-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import { UserFormat } from "./user-formats.js";
import { getUser, updateUser } from "./user-lib.js";

const userRouter: Router = Router();
userRouter.use(verifyJwt);


userRouter.get("/qr/", (_: Request, res: Response) => {
userRouter.get("/qr/", verifyJwt, (_: Request, res: Response) => {
// Return the same payload, but with
const payload: JwtPayload = res.locals.payload as JwtPayload;
const token: string = generateJwtToken(payload, "20s");
Expand All @@ -23,8 +22,8 @@ userRouter.get("/qr/", (_: Request, res: Response) => {
});


userRouter.get("/qr/:USERID", async (req: Request, res: Response) => {
const targetUser: string | undefined = req.query.USERID as string;
userRouter.get("/qr/:USERID", verifyJwt, async (req: Request, res: Response) => {
const targetUser: string | undefined = req.params.USERID as string;

// If target user -> redirect to base function
if (!targetUser) {
Expand Down Expand Up @@ -54,15 +53,14 @@ userRouter.get("/qr/:USERID", async (req: Request, res: Response) => {
});


userRouter.get("/:USERID", async (req: Request, res: Response) => {
const targetUser: string | undefined = req.params.USERID;

userRouter.get("/:USERID", verifyJwt, async (req: Request, res: Response) => {
// If no target user, exact same as next route
if (!targetUser) {
if (!req.params.USERID) {
res.redirect("/");
return;
}

const targetUser: string = req.params.USERID ?? "";

// Get payload, and check if authorized
const payload: JwtPayload = res.locals.payload as JwtPayload;
if (payload.id == targetUser || hasElevatedPerms(payload)) {
Expand All @@ -78,15 +76,15 @@ userRouter.get("/:USERID", async (req: Request, res: Response) => {
});


userRouter.get("/", async (_: Request, res: Response) => {
userRouter.get("/", verifyJwt, async (_: Request, res: Response) => {
// Get payload, return user's values
const payload: JwtPayload = res.locals.payload as JwtPayload;
const user: UserSchema = await getUser(payload.id);
res.status(Constants.SUCCESS).send(user);
});


userRouter.post("/", async (req: Request, res: Response) => {
userRouter.post("/", verifyJwt, async (req: Request, res: Response) => {
const token: JwtPayload = res.locals.payload as JwtPayload;

if (!hasElevatedPerms(token)) {
Expand All @@ -112,6 +110,4 @@ userRouter.post("/", async (req: Request, res: Response) => {
});




export default userRouter;

0 comments on commit d71e9e1

Please sign in to comment.