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:
parent
9cbcfd3397
commit
b6439858df
4 changed files with 104 additions and 1 deletions
|
@ -32,6 +32,7 @@ ${content}
|
|||
} else {
|
||||
contentToRender = "\n" + content
|
||||
}
|
||||
|
||||
const html = markdown(contentToRender)
|
||||
return html
|
||||
}
|
||||
|
|
42
server/src/lib/models/JWTDenyList.ts
Normal file
42
server/src/lib/models/JWTDenyList.ts
Normal 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
|
||||
}
|
31
server/src/migrations/07_denylist-table.ts
Normal file
31
server/src/migrations/07_denylist-table.ts
Normal 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")
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue