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

Require verified email #64

Merged
merged 1 commit into from
Sep 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions backend/src/controllers/TokensController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ResetPasswordTokenSignedPayload
} from "../types/tokens";
import { getEntityArray } from "../utils/entities";
import { getAuthenticationTokens } from "../utils/users";

export async function login(request: Request, response: Response) {
if (!request.headers.authorization) {
Expand Down Expand Up @@ -43,7 +42,7 @@ export async function login(request: Request, response: Response) {
return;
}

const result = getAuthenticationTokens(user);
const result = user.createAuthenticationTokens();
response.status(200).json(result);
}

Expand All @@ -63,12 +62,10 @@ export async function refreshAuthentication(
return;
}

const result = getAuthenticationTokens(user);
const result = user.createAuthenticationTokens();
response.status(200).json(result);
}

// export async function resetPassword(request: Request, response: Response) {

export async function editOpinion(request: Request, response: Response) {
try {
const entityTokenSignedPayload = response.locals
Expand Down Expand Up @@ -242,7 +239,10 @@ export async function verifyEmail(request: Request, response: Response) {
if (result.affected === 0) {
throw new Error("Failed to update user");
}
response.status(204).send();

user.emailVerified = true;
const authenticationTokens = user.createAuthenticationTokens();
response.status(204).json(authenticationTokens);
} catch (error) {
response.sendStatus(400);
console.error(error);
Expand Down
3 changes: 1 addition & 2 deletions backend/src/controllers/UsersController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { NextFunction, Request, Response } from "express";
import { getRepository } from "typeorm";
import { User } from "../entities/User";
import { AccessTokenSignedPayload } from "../types/tokens";
import { getAuthenticationTokens } from "../utils/users";
import {
sendVerificationEmail,
sendResetPasswordEmail
Expand All @@ -23,7 +22,7 @@ export async function create(request: Request, response: Response) {

sendVerificationEmail(user);

const result = { ...user, ...getAuthenticationTokens(user) };
const result = { ...user, ...user.createAuthenticationTokens() };
delete result.password;
response.status(201).json(result);
} catch (error) {
Expand Down
37 changes: 36 additions & 1 deletion backend/src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import {
IsString
} from "class-validator";
import { Column, Entity, OneToMany } from "typeorm";
import { sign } from "jsonwebtoken";
import { Discardable } from "./Discardable";
import { TipVote } from "./TipVote";
import { OpinionVote } from "./OpinionVote";
import { UserRole } from "../types/users";
import { BearerTokenType, EntityTokenPayload } from "../types/tokens";
import {
BearerTokenType,
EntityTokenPayload,
Credentials,
RefreshTokenPayload,
AccessTokenPayload
} from "../types/tokens";

@Entity()
export class User extends Discardable {
Expand Down Expand Up @@ -65,5 +72,33 @@ export class User extends Discardable {
username: this.username
});

getCredentials = (): Credentials => ({
id: this.id,
email: this.email,
emailVerified: this.emailVerified,
role: this.role,
username: this.username
});

createAuthenticationTokens = () => {
const credentials = this.getCredentials();
const accessTokenPayload: AccessTokenPayload = {
type: BearerTokenType.AccessToken,
...credentials
};
const accessToken = sign(accessTokenPayload, process.env.JWT_SECRET!, {
expiresIn: "15m"
});
const refreshTokenPayload: RefreshTokenPayload = {
type: BearerTokenType.RefreshToken,
...credentials
};
const refreshToken = sign(refreshTokenPayload, process.env.JWT_SECRET!, {
expiresIn: "7 days"
});

return { accessToken, refreshToken };
};

entityName = "User";
}
16 changes: 16 additions & 0 deletions backend/src/middlewares/checkEmailVerified.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from "express";
import { AccessTokenSignedPayload } from "../types/tokens";

export const checkEmailVerified = (
req: Request,
res: Response,
next: NextFunction
) => {
const payload = res.locals.payload as AccessTokenSignedPayload;

if (payload.emailVerified) {
next();
} else {
res.sendStatus(403);
}
};
2 changes: 2 additions & 0 deletions backend/src/routes/api/moduleSemesters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as OpinionsController from "../../controllers/OpinionsController";
import * as ReviewsController from "../../controllers/ReviewsController";
import * as TipsController from "../../controllers/TipsController";
import { checkBearerToken } from "../../middlewares/checkBearerToken";
import { checkEmailVerified } from "../../middlewares/checkEmailVerified";
import { BearerTokenType } from "../../types/tokens";

export const router = Router();
Expand All @@ -14,6 +15,7 @@ router.get("/:id/tips", ModuleSemestersController.tips);
router.get("/:id/reviews", ModuleSemestersController.reviews);

router.use(checkBearerToken(BearerTokenType.AccessToken));
router.use(checkEmailVerified);
router.post("/:id/opinions", OpinionsController.create);
router.post("/:id/tips", TipsController.create);
router.post("/:id/reviews", ReviewsController.create);
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes/api/opinions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { checkBearerToken } from "../../middlewares/checkBearerToken";
import { checkRole } from "../../middlewares/checkRole";
import { UserRole } from "../../types/users";
import { BearerTokenType } from "../../types/tokens";
import { checkEmailVerified } from "../../middlewares/checkEmailVerified";

export const router = Router();

router.get("/:id", OpinionsController.show);
router.get("/:id/votes", OpinionsController.votes);

router.use(checkBearerToken(BearerTokenType.AccessToken));
router.use(checkEmailVerified);
router.post("/:id/votes", OpinionVotesController.create);
router.use(checkRole([UserRole.Admin]));
router.delete("/:id", OpinionsController.discard);
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes/api/tips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { checkBearerToken } from "../../middlewares/checkBearerToken";
import { checkRole } from "../../middlewares/checkRole";
import { UserRole } from "../../types/users";
import { BearerTokenType } from "../../types/tokens";
import { checkEmailVerified } from "../../middlewares/checkEmailVerified";

export const router = Router();

router.get("/:id", TipsController.show);
router.get("/:id/votes", TipsController.votes);

router.use(checkBearerToken(BearerTokenType.AccessToken));
router.use(checkEmailVerified);
router.post("/:id/votes", TipVotesController.create);
router.use(checkRole([UserRole.Admin]));
router.delete("/:id", TipsController.discard);
Expand Down
4 changes: 3 additions & 1 deletion backend/src/types/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ type TokenLifespan = {
exp: number;
};

type Credentials = {
export type Credentials = {
id: number;
email: string;
emailVerified: boolean;
role: UserRole;
username: string;
};
Expand Down Expand Up @@ -65,6 +66,7 @@ function hasCredentials(payload: any) {
typeof payload.id === "number" &&
typeof payload.username === "string" &&
typeof payload.email === "string" &&
typeof payload.emailVerified === "boolean" &&
Object.values(UserRole).includes(payload.role)
);
}
Expand Down
5 changes: 1 addition & 4 deletions backend/src/utils/sendgrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ export function sendResetPasswordEmail(user: User) {

const payload: ResetPasswordTokenPayload = {
type: BearerTokenType.ResetPasswordToken,
id: user.id,
email: user.email,
role: user.role,
username: user.username
...user.getCredentials()
};

const token = jwt.sign(payload, process.env.JWT_SECRET!, {
Expand Down
35 changes: 0 additions & 35 deletions backend/src/utils/users.ts

This file was deleted.