feathers-authentication-management
Version:
Adds sign up verification, forgotten password reset, and other capabilities to local feathers-authentication
413 lines (351 loc) • 12.8 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Application, Id, NullableId, Paginated, Params, Query } from '@feathersjs/feathers';
//#region general
export interface User {
isVerified: boolean
verifyToken: string
verifyShortToken: string
verifyExpires: Date | number // unix
verifyChanges: VerifyChanges
resetToken: string
resetShortToken: string
resetExpires: Date | number // unix
resetAttempts: number
password: string
[key: string]: any
[key: number]: any
}
export type ArrayOrPaginated<T> = T[] | Paginated<T>;
export type UsersArrayOrPaginated = ArrayOrPaginated<User>;
export type NotifierOptions = Record<string, any>;
export type VerifyChanges = Record<string, any>;
export interface Tokens {
resetToken?: string
resetShortToken?: string
verifyShortToken?: string
verifyToken?: string
}
export type IdentifyUser = Query;
export type Notifier = (type: NotificationType, user: Partial<User>, notifierOptions?: NotifierOptions) => any;
export type SanitizeUserForClient = (user: Partial<User>) => SanitizedUser;
export type SanitizedUser = Partial<User>;
export type NotificationType =
'resendVerifySignup' |
'verifySignup' |
'verifySignupSetPassword' |
'sendResetPwd' |
'resetPwd' |
'passwordChange' |
'identityChange';
// #endregion
export type AuthenticationManagementAction =
'checkUnique' |
'resendVerifySignup' |
'verifySignupLong' |
'verifySignupShort' |
'verifySignupSetPasswordLong' |
'verifySignupSetPasswordShort' |
'sendResetPwd' |
'resetPwdLong' |
'resetPwdShort' |
'passwordChange' |
'identityChange' |
'options';
export type ActionPathMap<T> = {
[key in Exclude<AuthenticationManagementAction, 'options'>]: T
};
export type GetUserDataCheckProps = Array<'isNotVerified' | 'isNotVerifiedOrHasVerifyChanges' | 'isVerified' | 'verifyNotExpired' | 'resetNotExpired'>;
//#region options
export interface AuthenticationManagementServiceOptions {
/** The path of the service for user items.
* @default "/users" */
service: string
/** If `false` (default) it is impossible to reset passwords even if e-mail is not verified.
* @default false */
skipIsVerifiedCheck: boolean
/** The notifier function handles the sending of any notification depending on the action.
*/
notifier: Notifier
/** Half the length of the long token. Default is 15, giving tokens of 30 characters length.
* @default 15 */
longTokenLen: number
/** Length of short token (e.g. for sms).
* @default 6 */
shortTokenLen: number
/** If `true` short tokens contain only digits. Otherwise also characters.
* @default true */
shortTokenDigits: boolean
/** Lifetime for password reset tokens in ms. Default is 2*60*60*1000 = 7200000 (2 hours).
* @default 7200000 */
resetDelay: number
/** Lifetime for e-mail verification tokens in ms. Default is 5*24*60*60*1000 = 432000000 (5 days).
* @default 432000000
*/
delay: number
/** Amount of times a user can submit an invalid token before the current token gets removed from the database. Default is 0.
* @default 0 */
resetAttempts: number
/** Use the same reset token if the user resets password twice in a short period. In this case token is not hashed in the database. Default is false.
* @default false */
reuseResetToken: boolean
/** Property names in the user item which uniquely identify the user, e.g. `['username', 'email', 'cellphone']`. The default is `['email']`. Only these properties may be changed with verification by the service. At least one of these properties must be provided whenever a short token is used, as the short token alone is too susceptible to brute force attack.
* @default ['email']
*/
identifyUserProps: string[]
/** Used for sanitization reasions. By default, the user object is in the response e. g. of a password reset request. To reply with empty object use `() => ({})`.
* Deletes the following properties by default: `['password', 'verifyExpires', 'verifyToken', 'verifyShortToken', 'verifyChanges', 'resetExpires', 'resetToken', 'resetShortToken']`
*/
sanitizeUserForClient: (user: User) => Partial<User>
/** Property name of the password field on your `'/users'` service
* @default 'password' */
passwordField: string
/** Should we skip hashing password for `passwordField` ? If `true`, password won't be hashed by feathers-authentication-management when patching the user. This must be set to `true` if you are hashing your password field using resolvers. */
skipPasswordHash: boolean
/** Pass params from f-a-m service to `/users` service */
passParams: (params) => Params | Promise<Params>
}
export type AuthenticationManagementSetupOptions = AuthenticationManagementServiceOptions & { path: string };
export type VerifySignupLongServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'notifier' |
'sanitizeUserForClient' |
'passParams'>;
export type VerifySignupOptions = VerifySignupLongServiceOptions & { app: Application };
export type VerifySignupShortServiceOptions = VerifySignupLongServiceOptions & {
identifyUserProps: string[]
};
export type VerifySignupWithShortTokenOptions = VerifySignupShortServiceOptions & { app: Application };
export type VerifySignupSetPasswordLongServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'sanitizeUserForClient' |
'notifier' |
'passwordField' |
'skipPasswordHash' |
'passParams'>;
export type VerifySignupSetPasswordOptions = VerifySignupSetPasswordLongServiceOptions & { app: Application };
export type PasswordChangeServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'identifyUserProps' |
'notifier' |
'sanitizeUserForClient' |
'passwordField' |
'skipPasswordHash' |
'passParams'>;
export type PasswordChangeOptions = PasswordChangeServiceOptions & { app: Application };
export type VerifySignupSetPasswordShortServiceOptions = VerifySignupSetPasswordLongServiceOptions
& Pick<AuthenticationManagementServiceOptions, 'identifyUserProps'>;
export type VerifySignupSetPasswordWithShortTokenOptions = VerifySignupSetPasswordShortServiceOptions & { app: Application };
export type ResetPasswordServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'skipIsVerifiedCheck' |
'reuseResetToken' |
'notifier' |
'sanitizeUserForClient' |
'passwordField' |
'skipPasswordHash' |
'passParams'>;
export type ResetPasswordOptions = ResetPasswordServiceOptions & { app: Application };
export type ResetPwdWithShortServiceOptions = ResetPasswordServiceOptions & {
identifyUserProps: string[]
};
export type ResetPwdWithShortTokenOptions = ResetPwdWithShortServiceOptions & { app: Application };
export type ResendVerifySignupServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'identifyUserProps' |
'delay' |
'longTokenLen' |
'shortTokenLen' |
'shortTokenDigits' |
'notifier' |
'sanitizeUserForClient' |
'passParams'>;
export type ResendVerifySignupOptions = ResendVerifySignupServiceOptions & { app: Application };
export type IdentityChangeServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'identifyUserProps' |
'delay' |
'longTokenLen' |
'shortTokenLen' |
'shortTokenDigits' |
'notifier' |
'sanitizeUserForClient' |
'passwordField' |
'passParams'>;
export type IdentityChangeOptions = IdentityChangeServiceOptions & { app: Application };
export type CheckUniqueServiceOptions = Pick<AuthenticationManagementServiceOptions, 'service' | 'passParams'>;
export type CheckUniqueOptions = CheckUniqueServiceOptions & { app: Application };
export type SendResetPwdServiceOptions = Pick<AuthenticationManagementServiceOptions,
'service' |
'identifyUserProps' |
'skipIsVerifiedCheck' |
'reuseResetToken' |
'resetDelay' |
'sanitizeUserForClient' |
'resetAttempts' |
'shortTokenLen' |
'longTokenLen' |
'shortTokenDigits' |
'notifier' |
'passwordField' |
'passParams'>;
export type SendResetPwdOptions = SendResetPwdServiceOptions & { app: Application };
//#endregion
//#region client
export interface AuthenticationManagementClient {
checkUnique: (identifyUser: IdentifyUser, ownId?: NullableId, ifErrMsg?: boolean) => Promise<void>
resendVerifySignup: (identifyUser: IdentifyUser, notifierOptions: NotifierOptions) => Promise<void>
verifySignupLong: (verifyToken: string) => Promise<void>
verifySignupShort: (verifyToken: string, identifyUser: IdentifyUser) => Promise<void>
sendResetPwd: (IdentifyUser: IdentifyUser, notifierOptions: NotifierOptions) => Promise<void>
resetPwdLong: (resetToken: string, password: string) => Promise<void>
resetPwdShort: (resetShortToken: string, identifyUser: IdentifyUser, password: string) => Promise<void>
passwordChange: (oldPassword: string, password: string, identifyUser: IdentifyUser) => Promise<void>
identityChange: (password: string, changesIdentifyUser: NotifierOptions, identifyUser: IdentifyUser) => Promise<void>
authenticate: (email: string, password: string, cb?: (err: Error | null, user?: Partial<User>) => void) => Promise<any>
}
//#endregion
//#region service data
export interface WithNotifierOptions {
notifierOptions?: NotifierOptions
}
export type AuthenticationManagementData =
DataCheckUniqueWithAction |
DataIdentityChangeWithAction |
DataOptions |
DataPasswordChangeWithAction |
DataResendVerifySignupWithAction |
DataResetPwdLongWithAction |
DataResetPwdShortWithAction |
DataSendResetPwdWithAction |
DataVerifySignupLongWithAction |
DataVerifySignupSetPasswordLongWithAction |
DataVerifySignupSetPasswordShortWithAction |
DataVerifySignupShortWithAction;
export interface DataCheckUnique {
user: IdentifyUser
ownId?: Id
meta?: {
noErrMsg: boolean
}
}
export interface DataCheckUniqueWithAction {
action: 'checkUnique'
value: IdentifyUser
ownId?: Id
meta?: {
noErrMsg: boolean
}
}
export interface DataIdentityChange extends WithNotifierOptions {
changes: Record<string, any>
password: string
user: IdentifyUser
}
export interface DataIdentityChangeWithAction extends WithNotifierOptions {
action: 'identityChange'
value: {
changes: Record<string, any>
password: string
user: IdentifyUser
}
}
export interface DataPasswordChange extends WithNotifierOptions {
oldPassword: string
password: string
user: IdentifyUser
}
export interface DataPasswordChangeWithAction extends WithNotifierOptions {
action: 'passwordChange'
value: {
oldPassword: string
password: string
user: IdentifyUser
}
}
export interface DataResendVerifySignup extends WithNotifierOptions {
user: IdentifyUser
}
export interface DataResendVerifySignupWithAction extends WithNotifierOptions {
action: 'resendVerifySignup'
value: IdentifyUser
}
export interface DataResetPwdLong extends WithNotifierOptions {
password: string
token: string
}
export interface DataResetPwdLongWithAction extends WithNotifierOptions {
action: 'resetPwdLong'
value: {
password: string
token: string
}
}
export interface DataResetPwdShort extends WithNotifierOptions {
password: string
token: string
user: IdentifyUser
}
export interface DataResetPwdShortWithAction extends WithNotifierOptions {
action: 'resetPwdShort'
value: {
password: string
token: string
user: IdentifyUser
}
}
export interface DataSendResetPwd extends WithNotifierOptions {
user: IdentifyUser
}
export interface DataSendResetPwdWithAction extends WithNotifierOptions {
action: 'sendResetPwd'
value: IdentifyUser
}
export interface DataVerifySignupLong extends WithNotifierOptions {
token: string
}
export interface DataVerifySignupLongWithAction extends WithNotifierOptions {
action: 'verifySignupLong'
value: string
}
export interface DataVerifySignupSetPasswordLong extends WithNotifierOptions {
password: string
token: string
}
export interface DataVerifySignupSetPasswordLongWithAction extends WithNotifierOptions {
action: 'verifySignupSetPasswordLong'
value: {
password: string
token: string
}
}
export interface DataVerifySignupSetPasswordShort extends WithNotifierOptions {
password: string
token: string
user: IdentifyUser
}
export interface DataVerifySignupSetPasswordShortWithAction extends WithNotifierOptions {
action: 'verifySignupSetPasswordShort'
value: {
password: string
token: string
user: IdentifyUser
}
}
export interface DataVerifySignupShort extends WithNotifierOptions {
token: string
user: IdentifyUser
}
export interface DataVerifySignupShortWithAction extends WithNotifierOptions {
action: 'verifySignupShort'
value: {
token: string
user: IdentifyUser
}
}
export interface DataOptions {
action: 'options'
}
//#endregion
export interface ClientOptions {
path: string
}