18 Commits

Author SHA1 Message Date
cc3e97d2e1 Working! 2023-06-01 00:09:32 -04:00
e9681fbc9a Merge branch 'main' into develop 2023-05-30 20:23:24 -04:00
4ce4b62fe5 Reduce duplicate code, move shit out to common package 2023-05-30 20:22:55 -04:00
64e1f53f4e Updates as I integrate 2023-05-30 17:44:50 -04:00
efb237885f Merge pull request 'Release 1.0.6' (#4) from develop into main
All checks were successful
continuous-integration/drone/tag Build is passing
Reviewed-on: #4
2023-05-26 21:49:25 +00:00
ec61e29fb0 Tweaks and mergefail fixes
All checks were successful
continuous-integration/drone/pr Build is passing
2023-05-26 17:46:37 -04:00
d97a7d7f95 Merge branch 'main' into develop
Some checks failed
continuous-integration/drone/pr Build is failing
# Conflicts:
#	.npmignore
#	lib/dao/deleteById.ts
#	lib/dao/readOneByRecord.ts
#	lib/dao/readOneByUsername.ts
#	package.json
#	src/dao/auth/deleteById.ts
#	src/dao/auth/readOneByRecord.ts
#	src/dao/auth/readOneByUsername.ts
#	src/dao/create.ts
#	src/dao/deleteById.ts
#	src/dao/readOneByRecord.ts
#	src/dao/readOneByUsername.ts
2023-05-26 17:44:52 -04:00
4bbe520366 Merge pull request 'Add DAO shit for Strategy model, added types for different ops, bump package version' (#3) from feature/ISS-3_-_WebAuthn_Support into develop
Some checks failed
continuous-integration/drone/pr Build is failing
Reviewed-on: #3
2023-05-26 21:40:07 +00:00
e657d09ae5 Add DAO shit for Strategy model, added types for different ops, bump package version
All checks were successful
continuous-integration/drone/pr Build is passing
2023-05-26 17:35:58 -04:00
08293e8c16 .npmignore updates 2023-05-24 13:08:49 -04:00
fdfd8b567a Last try before I force it...
All checks were successful
continuous-integration/drone/pr Build is passing
2023-05-24 12:44:01 -04:00
55c2274a4f Grrrr...
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 12:41:12 -04:00
7d40fdfe74 Bam!
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 12:39:37 -04:00
8e5092b943 Haha!
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 12:35:37 -04:00
35b89062e4 Prettier?
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 10:46:24 -04:00
65e30ba1c7 Prettier...
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 10:33:41 -04:00
40645f58e3 Oops... paths.
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 10:31:38 -04:00
7619899edc Restructuring the folders
Some checks failed
continuous-integration/drone/pr Build is failing
2023-05-24 10:24:15 -04:00
46 changed files with 392 additions and 271 deletions

View File

@@ -1,10 +1,10 @@
.build.yarnrc.yml .build.yarnrc.yml
.drone.yml .drone.yml
.eslintrc* .eslintrc*
.npmrc
.prettierrc* .prettierrc*
.yarnrc.yml .yarnrc.yml
babel.config.* babel.config.*
jest.config.* jest.config.*
src
tsconfig*.json tsconfig*.json
tslint.json tslint.json

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mifi/auth-db", "name": "@mifi/auth-db",
"version": "1.0.5", "version": "1.0.20",
"author": "mifi (Mike Fitzpatrick)", "author": "mifi (Mike Fitzpatrick)",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@@ -39,7 +39,8 @@
"typescript": "^4.9.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@mifi/services-common": "1.x.x", "@mifi/auth-common": "^1.0.15",
"@mifi/services-common": "^1.0.16",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"mongoose": "^6.9.2" "mongoose": "^6.9.2"

View File

@@ -1,16 +1,17 @@
import { Auth, Log } from '..'; import { Action } from '@mifi/auth-common/lib/enums/action';
import { Action } from '../constants/action'; import { generateLoginToken } from '@mifi/auth-common/lib/utils/generateLoginToken';
import { getLoginToken } from '../utils/getLoginToken';
export const authenticate = async (username: string, password: string) => { import { Auth, Log } from '..';
export const authenticate = async (username: string, password: string, includeToken = false) => {
const doc = await Auth.findByUsername(username).catch(); const doc = await Auth.findByUsername(username).catch();
if (!!doc && (await doc.authenticate(password))) { if (!!doc && (await doc.authenticate(password))) {
Log.add(doc.id, Action.AUTHENTICATE); Log.add(doc.id, Action.AUTHENTICATE);
return { ...doc, token: getLoginToken(doc) }; return { sub: doc._id, record: doc.record, token: includeToken ? generateLoginToken(doc) : undefined };
} }
if (doc) { if (doc) {
Log.add(doc.id, Action.AUTHENTICATE_FAILURE); Log.add(doc.id, Action.AUTHENTICATE_FAILURE, { ...doc });
} }
return false; return false;

View File

@@ -1,4 +1,5 @@
import { StringSchemaDefinition } from 'mongoose'; import { StringSchemaDefinition } from 'mongoose';
import { Auth, Strategy } from '..'; import { Auth, Strategy } from '..';
export const deleteStrategy = async (id: StringSchemaDefinition) => { export const deleteStrategy = async (id: StringSchemaDefinition) => {

View File

@@ -1,7 +1,8 @@
import { Action } from '@mifi/auth-common/lib/enums/action';
import { TokenType } from '@mifi/auth-common/lib/enums/tokens';
import { readOneByUsername } from '../dao/readOneByUsername'; import { readOneByUsername } from '../dao/readOneByUsername';
import { Log, Token } from '..'; import { Log, Token } from '..';
import { TokenType } from '../constants/tokens';
import { Action } from '../constants/action';
export const resetPasswordGet = async (username: string) => { export const resetPasswordGet = async (username: string) => {
const doc = await readOneByUsername(username); const doc = await readOneByUsername(username);

View File

@@ -1,11 +1,12 @@
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { Log, Strategy, Token } from '..'; import { Action } from '@mifi/auth-common/lib/enums/action';
import { STRATEGIES } from '../constants/strategies'; import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
import { generateLoginToken } from '@mifi/auth-common/lib/utils/generateLoginToken';
import { AuthDocument } from '../schema/auth'; import { AuthDocument } from '../schema/auth';
import { getLoginToken } from '../utils/getLoginToken';
import { StrategyDocument } from '../schema/strategy'; import { StrategyDocument } from '../schema/strategy';
import { Action } from '../constants/action'; import { Log, Strategy, Token } from '..';
export const resetPasswordPost = async (token: string, password: string) => { export const resetPasswordPost = async (token: string, password: string) => {
const parentId = await Token.validateResetToken(token); const parentId = await Token.validateResetToken(token);
@@ -34,7 +35,7 @@ export const resetPasswordPost = async (token: string, password: string) => {
} }
Log.add(parent._id, Action.RESET); Log.add(parent._id, Action.RESET);
return { record: parent.record, token: getLoginToken(parent) }; return { record: parent.record, token: generateLoginToken(parent) };
} }
return false; return false;

View File

@@ -1,9 +0,0 @@
export enum Action {
AUTHENTICATE = 'AUTHENTICATE',
AUTHENTICATE_FAILURE = 'AUTHENTICATE_FAILURE',
CREATE = 'CREATE',
DELETE = 'DELETE',
RESET = 'RESET',
RESET_REQUEST = 'RESET_REQUEST',
UPDATE = 'UPDATE',
}

View File

@@ -1,8 +0,0 @@
export enum Status {
ACTIVE,
BLOCK_HARD,
BLOCK_SOFT,
DELETED,
INACTIVE,
UNVERIFIED,
}

View File

@@ -1,20 +0,0 @@
export const PACKAGE_NAME = '@mifi/auth';
export const PORT = process.env.PORT || 9000;
export const SESSION_KEY = process.env.SESSION_KEY || 'secret-key';
export const JWT_AUDIENCE = process.env.JWT_AUDIENCE || 'mifi.dev';
export const JWT_ISSUER = process.env.JWT_ISSUER || PACKAGE_NAME;
export const JWT_SECRET = process.env.JWT_SECRET || 'secret';
export const LOGIN_VALID_TIMEOUT = process.env.LOGIN_VALID_TIMEOUT || '12h'; // ###d|h|m
export const RESET_VALID_TIMEOUT = process.env.RESET_VALID_TIMEOUT || '15m'; // ###d|h|m
export const VERIFY_VALID_TIMEOUT = process.env.VERIFY_VALID_TIMEOUT || '60d'; // ###d|h|m
export const DEFAULT_TOKEN_DAYS = process.env.DEFAULT_TOKEN_DAYS || 365;
export const ROUTE_PREFIX = process.env.ROUTE_PREFIX || '/auth';
export const LOGIN_ROUTE = process.env.LOGIN_ROUTE || '/login';
export const RESET_ROUTE = process.env.RESET_ROUTE || '/reset';
export const VERIFICATION_ROUTE = process.env.VERIFICATION_ROUTE || '/verification';
export const REQUIRE_VERIFICATION = process.env.REQUIRE_VERIFICATION || true;

View File

@@ -1,12 +0,0 @@
export enum ErrorCodes {
RESET_REQUEST_DATA = 'RESET_REQUEST_DATA',
}
export const ErrorMessages = {
[ErrorCodes.RESET_REQUEST_DATA]: 'A valid username and password must be provided',
};
export const getErrorBody = (code: ErrorCodes) => ({
code,
message: ErrorMessages[code],
});

View File

@@ -1,7 +0,0 @@
export enum STRATEGIES {
LOCAL,
APPLE,
FACEBOOK,
FIDO2,
GOOGLE,
}

View File

@@ -0,0 +1,4 @@
export const LOGIN_VALID_TIMEOUT = process.env.LOGIN_VALID_TIMEOUT || '12h'; // ###d|h|m
export const RESET_VALID_TIMEOUT = process.env.RESET_VALID_TIMEOUT || '15m'; // ###d|h|m
export const VERIFY_VALID_TIMEOUT = process.env.VERIFY_VALID_TIMEOUT || '60d'; // ###d|h|m
export const DEFAULT_TOKEN_DAYS = process.env.DEFAULT_TOKEN_DAYS || 365;

View File

@@ -1,4 +0,0 @@
export enum TokenType {
RESET = 'RESET',
VERIFICATION = 'VERIFICATION',
}

65
src/dao/auth/create.ts Normal file
View File

@@ -0,0 +1,65 @@
import { Action } from '@mifi/auth-common/lib/enums/action';
import { Status } from '@mifi/auth-common/lib/enums/status';
import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
import { TokenType } from '@mifi/auth-common/lib/enums/tokens';
import { REQUIRE_VERIFICATION } from '@mifi/auth-common/lib/settings';
import { DatabaseError } from '@mifi/services-common/lib/domain/errors/DatabaseError';
import { Auth as AuthProps } from '../../schema/auth';
import { Auth, Log, Strategy, Token } from '../..';
type CreateProps = Pick<AuthProps, 'username'> & {
externalId?: string;
handle?: AuthProps['handle'];
password?: string;
publicKey?: string;
record?: AuthProps['record'];
};
export const create = async ({ record, username, externalId, handle, password, publicKey }: CreateProps) => {
const status = REQUIRE_VERIFICATION ? Status.UNVERIFIED : Status.ACTIVE;
const doc = await Auth.create({
handle,
record,
status,
username,
}).catch((err) => {
throw new DatabaseError('failed to create user', { err });
});
if (doc) {
const method = externalId && publicKey ? STRATEGIES.FIDO2 : STRATEGIES.LOCAL;
const strategy = await Strategy.create({
externalId,
key: password || publicKey,
method,
parent: doc._id,
}).catch((err) => {
throw new DatabaseError(`failed to create strategy ${STRATEGIES[method]}`, { err });
});
if (strategy) {
doc.strategies.push(strategy._id);
await doc.save();
Log.add(doc._id, Action.CREATE);
return {
doc,
token:
method === STRATEGIES.LOCAL &&
REQUIRE_VERIFICATION &&
(await Token.getToken(TokenType.VERIFICATION, doc._id)),
};
}
await doc.deleteOne((err) => {
throw new DatabaseError('failed to remove invalid auth record', {
err,
doc,
});
});
}
return null;
};
export type Fido2UserProps = Pick<AuthProps, 'handle' | 'username'> & { externalId: string; publicKey: string };
export const createFido2User = (props: Fido2UserProps) => create(props);
export type LocalUserProps = Pick<AuthProps, 'record' | 'username'> & { password: string };
export const createLocalUser = (props: LocalUserProps) => create(props);

View File

@@ -0,0 +1,21 @@
import { StringSchemaDefinition } from 'mongoose';
import { Action } from '@mifi/auth-common/lib/enums/action';
import { Status } from '@mifi/auth-common/lib/enums/status';
import { Auth, Log, Strategy, Token } from '../..';
export const deleteById = async (id: StringSchemaDefinition) => {
if (
await Auth.findByIdAndUpdate(id, {
status: Status.DELETED,
strategies: [],
}).catch()
) {
await Strategy.deleteMany({ parent: id });
await Token.deleteMany({ auth: id });
Log.add(id, Action.DELETE);
return true;
}
return false;
};

10
src/dao/auth/readAll.ts Normal file
View File

@@ -0,0 +1,10 @@
import { FilterQuery } from 'mongoose';
import { Status } from '@mifi/auth-common/lib/enums/status';
import { Auth } from '../../model/auth';
import { AuthDocument } from '../../schema/auth';
export const readAll = async (query: FilterQuery<AuthDocument> = {}) => Auth.find(query);
export const readAllActive = async () => readAll({ status: { $ne: Status.DELETED } });

View File

@@ -0,0 +1,5 @@
import { Types } from 'mongoose';
import { Auth } from '../../model/auth';
export const readOneById = async (id: Types.ObjectId) => Auth.findById(id);

View File

@@ -0,0 +1,5 @@
import { Types } from 'mongoose';
import { Auth } from '../../model/auth';
export const readOneByRecord = async (record: Types.ObjectId) => Auth.findOne({ record });

View File

@@ -0,0 +1,3 @@
import { Auth } from '../../model/auth';
export const readOneByUsername = async (username: string) => Auth.findOne({ username });

View File

@@ -1,45 +1,3 @@
import { DatabaseError } from '@mifi/services-common/lib/domain/errors/DatabaseError'; import { create } from './auth/create';
import { Auth, Log, Strategy, Token } from '..'; export { create };
import { Auth as AuthProps } from '../schema/auth';
import { STRATEGIES } from '../constants/strategies';
import { REQUIRE_VERIFICATION } from '../constants/env';
import { TokenType } from '../constants/tokens';
import { Status } from '../constants/auth';
import { Action } from '../constants/action';
export const create = async ({ record, username, password }: AuthProps & { password: string }) => {
const status = REQUIRE_VERIFICATION ? Status.UNVERIFIED : Status.ACTIVE;
const doc = await Auth.create({
record,
status,
username,
}).catch((err) => {
throw new DatabaseError('failed to create user', { err });
});
if (doc) {
const strategy = await Strategy.create({
method: STRATEGIES.LOCAL,
key: password,
parent: doc._id,
}).catch((err) => {
throw new DatabaseError('failed to create strategy', { err });
});
if (strategy) {
doc.strategies.push(strategy._id);
await doc.save();
Log.add(doc._id, Action.CREATE);
return {
doc,
token: REQUIRE_VERIFICATION && (await Token.getToken(TokenType.VERIFICATION, doc._id)),
};
}
await doc.deleteOne((err) => {
throw new DatabaseError('failed to remove invalid auth record', {
err,
doc,
});
});
}
return null;
};

View File

@@ -1,20 +1,3 @@
import { StringSchemaDefinition } from 'mongoose'; import { deleteById } from './auth/deleteById';
import { Auth, Log, Strategy, Token } from '..'; export { deleteById };
import { Status } from '../constants/auth';
import { Action } from '../constants/action';
export const deleteById = async (id: StringSchemaDefinition) => {
if (
await Auth.findByIdAndUpdate(id, {
status: Status.DELETED,
strategies: [],
}).catch()
) {
await Strategy.deleteMany({ parent: id });
await Token.deleteMany({ auth: id });
Log.add(id, Action.DELETE);
return true;
}
return false;
};

View File

@@ -1,9 +1,3 @@
import { FilterQuery } from 'mongoose'; import { readAll } from './auth/readAll';
import { Auth } from '../model/auth'; export { readAll };
import { Status } from '../constants/auth';
import { AuthDocument } from '../schema/auth';
export const readAll = async (query: FilterQuery<AuthDocument> = {}) => Auth.find(query);
export const readAllActive = async () => readAll({ status: { $ne: Status.DELETED } });

View File

@@ -1,5 +1,3 @@
import { Types } from 'mongoose'; import { readOneById } from './auth/readOneById';
import { Auth } from '../model/auth'; export { readOneById };
export const readOneById = async (id: Types.ObjectId) => Auth.findById(id);

View File

@@ -1,5 +1,3 @@
import { Types } from 'mongoose'; import { readOneByRecord } from './auth/readOneByRecord';
import { Auth } from '../model/auth'; export { readOneByRecord };
export const readOneByRecord = async (record: Types.ObjectId) => Auth.findOne({ record });

View File

@@ -1,3 +1,3 @@
import { Auth } from '../model/auth'; import { readOneByUsername } from './auth/readOneByUsername';
export const readOneByUsername = async (username: string) => Auth.findOne({ username }); export { readOneByUsername };

View File

@@ -0,0 +1,24 @@
import { DatabaseError } from '@mifi/services-common/lib/domain/errors/DatabaseError';
import { Strategy } from '../../model/strategy';
import { Strategy as StrategyProps } from '../../schema/strategy';
const create = async ({ externalId, key, method, parent, profile }: StrategyProps) =>
Strategy.create({
externalId,
key,
method,
parent,
profile,
}).catch((err) => {
throw new DatabaseError(err, {
externalId: externalId || null,
key,
method,
parent,
profile: profile || null,
});
});
export const createFido2Strategy = (props: Omit<StrategyProps, 'profile'>) => create(props);
export const createLocalStrategy = (props: Omit<StrategyProps, 'externalId' | 'profile'>) => create(props);

View File

@@ -0,0 +1,8 @@
import { StringSchemaDefinition } from 'mongoose';
import { Strategy } from '../../model/strategy';
export const deleteById = async (id: StringSchemaDefinition) => {
const result = await Strategy.findByIdAndDelete(id).catch();
return !!result;
};

View File

@@ -0,0 +1,6 @@
import { FilterQuery } from 'mongoose';
import { Strategy } from '../../model/strategy';
import { StrategyDocument } from '../../schema/strategy';
export const readAll = async (query: FilterQuery<StrategyDocument> = {}) => Strategy.find(query);

View File

@@ -0,0 +1,7 @@
import { Strategy } from '../../model/strategy';
import { AuthDocument } from '../../schema/auth';
export const readOneByExternalId = async (externalId: string, populate = false) =>
populate
? Strategy.findOne({ externalId }).populate<{ parent: AuthDocument }>('parent')
: Strategy.findOne({ externalId });

View File

@@ -0,0 +1,5 @@
import { Types } from 'mongoose';
import { Strategy } from '../../model/strategy';
export const readOneById = async (id: Types.ObjectId) => Strategy.findById(id);

View File

@@ -0,0 +1,8 @@
import { Types } from 'mongoose';
import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
import { Strategy } from '../../model/strategy';
export const readOneByParentAndMethod = async (parent: Types.ObjectId, method: STRATEGIES) =>
Strategy.findOne({ method, parent });

View File

@@ -1,12 +1,14 @@
import { Document, InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose'; import { Document, InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose';
import { Status } from '../constants/auth'; import { Status } from '@mifi/auth-common/lib/enums/status';
import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
import { COLL_STRATEGY } from '../constants/db'; import { COLL_STRATEGY } from '../constants/db';
import { STRATEGIES } from '../constants/strategies';
import { StrategyDocument } from './strategy';
import { verify } from '../utils/password'; import { verify } from '../utils/password';
import { StrategyDocument } from './strategy';
export interface Auth { export interface Auth {
handle?: string;
is2FA?: boolean; is2FA?: boolean;
record: StringSchemaDefinition; record: StringSchemaDefinition;
username: string; username: string;
@@ -35,6 +37,7 @@ export interface AuthModel extends Model<AuthDocument> {
export const AuthSchema = new Schema<AuthDocument, AuthModel>( export const AuthSchema = new Schema<AuthDocument, AuthModel>(
{ {
handle: { type: String },
is2FA: { type: Boolean, default: false }, is2FA: { type: Boolean, default: false },
record: { type: Types.ObjectId, unique: true }, record: { type: Types.ObjectId, unique: true },
status: { status: {

View File

@@ -1,9 +1,8 @@
import { InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose'; import { InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose';
import { Action } from '@mifi/auth-common/lib/enums/action';
import { Payload } from '@mifi/services-common/lib/types/Payload'; import { Payload } from '@mifi/services-common/lib/types/Payload';
import { Action } from '../constants/action';
export interface Log { export interface Log {
action: Action; action: Action;
auth: StringSchemaDefinition; auth: StringSchemaDefinition;

View File

@@ -1,6 +1,7 @@
import { Document, InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose'; import { Document, InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose';
import { STRATEGIES } from '../constants/strategies'; import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
import { encrypt } from '../utils/password'; import { encrypt } from '../utils/password';
import { COLL_AUTH } from '../constants/db'; import { COLL_AUTH } from '../constants/db';
import { AuthDocument } from './auth'; import { AuthDocument } from './auth';

View File

@@ -1,8 +1,9 @@
import { InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose'; import { InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose';
import { TokenType } from '../constants/tokens'; import { TokenType } from '@mifi/auth-common/lib/enums/tokens';
import { getDefaultExpiresFor } from '../utils/getDefaultExpiresFor'; import { getDefaultExpiresFor } from '@mifi/auth-common/lib/helpers/getDefaultExpiresFor';
import { sign, verify } from '../utils/jwt'; import { sign, verify } from '@mifi/auth-common/lib/utils/jwt';
import { SignProps } from '@mifi/auth-common/lib/utils/jwt/sign';
export interface Token { export interface Token {
auth: StringSchemaDefinition; auth: StringSchemaDefinition;
@@ -50,7 +51,7 @@ TokenSchema.statics = {
return sign({ return sign({
sub: `${doc._id}`, sub: `${doc._id}`,
exp: doc.expires, exp: doc.expires,
}); } as SignProps);
}, },
async validateResetToken(token: string) { async validateResetToken(token: string) {

View File

@@ -1,15 +0,0 @@
import { LOGIN_VALID_TIMEOUT, RESET_VALID_TIMEOUT, VERIFY_VALID_TIMEOUT } from '../constants/env';
import { TokenType } from '../constants/tokens';
import { parseTimeoutToMs } from '../utils/parseTimeoutToMs';
export const getDefaultExpiresFor = (type: TokenType | void) => {
if (type === TokenType.RESET) {
return Date.now() + parseTimeoutToMs(RESET_VALID_TIMEOUT);
}
if (type === TokenType.VERIFICATION) {
return Date.now() + parseTimeoutToMs(VERIFY_VALID_TIMEOUT);
}
return Date.now() + parseTimeoutToMs(LOGIN_VALID_TIMEOUT);
};

View File

@@ -1,11 +0,0 @@
import { sign } from '../utils/jwt';
import { LOGIN_VALID_TIMEOUT } from '../constants/env';
import { parseTimeoutToMs } from '../utils/parseTimeoutToMs';
import { AuthDocument } from '../schema/auth';
export const getLoginToken = ({ record: sub, status }: AuthDocument) =>
sign({
sub: <string>sub,
status,
exp: Date.now() + parseTimeoutToMs(LOGIN_VALID_TIMEOUT),
});

View File

@@ -1,35 +0,0 @@
import jwt from 'jsonwebtoken';
import { JWT_AUDIENCE, JWT_ISSUER, JWT_SECRET } from '../constants/env';
export interface TokenProps {
aud?: string;
exp?: number | Date;
iss?: string;
sub: string | null;
[key: string]: any;
}
export type SignProps = string | TokenProps | void;
export const sign = (props: SignProps) => {
const today = new Date();
const { sub = null, ...rest }: TokenProps =
typeof props === 'string' || typeof props === 'undefined' ? { sub: props || null } : props;
let { exp } = rest;
if (!exp) {
exp = new Date(today);
exp.setDate(today.getDate() + parseInt(process.env.JWT_DAYS_VALID as string));
exp = exp.getTime() / 1000;
}
return jwt.sign(
{
exp,
sub,
aud: rest.aud || JWT_AUDIENCE,
iat: today.getTime(),
iss: rest.iss || JWT_ISSUER,
},
JWT_SECRET,
);
};
export const verify = (token: string) => jwt.verify(token, JWT_SECRET);

View File

@@ -1,5 +0,0 @@
import { RESET_ROUTE, ROUTE_PREFIX, VERIFICATION_ROUTE } from '../constants/env';
export const getPasswordResetPath = (token: string) => `${ROUTE_PREFIX}${RESET_ROUTE}?t=${token}`;
export const getVerificationPath = (token: string) => `${ROUTE_PREFIX}${VERIFICATION_ROUTE}?t=${token}`;

View File

@@ -1,13 +0,0 @@
export const parseTimeoutToMs = (timeout: string) => {
const match = timeout.match(/(?<number>\d+)(?<unit>d|h|m)/gi)?.groups || {};
const { number, unit } = match;
switch (unit) {
case 'd':
return 1000 * 60 * 60 * 24 * parseInt(number);
case 'h':
return 1000 * 60 * 60 * parseInt(number);
case 'm':
default:
return 1000 * 60 * parseInt(number) || 1;
}
};

View File

@@ -5,8 +5,3 @@ export const encrypt = (password: string) => {
const hash = pbkdf2Sync(password, salt, 10000, 512, 'sha512').toString('hex'); const hash = pbkdf2Sync(password, salt, 10000, 512, 'sha512').toString('hex');
return `${salt}:${hash}`; return `${salt}:${hash}`;
}; };
export const verify = (test: string, secret: string) => {
const [salt, hash] = secret.split(':');
return pbkdf2Sync(test, salt, 10000, 512, 'sha512').toString('hex') === hash;
};

View File

@@ -0,0 +1,4 @@
import { encrypt } from './encrypt';
import { verify } from './verify';
export { encrypt, verify };

View File

@@ -0,0 +1,6 @@
import { pbkdf2Sync } from 'crypto';
export const verify = (test: string, secret: string) => {
const [salt, hash] = secret.split(':');
return pbkdf2Sync(test, salt, 10000, 512, 'sha512').toString('hex') === hash;
};

View File

@@ -1,11 +0,0 @@
import { sign } from './jwt';
import { LOGIN_VALID_TIMEOUT } from '../constants/env';
import { Status } from '../constants/auth';
import { parseTimeoutToMs } from './parseTimeoutToMs';
export const generateLoginToken = (sub: string, status: Status) =>
sign({
sub,
status,
exp: Date.now() + parseTimeoutToMs(LOGIN_VALID_TIMEOUT),
});

View File

@@ -10,8 +10,8 @@
"rootDirs": ["./", "src/"], "rootDirs": ["./", "src/"],
"strict": true, "strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"sourceMap": false, "sourceMap": true,
"removeComments": true "removeComments": false
}, },
"include": ["src"] "include": ["src"]
} }

169
yarn.lock
View File

@@ -2648,6 +2648,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mifi/auth-common@npm:^1.0.15":
version: 1.0.15
resolution: "@mifi/auth-common@npm:1.0.15::__archiveUrl=https%3A%2F%2Fgit.mifi.dev%2Fapi%2Fpackages%2Fmifi%2Fnpm%2F%2540mifi%252Fauth-common%2F-%2F1.0.15%2Fauth-common-1.0.15.tgz"
dependencies:
"@mifi/breakerbox-db": ^1.0.13
"@mifi/services-common": ^1.0.16
jsonwebtoken: ^9.0.0
checksum: 63931f45a18871362e16e0affc95d16ece4f0a4e6cf05d99146063115fc51d62cf75f262cfad661d76ec4a47f6e5ee32742fe840fb466cb4905829f67999d974
languageName: node
linkType: hard
"@mifi/auth-db@workspace:.": "@mifi/auth-db@workspace:.":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@mifi/auth-db@workspace:." resolution: "@mifi/auth-db@workspace:."
@@ -2655,7 +2666,8 @@ __metadata:
"@babel/core": ^7.21.8 "@babel/core": ^7.21.8
"@babel/preset-env": ^7.21.5 "@babel/preset-env": ^7.21.5
"@babel/preset-typescript": ^7.21.5 "@babel/preset-typescript": ^7.21.5
"@mifi/services-common": 1.x.x "@mifi/auth-common": ^1.0.15
"@mifi/services-common": ^1.0.16
"@tsconfig/node16": ^1.0.4 "@tsconfig/node16": ^1.0.4
"@types/jsonwebtoken": ^9.0.2 "@types/jsonwebtoken": ^9.0.2
"@types/node": ^18.14.0 "@types/node": ^18.14.0
@@ -2682,10 +2694,20 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@mifi/services-common@npm:1.x.x": "@mifi/breakerbox-db@npm:^1.0.13":
version: 1.0.6 version: 1.0.13
resolution: "@mifi/services-common@npm:1.0.6::__archiveUrl=https%3A%2F%2Fgit.mifi.dev%2Fapi%2Fpackages%2Fmifi%2Fnpm%2F%2540mifi%252Fservices-common%2F-%2F1.0.6%2Fservices-common-1.0.6.tgz" resolution: "@mifi/breakerbox-db@npm:1.0.13::__archiveUrl=https%3A%2F%2Fgit.mifi.dev%2Fapi%2Fpackages%2Fmifi%2Fnpm%2F%2540mifi%252Fbreakerbox-db%2F-%2F1.0.13%2Fbreakerbox-db-1.0.13.tgz"
checksum: 22e9aa2714d088a286fc76ddf88c89698764f966ea5f3a208da45df893ec8a0c4e43848f4ba59746cd96b16880dbfa9f3a88610e43aa3825cbdb78afc11f59dc dependencies:
level: ^8.0.0
yaml: ^2.3.1
checksum: 57d431249b2b20387faea8e99546ff67b60b543636d1bf80479e9443f1bda71364cb890ef1d2c8d6cf28623c2b208e68c44076a03c4080417c0b072188362b9c
languageName: node
linkType: hard
"@mifi/services-common@npm:^1.0.16":
version: 1.0.16
resolution: "@mifi/services-common@npm:1.0.16::__archiveUrl=https%3A%2F%2Fgit.mifi.dev%2Fapi%2Fpackages%2Fmifi%2Fnpm%2F%2540mifi%252Fservices-common%2F-%2F1.0.16%2Fservices-common-1.0.16.tgz"
checksum: 9ef5aae54cfdc21b2f1ee78366767890d0fbda0d2459783ffba0af239a7a7a27e52dc2cb2dc82318763a5ecc8ec1bac0570a79f0973b620bd8a5c911a65768bc
languageName: node languageName: node
linkType: hard linkType: hard
@@ -3162,6 +3184,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"abstract-level@npm:^1.0.2":
version: 1.0.3
resolution: "abstract-level@npm:1.0.3"
dependencies:
buffer: ^6.0.3
catering: ^2.1.0
is-buffer: ^2.0.5
level-supports: ^4.0.0
level-transcoder: ^1.0.1
module-error: ^1.0.1
queue-microtask: ^1.2.3
checksum: 70d61a3924526ebc257b138992052f9ff571a6cee5a7660836e37a1cc7081273c3acf465dd2f5e1897b38dc743a6fd9dba14a5d8a2a9d39e5787cd3da99f301d
languageName: node
linkType: hard
"acorn-jsx@npm:^5.3.2": "acorn-jsx@npm:^5.3.2":
version: 5.3.2 version: 5.3.2
resolution: "acorn-jsx@npm:5.3.2" resolution: "acorn-jsx@npm:5.3.2"
@@ -3600,6 +3637,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"browser-level@npm:^1.0.1":
version: 1.0.1
resolution: "browser-level@npm:1.0.1"
dependencies:
abstract-level: ^1.0.2
catering: ^2.1.1
module-error: ^1.0.2
run-parallel-limit: ^1.1.0
checksum: 67fbc77ce832940bfa25073eccff279f512ad56f545deb996a5b23b02316f5e76f4a79d381acc27eda983f5c9a2566aaf9c97e4fdd0748288c4407307537a29b
languageName: node
linkType: hard
"browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": "browserslist@npm:^4.21.3, browserslist@npm:^4.21.5":
version: 4.21.5 version: 4.21.5
resolution: "browserslist@npm:4.21.5" resolution: "browserslist@npm:4.21.5"
@@ -3656,6 +3705,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"buffer@npm:^6.0.3":
version: 6.0.3
resolution: "buffer@npm:6.0.3"
dependencies:
base64-js: ^1.3.1
ieee754: ^1.2.1
checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9
languageName: node
linkType: hard
"builtins@npm:^5.0.1": "builtins@npm:^5.0.1":
version: 5.0.1 version: 5.0.1
resolution: "builtins@npm:5.0.1" resolution: "builtins@npm:5.0.1"
@@ -3750,6 +3809,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"catering@npm:^2.1.0, catering@npm:^2.1.1":
version: 2.1.1
resolution: "catering@npm:2.1.1"
checksum: 205daefa69c935b0c19f3d8f2e0a520dd69aebe9bda55902958003f7c9cff8f967dfb90071b421bd6eb618576f657a89d2bc0986872c9bc04bbd66655e9d4bd6
languageName: node
linkType: hard
"chalk@npm:^1.1.3": "chalk@npm:^1.1.3":
version: 1.1.3 version: 1.1.3
resolution: "chalk@npm:1.1.3" resolution: "chalk@npm:1.1.3"
@@ -3812,6 +3878,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"classic-level@npm:^1.2.0":
version: 1.3.0
resolution: "classic-level@npm:1.3.0"
dependencies:
abstract-level: ^1.0.2
catering: ^2.1.0
module-error: ^1.0.1
napi-macros: ^2.2.2
node-gyp: latest
node-gyp-build: ^4.3.0
checksum: 773da48aef52a041115d413fee8340b357a4da2eb505764f327183b155edd7cc9d24819eb4f707c83dbdae8588024f5dddeb322125567c59d5d1f6f16334cdb9
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0": "clean-stack@npm:^2.0.0":
version: 2.2.0 version: 2.2.0
resolution: "clean-stack@npm:2.2.0" resolution: "clean-stack@npm:2.2.0"
@@ -5221,7 +5301,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ieee754@npm:^1.1.13": "ieee754@npm:^1.1.13, ieee754@npm:^1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "ieee754@npm:1.2.1" resolution: "ieee754@npm:1.2.1"
checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
@@ -5350,6 +5430,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"is-buffer@npm:^2.0.5":
version: 2.0.5
resolution: "is-buffer@npm:2.0.5"
checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42
languageName: node
linkType: hard
"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": "is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7":
version: 1.2.7 version: 1.2.7
resolution: "is-callable@npm:1.2.7" resolution: "is-callable@npm:1.2.7"
@@ -6197,6 +6284,33 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"level-supports@npm:^4.0.0":
version: 4.0.1
resolution: "level-supports@npm:4.0.1"
checksum: d4552b42bb8cdeada07b0f6356c7a90fefe76279147331f291aceae26e3e56d5f927b09ce921647c0230bfe03ddfbdcef332be921e5c2194421ae2bfa3cf6368
languageName: node
linkType: hard
"level-transcoder@npm:^1.0.1":
version: 1.0.1
resolution: "level-transcoder@npm:1.0.1"
dependencies:
buffer: ^6.0.3
module-error: ^1.0.1
checksum: 304f08d802faf3491a533b6d87ad8be3cabfd27f2713bbe9d4c633bf50fcb9460eab5a6776bf015e101ead7ba1c1853e05e7f341112f17a9d0cb37ee5a421a25
languageName: node
linkType: hard
"level@npm:^8.0.0":
version: 8.0.0
resolution: "level@npm:8.0.0"
dependencies:
browser-level: ^1.0.1
classic-level: ^1.2.0
checksum: 13eb25bd71bfdca6cd714d1233adf9da97de9a8a4bf9f28d62a390b5c96d0250abaf983eb90eb8c4e89c7a985bb330750683d106f12670e5ea8fba1d7e608a1f
languageName: node
linkType: hard
"leven@npm:^3.1.0": "leven@npm:^3.1.0":
version: 3.1.0 version: 3.1.0
resolution: "leven@npm:3.1.0" resolution: "leven@npm:3.1.0"
@@ -6538,6 +6652,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"module-error@npm:^1.0.1, module-error@npm:^1.0.2":
version: 1.0.2
resolution: "module-error@npm:1.0.2"
checksum: 5d653e35bd55b3e95f8aee2cdac108082ea892e71b8f651be92cde43e4ee86abee4fa8bd7fc3fe5e68b63926d42f63c54cd17b87a560c31f18739295575a3962
languageName: node
linkType: hard
"mongodb-connection-string-url@npm:^2.5.4": "mongodb-connection-string-url@npm:^2.5.4":
version: 2.6.0 version: 2.6.0
resolution: "mongodb-connection-string-url@npm:2.6.0" resolution: "mongodb-connection-string-url@npm:2.6.0"
@@ -6618,6 +6739,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"napi-macros@npm:^2.2.2":
version: 2.2.2
resolution: "napi-macros@npm:2.2.2"
checksum: c6f9bd71cdbbc37ddc3535aa5be481238641d89585b8a3f4d301cb89abf459e2d294810432bb7d12056d1f9350b1a0899a5afcf460237a3da6c398cf0fec7629
languageName: node
linkType: hard
"natural-compare-lite@npm:^1.4.0": "natural-compare-lite@npm:^1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "natural-compare-lite@npm:1.4.0" resolution: "natural-compare-lite@npm:1.4.0"
@@ -6639,6 +6767,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"node-gyp-build@npm:^4.3.0":
version: 4.6.0
resolution: "node-gyp-build@npm:4.6.0"
bin:
node-gyp-build: bin.js
node-gyp-build-optional: optional.js
node-gyp-build-test: build-test.js
checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15
languageName: node
linkType: hard
"node-gyp@npm:latest": "node-gyp@npm:latest":
version: 9.3.1 version: 9.3.1
resolution: "node-gyp@npm:9.3.1" resolution: "node-gyp@npm:9.3.1"
@@ -7092,7 +7231,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"queue-microtask@npm:^1.2.2": "queue-microtask@npm:^1.2.2, queue-microtask@npm:^1.2.3":
version: 1.2.3 version: 1.2.3
resolution: "queue-microtask@npm:1.2.3" resolution: "queue-microtask@npm:1.2.3"
checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4
@@ -7317,6 +7456,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"run-parallel-limit@npm:^1.1.0":
version: 1.1.0
resolution: "run-parallel-limit@npm:1.1.0"
dependencies:
queue-microtask: ^1.2.2
checksum: 672c3b87e7f939c684b9965222b361421db0930223ed1e43ebf0e7e48ccc1a022ea4de080bef4d5468434e2577c33b7681e3f03b7593fdc49ad250a55381123c
languageName: node
linkType: hard
"run-parallel@npm:^1.1.9": "run-parallel@npm:^1.1.9":
version: 1.2.0 version: 1.2.0
resolution: "run-parallel@npm:1.2.0" resolution: "run-parallel@npm:1.2.0"
@@ -8255,6 +8403,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yaml@npm:^2.3.1":
version: 2.3.1
resolution: "yaml@npm:2.3.1"
checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54
languageName: node
linkType: hard
"yargs-parser@npm:^13.1.2": "yargs-parser@npm:^13.1.2":
version: 13.1.2 version: 13.1.2
resolution: "yargs-parser@npm:13.1.2" resolution: "yargs-parser@npm:13.1.2"