This repository has been archived on 2023-05-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
auth/lib/schema/auth.ts
mifi 34acea15a2
Some checks failed
continuous-integration/drone/push Build is failing
- Linty fresh...
2023-05-02 02:04:09 -04:00

153 lines
4.2 KiB
TypeScript

import { JwtPayload } from 'jsonwebtoken';
import {
InferSchemaType,
Model,
Schema,
StringSchemaDefinition,
Types,
} from 'mongoose';
import { Strategy } from './strategy';
import { STRATEGIES } from '../constants/strategies';
import { TokenProps, sign, verify as verifyJwt } from '../utils/jwt';
import { encrypt, verify as verifyPassword } from '../utils/password';
import { generateResetToken } from '../utils/tokens';
export type Auth = {
is2FA?: boolean;
record: StringSchemaDefinition;
username: string;
};
export type AuthPrivate = Auth & {
strategies: Types.ArraySubdocument<Strategy>;
};
export interface AuthMethods {
authenticate(password: string): boolean;
getAuthStrategy(method?: STRATEGIES): Strategy | false;
getResetLink(route: string): Promise<string | undefined>;
getResetToken(): Promise<string | undefined>;
getToken(props?: Omit<TokenProps, 'sub'>): string;
setPassword(password: string): Promise<boolean>;
}
export interface AuthModel extends Model<AuthPrivate, {}, AuthMethods> {
authenticate(password: any): boolean;
findByUsername(username: string): Promise<AuthModel & AuthPrivate>;
isUsernameAvailable(username: string): Promise<boolean>;
findUserForReset(
strategy: STRATEGIES,
token: string
): Promise<Strategy | undefined>;
resetPassword(token: string, password: string): Promise<boolean>;
}
export const AuthSchema = new Schema<AuthPrivate, AuthModel, AuthMethods>(
{
is2FA: { type: Boolean, default: false },
record: { type: Types.ObjectId },
strategies: { type: Types.ArraySubdocument<Strategy>, required: true },
username: { type: String, required: true, unique: true },
},
{
minimize: true,
timestamps: true,
}
);
AuthSchema.methods = {
authenticate: function (password: string) {
const strategy = this.getAuthStrategy(STRATEGIES.LOCAL);
return !!strategy && verifyPassword(password, strategy.key);
},
getAuthStrategy: function (method = STRATEGIES.LOCAL) {
return (
this.strategies
.filter((strategy: Strategy) => strategy.method === method)
.pop() || false
);
},
getToken: function (props = {}) {
return sign({
sub: this._id,
...props,
});
},
getResetLink: async function (route) {
const resetToken = await this.getResetToken();
if (resetToken) {
let resetRoute = route;
resetRoute = resetRoute.replace(':user_id', this._id);
resetRoute = resetRoute.replace(':reset_token?', resetToken);
const resetUrl = `${process.env.URL}${resetRoute}`;
console.log('[sendPasswordReset] resetUrl:', resetUrl);
return resetUrl;
}
},
getResetToken: async function () {
const { key, token } = generateResetToken(this._id);
this.resetCheckBit = key;
await this.save().catch(() => undefined);
return token;
},
setPassword: async function (password) {
const key = encrypt(password);
const hasLocalStrategy = !!this.getAuthStrategy(STRATEGIES.LOCAL);
const strategy = {
key,
method: STRATEGIES.LOCAL,
resetToken: undefined,
};
if (hasLocalStrategy) {
await this.model('User')
.findOneAndUpdate(
{ _id: this._id, 'strategies.method': STRATEGIES.LOCAL },
{ $set: { 'strategies.$': strategy } },
{ upsert: true },
)
.catch();
return true;
}
this.credentials.push(strategy);
await this.save().catch(() => false);
return true;
}
};
AuthSchema.statics = {
// authenticateAndGetRecordLocator: async function (username, password) {
// const auth = await this.findByUserName(username);
// if (auth && auth.authenticate(password)) {
// return auth?.record;
// }
// return false;
// },
findByUsername: async function (username) {
return this.findOne({ username });
},
isUsernameAvailable: async function (username) {
return !this.findByUsername(username);
},
resetPassword: async function (token, password) {
const decoded = verifyJwt(token);
const { sub, key } = decoded as JwtPayload;
const auth = await this.findOne({
_id: sub,
'strategies.resetToken': key,
}).catch();
return !!auth && auth.setPassword(password);
}
};
export type AuthSchema = InferSchemaType<typeof AuthSchema>;