diff --git a/src/api/Security.ts b/src/api/Security.ts new file mode 100644 index 0000000..3411b4e --- /dev/null +++ b/src/api/Security.ts @@ -0,0 +1,58 @@ +import crypto from 'crypto'; +import { Request } from 'express'; +import { Client } from '..'; + +export default class Security { + public client: Client; + + public keyPair: { publicKey: string, privateKey: string }; + + constructor(client: Client) { + this.client = client; + this.keyPair = client.config.keyPair; + } + + /** + * Creates a new Bearer token. + * @param _id The Mongoose Document property labeled ._id + */ + public async createBearer(_id: string): Promise { + const account = await this.client.db.Account.findOne({ _id }); + if (!account) throw new Error(`Account [${_id}] cannot be found.`); + const bearer = crypto.randomBytes(12); + const sign = crypto.createSign('sha3-224'); + sign.update(bearer); + sign.end(); + const signature = sign.sign(this.keyPair.privateKey, 'hex'); + await account.updateOne({ bearerSignature: signature }); + return bearer.toString('base64'); + } + + public async checkBearer(_id: string, bearer: string): Promise { + const account = await this.client.db.Account.findOne({ _id }); + if (!account) return false; + if (!account.bearerSignature) return false; + const verify = crypto.createVerify('sha3-224'); + verify.update(bearer); + verify.end(); + try { + return verify.verify(this.keyPair.publicKey, account.bearerSignature, 'base64'); + } catch { + return false; + } + } + + public extractBearer(req: Request): string { + const url = new URL(req.url); + if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { + return req.headers.authorization.split(' ')[1]; + } + if (req.query && req.query.token) { + return req.query.token; + } + if (url.password) { + return url.password; + } + return 'null'; + } +}