Skip to content

Commit

Permalink
feat(password): refactor password services for forward compat with up…
Browse files Browse the repository at this point in the history
…coming features
  • Loading branch information
MrMaz committed Dec 4, 2023
1 parent bb18d7f commit 526593e
Show file tree
Hide file tree
Showing 15 changed files with 345 additions and 129 deletions.
4 changes: 2 additions & 2 deletions packages/nestjs-auth-local/src/auth-local.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const AUTH_LOCAL_MODULE_DEFAULT_SETTINGS_TOKEN =
export const AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN =
'AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN';

export const AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN =
'AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN';
export const AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN =
'AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN';

export const AUTH_LOCAL_STRATEGY_NAME = 'local';
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';

import { AppModuleDbFixture } from './__fixtures__/app.module.fixture';
import { PasswordStorageService } from '@concepta/nestjs-password';
import { PasswordValidationService } from '@concepta/nestjs-password';
import { LOGIN_SUCCESS } from './__fixtures__/user/constants';

describe('AuthLocalController (e2e)', () => {
Expand All @@ -13,7 +13,7 @@ describe('AuthLocalController (e2e)', () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModuleDbFixture],
})
.overrideProvider(PasswordStorageService)
.overrideProvider(PasswordValidationService)
.useValue({
validateObject: () => {
return true;
Expand Down
30 changes: 15 additions & 15 deletions packages/nestjs-auth-local/src/auth-local.module-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ConfigModule } from '@nestjs/config';

import { createSettingsProvider } from '@concepta/nestjs-common';
import {
PasswordStorageService,
PasswordStorageServiceInterface,
PasswordValidationService,
PasswordValidationServiceInterface,
} from '@concepta/nestjs-password';
import {
IssueTokenService,
Expand All @@ -17,7 +17,7 @@ import {

import {
AUTH_LOCAL_MODULE_ISSUE_TOKEN_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_SETTINGS_TOKEN,
AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_VALIDATE_USER_SERVICE_TOKEN,
Expand Down Expand Up @@ -81,7 +81,7 @@ export function createAuthLocalExports(): string[] {
AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_ISSUE_TOKEN_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_VALIDATE_USER_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN,
];
}

Expand All @@ -92,14 +92,14 @@ export function createAuthLocalProviders(options: {
return [
...(options.providers ?? []),
IssueTokenService,
PasswordStorageService,
PasswordValidationService,
AuthLocalStrategy,
AuthLocalValidateUserService,
createAuthLocalOptionsProvider(options.overrides),
createAuthLocalValidateUserServiceProvider(options.overrides),
createAuthLocalIssueTokenServiceProvider(options.overrides),
createAuthLocalUserLookupServiceProvider(options.overrides),
createAuthLocalPasswordStorageServiceProvider(options.overrides),
createAuthLocalPasswordValidationServiceProvider(options.overrides),
];
}

Expand Down Expand Up @@ -133,18 +133,18 @@ export function createAuthLocalValidateUserServiceProvider(
inject: [
RAW_OPTIONS_TOKEN,
AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN,
],
useFactory: async (
options: AuthLocalOptionsInterface,
userLookupService: AuthLocalUserLookupServiceInterface,
passwordStorageService: PasswordStorageServiceInterface,
passwordValidationService: PasswordValidationServiceInterface,
) =>
optionsOverrides?.validateUserService ??
options.validateUserService ??
new AuthLocalValidateUserService(
userLookupService,
passwordStorageService,
passwordValidationService,
),
};
}
Expand All @@ -165,18 +165,18 @@ export function createAuthLocalIssueTokenServiceProvider(
};
}

export function createAuthLocalPasswordStorageServiceProvider(
export function createAuthLocalPasswordValidationServiceProvider(
optionsOverrides?: AuthLocalOptions,
): Provider {
return {
provide: AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN,
inject: [RAW_OPTIONS_TOKEN, PasswordStorageService],
provide: AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN,
inject: [RAW_OPTIONS_TOKEN, PasswordValidationService],
useFactory: async (
options: AuthLocalOptionsInterface,
defaultService: PasswordStorageServiceInterface,
defaultService: PasswordValidationServiceInterface,
) =>
optionsOverrides?.passwordStorageService ??
options.passwordStorageService ??
optionsOverrides?.passwordValidationService ??
options.passwordValidationService ??
defaultService,
};
}
Expand Down
10 changes: 5 additions & 5 deletions packages/nestjs-auth-local/src/auth-local.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import {
IssueTokenServiceInterface,
} from '@concepta/nestjs-authentication';
import {
PasswordStorageService,
PasswordStorageServiceInterface,
PasswordValidationService,
PasswordValidationServiceInterface,
} from '@concepta/nestjs-password';

import {
Expand Down Expand Up @@ -50,7 +50,7 @@ describe(AuthLocalModule, () => {
let userLookupService: AuthLocalUserLookupServiceInterface;
let validateUserService: AuthLocalUserLookupServiceInterface;
let issueTokenService: IssueTokenServiceInterface;
let passwordStorageService: PasswordStorageServiceInterface;
let passwordValidationService: PasswordValidationServiceInterface;

describe(AuthLocalModule.forRoot, () => {
beforeEach(async () => {
Expand Down Expand Up @@ -203,14 +203,14 @@ describe(AuthLocalModule, () => {
userLookupService = module.get(UserLookupServiceFixture);
validateUserService = module.get(AuthLocalValidateUserService);
issueTokenService = module.get(IssueTokenService);
passwordStorageService = module.get(PasswordStorageService);
passwordValidationService = module.get(PasswordValidationService);
}

function commonTests() {
expect(authLocalModule).toBeInstanceOf(AuthLocalModule);
expect(userLookupService).toBeInstanceOf(UserLookupServiceFixture);
expect(issueTokenService).toBeInstanceOf(IssueTokenService);
expect(passwordStorageService).toBeInstanceOf(PasswordStorageService);
expect(passwordValidationService).toBeInstanceOf(PasswordValidationService);
expect(validateUserService).toBeInstanceOf(AuthLocalValidateUserService);
}
});
Expand Down
12 changes: 6 additions & 6 deletions packages/nestjs-auth-local/src/auth-local.strategy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PasswordStorageService } from '@concepta/nestjs-password';
import { PasswordValidationService } from '@concepta/nestjs-password';
import { UnauthorizedException } from '@nestjs/common';
import { randomUUID } from 'crypto';
import { mock } from 'jest-mock-extended';
Expand All @@ -18,7 +18,7 @@ describe(AuthLocalStrategy, () => {
let settings: AuthLocalSettingsInterface;
let userLookUpService: AuthLocalUserLookupServiceInterface;
let validateUserService: AuthLocalValidateUserServiceInterface;
let passwordStorageService: PasswordStorageService;
let passwordValidationService: PasswordValidationService;
let authLocalStrategy: AuthLocalStrategy;

beforeEach(async () => {
Expand All @@ -29,10 +29,10 @@ describe(AuthLocalStrategy, () => {
});

userLookUpService = mock<AuthLocalUserLookupServiceInterface>();
passwordStorageService = mock<PasswordStorageService>();
passwordValidationService = mock<PasswordValidationService>();
validateUserService = new AuthLocalValidateUserService(
userLookUpService,
passwordStorageService,
passwordValidationService,
);
authLocalStrategy = new AuthLocalStrategy(settings, validateUserService);

Expand All @@ -53,7 +53,7 @@ describe(AuthLocalStrategy, () => {
describe(AuthLocalStrategy.prototype.validate, () => {
it('should return user', async () => {
jest
.spyOn(passwordStorageService, 'validateObject')
.spyOn(passwordValidationService, 'validateObject')
.mockResolvedValue(true);

const result = await authLocalStrategy.validate(USERNAME, PASSWORD);
Expand All @@ -74,7 +74,7 @@ describe(AuthLocalStrategy, () => {

it('should be invalid on passwordService.validateObject', async () => {
jest
.spyOn(passwordStorageService, 'validateObject')
.spyOn(passwordValidationService, 'validateObject')
.mockResolvedValue(false);

const t = () => authLocalStrategy.validate(USERNAME, PASSWORD);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IssueTokenServiceInterface } from '@concepta/nestjs-authentication';
import { PasswordStorageServiceInterface } from '@concepta/nestjs-password';
import { PasswordValidationServiceInterface } from '@concepta/nestjs-password';
import { AuthLocalSettingsInterface } from './auth-local-settings.interface';
import { AuthLocalUserLookupServiceInterface } from './auth-local-user-lookup-service.interface';
import { AuthLocalValidateUserServiceInterface } from './auth-local-validate-user-service.interface';
Expand All @@ -21,9 +21,9 @@ export interface AuthLocalOptionsInterface {
validateUserService?: AuthLocalValidateUserServiceInterface;

/**
* Implementation of a class to handle password storage
* Implementation of a class to handle password validation
*/
passwordStorageService?: PasswordStorageServiceInterface;
passwordValidationService?: PasswordValidationServiceInterface;

/**
* Settings
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Inject, Injectable } from '@nestjs/common';
import { ReferenceIdInterface } from '@concepta/ts-core';
import { ValidateUserService } from '@concepta/nestjs-authentication';
import { PasswordStorageServiceInterface } from '@concepta/nestjs-password';
import { PasswordValidationServiceInterface } from '@concepta/nestjs-password';
import {
AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN,
AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN,
} from '../auth-local.constants';
import { AuthLocalValidateUserInterface } from '../interfaces/auth-local-validate-user.interface';
Expand All @@ -18,8 +18,8 @@ export class AuthLocalValidateUserService
constructor(
@Inject(AUTH_LOCAL_MODULE_USER_LOOKUP_SERVICE_TOKEN)
private userLookupService: AuthLocalUserLookupServiceInterface,
@Inject(AUTH_LOCAL_MODULE_PASSWORD_STORAGE_SERVICE_TOKEN)
private passwordStorageService: PasswordStorageServiceInterface,
@Inject(AUTH_LOCAL_MODULE_PASSWORD_VALIDATION_SERVICE_TOKEN)
private passwordValidationService: PasswordValidationServiceInterface,
) {
super();
}
Expand All @@ -46,10 +46,10 @@ export class AuthLocalValidateUserService
}

// validate password
const isValid = await this.passwordStorageService.validateObject(
dto.password,
user,
);
const isValid = await this.passwordValidationService.validateObject({
passwordPlain: dto.password,
object: user,
});

// password is valid?
if (!isValid) {
Expand Down
2 changes: 2 additions & 0 deletions packages/nestjs-password/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ export * from './enum/password-strength.enum';

export * from './services/password-creation.service';
export * from './services/password-storage.service';
export * from './services/password-validation.service';
export * from './services/password-strength.service';

export * from './interfaces/password-options.interface';
export * from './interfaces/password-storage.interface';
export * from './interfaces/password-storage-service.interface';
export * from './interfaces/password-validation-service.interface';
export * from './interfaces/password-creation-service.interface';
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,4 @@ export interface PasswordStorageServiceInterface {
): Promise<
Omit<T, 'password'> | (Omit<T, 'password'> & PasswordStorageInterface)
>;

/**
* Validate if password matches and its valid.
*
* @param passwordPlain Plain text password
* @param passwordHash Password hashed
* @param salt salt to be used on plain password to see it match
*/
validate(
passwordPlain: string,
passwordHash: string,
salt: string,
): Promise<boolean>;

/**
* Validate password on an object.
*
* @param passwordPlain Plain text password
* @param object The object on which the password and salt are stored
*/
validateObject<T extends PasswordStorageInterface>(
passwordPlain: string,
object: T,
): Promise<boolean>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PasswordStorageInterface } from './password-storage.interface';

/**
* Password Storage Validation Interface
*/
export interface PasswordValidationServiceInterface {
/**
* Validate if password matches and its valid.
*
* @param options.passwordPlain Plain text password
* @param options.passwordHash Password hashed
* @param options.passwordSalt salt to be used on plain password to see it match
*/
validate(options: {
passwordPlain: string;
passwordHash: string;
passwordSalt: string;
}): Promise<boolean>;

/**
* Validate password on an object.
*
* @param options.passwordPlain Plain text password
* @param options.object The object on which the password and salt are stored
*/
validateObject<T extends PasswordStorageInterface>(options: {
passwordPlain: string;
object: T;
}): Promise<boolean>;
}
3 changes: 3 additions & 0 deletions packages/nestjs-password/src/password.module-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PasswordSettingsInterface } from './interfaces/password-settings.interf
import { PasswordCreationService } from './services/password-creation.service';
import { PasswordStorageService } from './services/password-storage.service';
import { PasswordStrengthService } from './services/password-strength.service';
import { PasswordValidationService } from './services/password-validation.service';
import { passwordDefaultConfig } from './config/password-default.config';

const RAW_OPTIONS_TOKEN = Symbol('__PASSWORD_MODULE_RAW_OPTIONS_TOKEN__');
Expand Down Expand Up @@ -68,6 +69,7 @@ export function createPasswordProviders(overrides: {
PasswordCreationService,
PasswordStrengthService,
PasswordStorageService,
PasswordValidationService,
];
}

Expand All @@ -79,6 +81,7 @@ export function createPasswordExports(): Required<
PasswordCreationService,
PasswordStrengthService,
PasswordStorageService,
PasswordValidationService,
];
}

Expand Down
Loading

0 comments on commit 526593e

Please sign in to comment.