Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add verify feature #321

Merged
merged 15 commits into from
Jan 10, 2025
758 changes: 758 additions & 0 deletions packages/nestjs-auth-verify/README.md

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions packages/nestjs-auth-verify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@concepta/nestjs-auth-verify",
"version": "6.0.0-alpha.0",
"description": "Rockets NestJS Auth Verify",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "BSD-3-Clause",
"publishConfig": {
"access": "public"
},
"files": [
"dist/**/!(*.spec|*.e2e-spec|*.fixture).{js,d.ts}"
],
"dependencies": {
"@concepta/nestjs-common": "^6.0.0-alpha.0",
"@concepta/nestjs-exception": "^6.0.0-alpha.0",
"@concepta/typeorm-common": "^6.0.0-alpha.0",
"@nestjs/common": "^10.4.1",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.4.1",
"@nestjs/swagger": "^7.4.0"
},
"devDependencies": {
"@concepta/nestjs-auth-jwt": "^6.0.0-alpha.0",
"@concepta/nestjs-authentication": "^6.0.0-alpha.0",
"@concepta/nestjs-crud": "^6.0.0-alpha.0",
"@concepta/nestjs-email": "^6.0.0-alpha.0",
"@concepta/nestjs-jwt": "^6.0.0-alpha.0",
"@concepta/nestjs-otp": "^6.0.0-alpha.0",
"@concepta/nestjs-password": "^6.0.0-alpha.0",
"@concepta/nestjs-typeorm-ext": "^6.0.0-alpha.0",
"@concepta/nestjs-user": "^6.0.0-alpha.0",
"@concepta/typeorm-seeding": "^4.0.0",
"@nestjs/testing": "^10.4.1",
"@nestjs/typeorm": "^10.0.2",
"jest-mock-extended": "^2.0.9",
"supertest": "^6.3.4"
},
"peerDependencies": {
"class-transformer": "*",
"class-validator": "*",
"rxjs": "^7.1.0",
"typeorm": "^0.3.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Module } from '@nestjs/common';

import { TypeOrmExtModule } from '@concepta/nestjs-typeorm-ext';
import { CrudModule } from '@concepta/nestjs-crud';
import { PasswordModule } from '@concepta/nestjs-password';
import { AuthenticationModule } from '@concepta/nestjs-authentication';
import { JwtModule } from '@concepta/nestjs-jwt';
import { AuthJwtModule } from '@concepta/nestjs-auth-jwt';
import { OtpModule, OtpService } from '@concepta/nestjs-otp';
import { EmailModule, EmailService } from '@concepta/nestjs-email';
import {
UserLookupService,
UserModule,
UserMutateService,
} from '@concepta/nestjs-user';

import { AuthVerifyModule } from '../auth-verify.module';
import { UserOtpEntityFixture } from './user/entities/user-otp-entity.fixture';
import { UserEntityFixture } from './user/entities/user-entity.fixture';

import { default as ormConfig } from './ormconfig.fixture';
import { MailerServiceFixture } from './email/mailer.service.fixture';

@Module({
imports: [
TypeOrmExtModule.forRoot(ormConfig),
CrudModule.forRoot({}),
JwtModule.forRoot({}),
AuthenticationModule.forRoot({
settings: {
disableGuard: (context, guard) =>
guard.constructor.name === 'AuthJwtGuard' &&
context.getClass().name === 'UserController',
},
}),
AuthJwtModule.forRootAsync({
inject: [UserLookupService],
useFactory: (userLookupService: UserLookupService) => ({
userLookupService,
}),
}),
AuthVerifyModule.forRootAsync({
inject: [UserLookupService, UserMutateService, OtpService, EmailService],
useFactory: (
userLookupService,
userMutateService,
otpService,
emailService,
) => ({
userLookupService,
userMutateService,
otpService,
emailService,
}),
}),
OtpModule.forRoot({
entities: {
userOtp: {
entity: UserOtpEntityFixture,
},
},
}),
PasswordModule.forRoot({}),
UserModule.forRoot({
entities: {
user: {
entity: UserEntityFixture,
},
},
}),
EmailModule.forRoot({
mailerService: new MailerServiceFixture(),
}),
],
})
export class AppModuleDbFixture {}
52 changes: 52 additions & 0 deletions packages/nestjs-auth-verify/src/__fixtures__/app.module.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Module } from '@nestjs/common';
import { EmailModule, EmailService } from '@concepta/nestjs-email';
import { AuthenticationModule } from '@concepta/nestjs-authentication';
import { AuthJwtModule } from '@concepta/nestjs-auth-jwt';
import { JwtModule } from '@concepta/nestjs-jwt';

import { AuthVerifyModule } from '../auth-verify.module';

import { TypeOrmModuleFixture } from './typeorm.module.fixture';
import { OtpServiceFixture } from './otp/otp.service.fixture';
import { UserLookupServiceFixture } from './user/services/user-lookup.service.fixture';
import { UserMutateServiceFixture } from './user/services/user-mutate.service.fixture';
import { UserModuleFixture } from './user/user.module.fixture';
import { OtpModuleFixture } from './otp/otp.module.fixture';
import { MailerServiceFixture } from './email/mailer.service.fixture';

@Module({
imports: [
TypeOrmModuleFixture,
JwtModule.forRoot({}),
AuthenticationModule.forRoot({}),
AuthJwtModule.forRootAsync({
inject: [UserLookupServiceFixture],
useFactory: (userLookupService: UserLookupServiceFixture) => ({
userLookupService,
}),
}),
AuthVerifyModule.forRootAsync({
inject: [
EmailService,
OtpServiceFixture,
UserLookupServiceFixture,
UserMutateServiceFixture,
],
useFactory: (
emailService,
otpService,
userLookupService,
userMutateService,
) => ({
emailService,
otpService,
userLookupService,
userMutateService,
}),
}),
EmailModule.forRoot({ mailerService: new MailerServiceFixture() }),
OtpModuleFixture,
UserModuleFixture,
],
})
export class AppModuleFixture {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Injectable } from '@nestjs/common';
import {
EmailSendInterface,
EmailSendOptionsInterface,
} from '@concepta/nestjs-common';

@Injectable()
export class MailerServiceFixture implements EmailSendInterface {
sendMail(_sendMailOptions: EmailSendOptionsInterface): Promise<void> {
throw new Error('Method not implemented.');
}
}
12 changes: 12 additions & 0 deletions packages/nestjs-auth-verify/src/__fixtures__/ormconfig.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DataSourceOptions } from 'typeorm';
import { UserOtpEntityFixture } from './user/entities/user-otp-entity.fixture';
import { UserEntityFixture } from './user/entities/user-entity.fixture';

const config: DataSourceOptions = {
type: 'sqlite',
database: ':memory:',
synchronize: true,
entities: [UserEntityFixture, UserOtpEntityFixture],
};

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { OtpServiceFixture } from './otp.service.fixture';

@Global()
@Module({
providers: [OtpServiceFixture],
exports: [OtpServiceFixture],
})
export class OtpModuleFixture {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { randomUUID } from 'crypto';
import { Injectable } from '@nestjs/common';

import {
OtpCreateParamsInterface,
OtpInterface,
ReferenceAssigneeInterface,
ReferenceIdInterface,
} from '@concepta/nestjs-common';

import { AuthVerifyOtpServiceInterface } from '../../interfaces/auth-verify-otp.service.interface';
import { UserFixture } from '../user/user.fixture';

@Injectable()
export class OtpServiceFixture implements AuthVerifyOtpServiceInterface {
async create({ otp }: OtpCreateParamsInterface): Promise<OtpInterface> {
const { assignee, category, type } = otp;
return {
id: randomUUID(),
category,
type,
assignee,
passcode: 'GOOD_PASSCODE',
expirationDate: new Date(),
dateCreated: new Date(),
dateUpdated: new Date(),
dateDeleted: null,
version: 1,
};
}

async validate(
_assignment: string,
otp: Pick<OtpInterface, 'category' | 'passcode'>,
_deleteIfValid: boolean,
): Promise<ReferenceAssigneeInterface<ReferenceIdInterface<string>> | null> {
return otp.passcode === 'GOOD_PASSCODE' ? { assignee: UserFixture } : null;
}

async clear(
_assignment: string,
_otp: Pick<OtpInterface, 'category' | 'assignee'>,
): Promise<void> {
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Global, Module } from '@nestjs/common';
import { getEntityManagerToken } from '@nestjs/typeorm';
import { createEntityManagerMock } from '@concepta/typeorm-common';

@Global()
@Module({
providers: [
{
provide: getEntityManagerToken(),
useFactory: createEntityManagerMock,
},
],
exports: [getEntityManagerToken()],
})
export class TypeOrmModuleFixture {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Entity, OneToMany } from 'typeorm';
import { UserSqliteEntity } from '@concepta/nestjs-user';
import { UserOtpEntityFixture } from './user-otp-entity.fixture';

/**
* User Entity Fixture
*/
@Entity()
export class UserEntityFixture extends UserSqliteEntity {
@OneToMany(() => UserOtpEntityFixture, (userOtp) => userOtp.assignee)
userOtps?: UserOtpEntityFixture[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Column, Entity, ManyToOne } from 'typeorm';
import { ReferenceIdInterface } from '@concepta/nestjs-common';
import { UserEntityFixture } from './user-entity.fixture';
import { OtpInterface } from '@concepta/nestjs-common';
import { CommonSqliteEntity } from '@concepta/typeorm-common';

/**
* Otp Entity Fixture
*/
@Entity()
export class UserOtpEntityFixture
extends CommonSqliteEntity
implements OtpInterface
{
@Column()
category!: string;

@Column({ nullable: true })
type!: string;

@Column()
passcode!: string;

@Column({ type: 'datetime' })
expirationDate!: Date;

@ManyToOne(() => UserEntityFixture, (user) => user.userOtps)
assignee!: ReferenceIdInterface;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from '@nestjs/common';
import {
ReferenceEmail,
ReferenceIdInterface,
ReferenceSubject,
} from '@concepta/nestjs-common';

import { AuthVerifyUserLookupServiceInterface } from '../../../interfaces/auth-verify-user-lookup.service.interface';

import { UserFixture } from '../user.fixture';

@Injectable()
export class UserLookupServiceFixture
implements AuthVerifyUserLookupServiceInterface
{
async byId(
id: string,
): ReturnType<AuthVerifyUserLookupServiceInterface['byId']> {
if (id === UserFixture.id) {
return UserFixture;
} else {
throw new Error();
}
}

async byEmail(
email: ReferenceEmail,
): ReturnType<AuthVerifyUserLookupServiceInterface['byEmail']> {
return email === UserFixture.email ? UserFixture : null;
}

async bySubject(subject: ReferenceSubject): Promise<ReferenceIdInterface> {
throw new Error(`Method not implemented, can't get ${subject}.`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';
import {
ReferenceActiveInterface,
ReferenceIdInterface,
} from '@concepta/nestjs-common';

import { AuthVerifyUserMutateServiceInterface } from '../../../interfaces/auth-verify-user-mutate.service.interface';

import { UserFixture } from '../user.fixture';

@Injectable()
export class UserMutateServiceFixture
implements AuthVerifyUserMutateServiceInterface
{
async update(
object: ReferenceIdInterface<string> & ReferenceActiveInterface,
): ReturnType<AuthVerifyUserMutateServiceInterface['update']> {
if (object.id === UserFixture.id) {
return UserFixture;
} else {
throw new Error();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const UserFixture = {
id: 'abc',
email: '[email protected]',
username: '[email protected]',
active: true,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Global, Module } from '@nestjs/common';
import { UserLookupServiceFixture } from './services/user-lookup.service.fixture';
import { UserMutateServiceFixture } from './services/user-mutate.service.fixture';

@Global()
@Module({
providers: [UserLookupServiceFixture, UserMutateServiceFixture],
exports: [UserLookupServiceFixture, UserMutateServiceFixture],
})
export class UserModuleFixture {}
Loading
Loading