diff --git a/server/src/lib/get-html-from-drift-file.ts b/server/src/lib/get-html-from-drift-file.ts index bbee1ea0..dcb22dd8 100644 --- a/server/src/lib/get-html-from-drift-file.ts +++ b/server/src/lib/get-html-from-drift-file.ts @@ -32,6 +32,7 @@ ${content} } else { contentToRender = "\n" + content } + const html = markdown(contentToRender) return html } diff --git a/server/src/lib/models/JWTDenyList.ts b/server/src/lib/models/JWTDenyList.ts new file mode 100644 index 00000000..fce57cec --- /dev/null +++ b/server/src/lib/models/JWTDenyList.ts @@ -0,0 +1,42 @@ +import { + Model, + Column, + Table, + IsUUID, + PrimaryKey, + DataType, + CreatedAt, + UpdatedAt, + DeletedAt, + Unique +} from "sequelize-typescript" + +@Table +export class JWTDenyList extends Model { + @IsUUID(4) + @PrimaryKey + @Unique + @Column({ + type: DataType.UUID, + defaultValue: DataType.UUIDV4 + }) + id!: string + + @Column + token!: string + + @Column + reason!: string + + @CreatedAt + @Column + createdAt!: Date + + @UpdatedAt + @Column + updatedAt!: Date + + @DeletedAt + @Column + deletedAt?: Date +} diff --git a/server/src/migrations/07_denylist-table.ts b/server/src/migrations/07_denylist-table.ts new file mode 100644 index 00000000..2ba01062 --- /dev/null +++ b/server/src/migrations/07_denylist-table.ts @@ -0,0 +1,31 @@ +"use strict" +import { DataTypes } from "sequelize" +import type { Migration } from "../database" + +export const up: Migration = async ({ context: queryInterface }) => + queryInterface.createTable("JWTDenyLists", { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + unique: true + }, + token: { + type: DataTypes.STRING + }, + reason: { + type: DataTypes.STRING + }, + createdAt: { + type: DataTypes.DATE + }, + updatedAt: { + type: DataTypes.DATE + }, + deletedAt: { + type: DataTypes.DATE + } + }) + +export const down: Migration = async ({ context: queryInterface }) => + queryInterface.dropTable("JWTDenyLists") diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 1ac96668..2b485432 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -1,7 +1,8 @@ import { Router } from "express" import { genSalt, hash, compare } from "bcryptjs" import { User } from "@lib/models/User" -import { sign } from "jsonwebtoken" +import { JWTDenyList } from "@lib/models/JWTDenyList" +import { sign, verify } from "jsonwebtoken" import config from "@lib/config" import jwt from "@lib/middleware/jwt" import { celebrate, Joi } from "celebrate" @@ -144,3 +145,31 @@ auth.get("/verify-token", jwt, async (req, res, next) => { next(e) } }) + +auth.post("/signout", jwt, async (req, res, next) => { + try { + const authHeader = req.headers["authorization"] + const token = authHeader?.split(" ")[1] + let reason = "" + if (token == null) return res.sendStatus(401) + + verify(token, config.jwt_secret, (err: any, user: any) => { + if (err) return res.sendStatus(403) + if (user) { + reason = "Manually revoked" + } else { + reason = "Token expired" + } + }) + const denylist = await new JWTDenyList({ token, reason }) + await denylist.save() + req.headers["authorization"] = "" + res.status(201).json({ + message: "You are now logged out", + token, + reason + }) + } catch (e) { + next(e) + } +})