Files
auth-db/lib/schema/token.ts

66 lines
2.0 KiB
TypeScript

import { InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose';
import { TokenType } from '../constants/tokens';
import { getDefaultExpiresFor } from '../utils/getDefaultExpiresFor';
import { sign, verify } from '../utils/jwt';
export interface Token {
auth: StringSchemaDefinition;
expires?: number;
type: TokenType;
}
export interface TokenModel extends Model<Token> {
cleanupExpiredTokens(): { success: boolean; deletedCount: number };
getToken(type: TokenType, auth: Types.ObjectId, expires?: number): string;
validateResetToken(token: string): Types.ObjectId | false;
}
export const TokenSchema = new Schema<Token, TokenModel>(
{
auth: { type: Types.ObjectId, index: true },
expires: { type: Number, required: true },
type: { type: String, enum: TokenType, required: true },
},
{
minimize: true,
timestamps: true,
},
);
TokenSchema.statics = {
async cleanupExpiredTokens() {
const { acknowledged, deletedCount } = await this.deleteMany({ expires: { $lte: Date.now() } });
return { success: acknowledged, deletedCount };
},
async getToken(type: TokenType, auth: StringSchemaDefinition, expires?: number) {
const existing = await this.findOne({ type, auth });
if (existing) {
await existing.deleteOne();
}
const doc = await this.create({ type, auth, expires: expires || getDefaultExpiresFor(type) });
return sign({
sub: `${doc._id}`,
exp: doc.expires,
});
},
async validateResetToken(token: string) {
const { sub } = verify(token);
if (sub) {
const record = await this.findById(sub);
if (record) {
await record.deleteOne();
return !!record?.expires && record.expires >= Date.now() && record.auth;
}
}
return false;
},
};
export type TokenSchema = InferSchemaType<typeof TokenSchema>;