server: add JWTDenyList table and signout route (#52)

* add models and signout route
* add migration file for JWTDenylist

Closes #25 

Co-authored-by: Max Leiter <maxwell.leiter@gmail.com>
This commit is contained in:
NDI Lionel 2022-04-06 16:39:06 +01:00 committed by GitHub
parent 9cbcfd3397
commit b6439858df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 1 deletions

View file

@ -32,6 +32,7 @@ ${content}
} else {
contentToRender = "\n" + content
}
const html = markdown(contentToRender)
return html
}

View file

@ -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
}

View file

@ -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")

View file

@ -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)
}
})