@aranzatech/aranza-auth 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -2
- package/README.md +180 -16
- package/SECURITY.md +11 -0
- package/dist/{auth-repository.interface-9PpDVOs8.d.cts → auth-repository.interface--1rv0RCD.d.cts} +22 -3
- package/dist/{auth-repository.interface-9PpDVOs8.d.ts → auth-repository.interface--1rv0RCD.d.ts} +22 -3
- package/dist/index.cjs +621 -186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -11
- package/dist/index.d.ts +142 -11
- package/dist/index.js +617 -188
- package/dist/index.js.map +1 -1
- package/dist/mongo/index.cjs +34 -6
- package/dist/mongo/index.cjs.map +1 -1
- package/dist/mongo/index.d.cts +4 -1
- package/dist/mongo/index.d.ts +4 -1
- package/dist/mongo/index.js +34 -6
- package/dist/mongo/index.js.map +1 -1
- package/package.json +12 -1
package/dist/mongo/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DynamicModule } from '@nestjs/common';
|
|
2
2
|
import * as mongoose from 'mongoose';
|
|
3
3
|
import { Schema, HydratedDocument, Model } from 'mongoose';
|
|
4
|
-
import {
|
|
4
|
+
import { g as AuthIdentifierField, I as IAuthRepository, C as CreateAccountData, B as BaseAuthAccount, e as AuthAccountWithSecrets, i as AuthLockoutOptions } from '../auth-repository.interface--1rv0RCD.cjs';
|
|
5
5
|
|
|
6
6
|
interface MongoAuthFeatureOptions {
|
|
7
7
|
/** Mongoose model name. Default: `AuthAccount`. */
|
|
@@ -56,6 +56,7 @@ declare class MongoAuthRepository implements IAuthRepository {
|
|
|
56
56
|
findById(id: string): Promise<BaseAuthAccount | null>;
|
|
57
57
|
findByIdWithSecrets(id: string): Promise<AuthAccountWithSecrets | null>;
|
|
58
58
|
updateRefreshTokenHash(id: string, hash: string | null): Promise<void>;
|
|
59
|
+
rotateRefreshTokenHashIfMatch(id: string, expectedHash: string, newHash: string): Promise<boolean>;
|
|
59
60
|
updatePasswordHash(id: string, passwordHash: string): Promise<void>;
|
|
60
61
|
setEmailVerificationToken(id: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
61
62
|
findByEmailVerificationTokenHash(tokenHash: string): Promise<BaseAuthAccount | null>;
|
|
@@ -65,6 +66,8 @@ declare class MongoAuthRepository implements IAuthRepository {
|
|
|
65
66
|
clearResetToken(id: string): Promise<void>;
|
|
66
67
|
recordLoginSuccess(id: string): Promise<void>;
|
|
67
68
|
recordLoginFailure(id: string, lockout?: AuthLockoutOptions): Promise<void>;
|
|
69
|
+
setAccountDisabled(id: string, disabled: boolean): Promise<void>;
|
|
70
|
+
findUnverifiedByEmail(email: string): Promise<BaseAuthAccount | null>;
|
|
68
71
|
private identifierQuery;
|
|
69
72
|
}
|
|
70
73
|
|
package/dist/mongo/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DynamicModule } from '@nestjs/common';
|
|
2
2
|
import * as mongoose from 'mongoose';
|
|
3
3
|
import { Schema, HydratedDocument, Model } from 'mongoose';
|
|
4
|
-
import {
|
|
4
|
+
import { g as AuthIdentifierField, I as IAuthRepository, C as CreateAccountData, B as BaseAuthAccount, e as AuthAccountWithSecrets, i as AuthLockoutOptions } from '../auth-repository.interface--1rv0RCD.js';
|
|
5
5
|
|
|
6
6
|
interface MongoAuthFeatureOptions {
|
|
7
7
|
/** Mongoose model name. Default: `AuthAccount`. */
|
|
@@ -56,6 +56,7 @@ declare class MongoAuthRepository implements IAuthRepository {
|
|
|
56
56
|
findById(id: string): Promise<BaseAuthAccount | null>;
|
|
57
57
|
findByIdWithSecrets(id: string): Promise<AuthAccountWithSecrets | null>;
|
|
58
58
|
updateRefreshTokenHash(id: string, hash: string | null): Promise<void>;
|
|
59
|
+
rotateRefreshTokenHashIfMatch(id: string, expectedHash: string, newHash: string): Promise<boolean>;
|
|
59
60
|
updatePasswordHash(id: string, passwordHash: string): Promise<void>;
|
|
60
61
|
setEmailVerificationToken(id: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
61
62
|
findByEmailVerificationTokenHash(tokenHash: string): Promise<BaseAuthAccount | null>;
|
|
@@ -65,6 +66,8 @@ declare class MongoAuthRepository implements IAuthRepository {
|
|
|
65
66
|
clearResetToken(id: string): Promise<void>;
|
|
66
67
|
recordLoginSuccess(id: string): Promise<void>;
|
|
67
68
|
recordLoginFailure(id: string, lockout?: AuthLockoutOptions): Promise<void>;
|
|
69
|
+
setAccountDisabled(id: string, disabled: boolean): Promise<void>;
|
|
70
|
+
findUnverifiedByEmail(email: string): Promise<BaseAuthAccount | null>;
|
|
68
71
|
private identifierQuery;
|
|
69
72
|
}
|
|
70
73
|
|
package/dist/mongo/index.js
CHANGED
|
@@ -17,6 +17,7 @@ function toAccount(doc) {
|
|
|
17
17
|
disabled: doc.disabled,
|
|
18
18
|
...doc.lastLoginAt != null ? { lastLoginAt: doc.lastLoginAt } : {},
|
|
19
19
|
...doc.passwordChangedAt != null ? { passwordChangedAt: doc.passwordChangedAt } : {},
|
|
20
|
+
...doc.lockedUntil != null ? { lockedUntil: doc.lockedUntil } : {},
|
|
20
21
|
createdAt: doc.createdAt,
|
|
21
22
|
updatedAt: doc.updatedAt
|
|
22
23
|
};
|
|
@@ -76,6 +77,14 @@ var MongoAuthRepository = class {
|
|
|
76
77
|
if (!Types.ObjectId.isValid(id)) return;
|
|
77
78
|
await this.authModel.updateOne({ _id: id }, { $set: { refreshTokenHash: hash } }).exec();
|
|
78
79
|
}
|
|
80
|
+
async rotateRefreshTokenHashIfMatch(id, expectedHash, newHash) {
|
|
81
|
+
if (!Types.ObjectId.isValid(id)) return false;
|
|
82
|
+
const result = await this.authModel.updateOne(
|
|
83
|
+
{ _id: id, refreshTokenHash: expectedHash },
|
|
84
|
+
{ $set: { refreshTokenHash: newHash } }
|
|
85
|
+
).exec();
|
|
86
|
+
return result.matchedCount > 0;
|
|
87
|
+
}
|
|
79
88
|
async updatePasswordHash(id, passwordHash) {
|
|
80
89
|
if (!Types.ObjectId.isValid(id)) return;
|
|
81
90
|
await this.authModel.updateOne(
|
|
@@ -153,14 +162,29 @@ var MongoAuthRepository = class {
|
|
|
153
162
|
if (!Types.ObjectId.isValid(id)) return;
|
|
154
163
|
const maxAttempts = lockout?.maxAttempts ?? DEFAULT_LOCKOUT_MAX_ATTEMPTS;
|
|
155
164
|
const lockoutDurationMs = lockout?.lockoutDurationMs ?? DEFAULT_LOCKOUT_DURATION_MS;
|
|
156
|
-
const doc = await this.authModel.
|
|
165
|
+
const doc = await this.authModel.findOneAndUpdate(
|
|
166
|
+
{ _id: id },
|
|
167
|
+
{ $inc: { failedLoginAttempts: 1 } },
|
|
168
|
+
{ new: true }
|
|
169
|
+
).select("failedLoginAttempts").exec();
|
|
157
170
|
if (doc == null) return;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
if (doc.failedLoginAttempts >= maxAttempts) {
|
|
172
|
+
await this.authModel.updateOne(
|
|
173
|
+
{ _id: id },
|
|
174
|
+
{ $set: { lockedUntil: new Date(Date.now() + lockoutDurationMs) } }
|
|
175
|
+
).exec();
|
|
162
176
|
}
|
|
163
|
-
|
|
177
|
+
}
|
|
178
|
+
async setAccountDisabled(id, disabled) {
|
|
179
|
+
if (!Types.ObjectId.isValid(id)) return;
|
|
180
|
+
await this.authModel.updateOne({ _id: id }, { $set: { disabled } }).exec();
|
|
181
|
+
}
|
|
182
|
+
async findUnverifiedByEmail(email) {
|
|
183
|
+
const doc = await this.authModel.findOne({
|
|
184
|
+
email: normalizeIdentifier(email),
|
|
185
|
+
emailVerified: false
|
|
186
|
+
}).exec();
|
|
187
|
+
return doc != null ? toAccount(doc) : null;
|
|
164
188
|
}
|
|
165
189
|
identifierQuery(identifier) {
|
|
166
190
|
const normalized = normalizeIdentifier(identifier);
|
|
@@ -236,6 +260,10 @@ baseAuthAccountSchema.index(
|
|
|
236
260
|
partialFilterExpression: { username: { $type: "string" } }
|
|
237
261
|
}
|
|
238
262
|
);
|
|
263
|
+
baseAuthAccountSchema.index(
|
|
264
|
+
{ email: 1, disabled: 1 },
|
|
265
|
+
{ partialFilterExpression: { email: { $type: "string" } } }
|
|
266
|
+
);
|
|
239
267
|
baseAuthAccountSchema.index({ emailVerificationTokenHash: 1 });
|
|
240
268
|
baseAuthAccountSchema.index({ resetTokenHash: 1 });
|
|
241
269
|
baseAuthAccountSchema.index({ emailVerificationExpiresAt: 1 });
|
package/dist/mongo/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/constants/lockout.constants.ts","../../src/mongo/mongo-auth.repository.ts","../../src/mongo/schemas/base-auth-account.schema.ts","../../src/mongo/mongo-auth.module.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export const DEFAULT_LOCKOUT_MAX_ATTEMPTS = 5;\nexport const DEFAULT_LOCKOUT_DURATION_MS = 15 * 60 * 1000;\n","import { Injectable } from \"@nestjs/common\";\nimport { Types, type Model } from \"mongoose\";\n\nimport type { AuthIdentifierField } from \"../interfaces/auth-config.interface\";\nimport type { AuthLockoutOptions } from \"../interfaces/auth-config.interface\";\nimport type {\n AuthAccountWithSecrets,\n BaseAuthAccount,\n} from \"../interfaces/auth-hooks.interface\";\nimport type {\n CreateAccountData,\n IAuthRepository,\n} from \"../interfaces/auth-repository.interface\";\nimport {\n normalizeIdentifier,\n resolveRegisterIdentifier,\n} from \"../utils/identifier.util\";\nimport {\n DEFAULT_LOCKOUT_DURATION_MS,\n DEFAULT_LOCKOUT_MAX_ATTEMPTS,\n} from \"../constants/lockout.constants\";\nimport type { BaseAuthAccountDocument } from \"./schemas/base-auth-account.schema\";\n\nfunction toAccount(doc: BaseAuthAccountDocument): BaseAuthAccount {\n return {\n id: doc._id.toString(),\n ...(doc.email != null ? { email: doc.email } : {}),\n ...(doc.username != null ? { username: doc.username } : {}),\n emailVerified: doc.emailVerified,\n disabled: doc.disabled,\n ...(doc.lastLoginAt != null ? { lastLoginAt: doc.lastLoginAt } : {}),\n ...(doc.passwordChangedAt != null\n ? { passwordChangedAt: doc.passwordChangedAt }\n : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n };\n}\n\nfunction toAccountWithSecrets(\n doc: BaseAuthAccountDocument,\n): AuthAccountWithSecrets {\n return {\n ...toAccount(doc),\n passwordHash: doc.passwordHash,\n refreshTokenHash: doc.refreshTokenHash ?? null,\n failedLoginAttempts: doc.failedLoginAttempts ?? 0,\n lockedUntil: doc.lockedUntil ?? null,\n };\n}\n\n@Injectable()\nexport class MongoAuthRepository implements IAuthRepository {\n constructor(\n private readonly authModel: Model<BaseAuthAccountDocument>,\n private readonly identifierField: AuthIdentifierField = \"email\",\n ) {}\n\n async create(data: CreateAccountData): Promise<BaseAuthAccount> {\n const identifier = resolveRegisterIdentifier(\n data,\n this.identifierField,\n );\n\n const created = new this.authModel({\n email:\n this.identifierField === \"email\"\n ? identifier\n : data.email != null\n ? normalizeIdentifier(data.email)\n : undefined,\n username:\n this.identifierField === \"username\"\n ? identifier\n : data.username != null\n ? normalizeIdentifier(data.username)\n : undefined,\n passwordHash: data.passwordHash,\n emailVerified: data.emailVerified ?? false,\n disabled: false,\n });\n\n const saved = await created.save();\n return toAccount(saved);\n }\n\n async findByEmail(email: string): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne({ email: normalizeIdentifier(email) })\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdentifier(identifier: string): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne(this.identifierQuery(identifier))\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdentifierWithSecrets(\n identifier: string,\n ): Promise<AuthAccountWithSecrets | null> {\n const doc = await this.authModel\n .findOne(this.identifierQuery(identifier))\n .select(\"+passwordHash +refreshTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async findById(id: string): Promise<BaseAuthAccount | null> {\n if (!Types.ObjectId.isValid(id)) return null;\n const doc = await this.authModel.findById(id).exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdWithSecrets(id: string): Promise<AuthAccountWithSecrets | null> {\n if (!Types.ObjectId.isValid(id)) return null;\n const doc = await this.authModel\n .findById(id)\n .select(\"+passwordHash +refreshTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async updateRefreshTokenHash(\n id: string,\n hash: string | null,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne({ _id: id }, { $set: { refreshTokenHash: hash } })\n .exec();\n }\n\n async updatePasswordHash(id: string, passwordHash: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $set: { passwordHash, passwordChangedAt: new Date() } },\n )\n .exec();\n }\n\n async setEmailVerificationToken(\n id: string,\n tokenHash: string,\n expiresAt: Date,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: {\n emailVerificationTokenHash: tokenHash,\n emailVerificationExpiresAt: expiresAt,\n },\n },\n )\n .exec();\n }\n\n async findByEmailVerificationTokenHash(\n tokenHash: string,\n ): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne({\n emailVerificationTokenHash: tokenHash,\n emailVerificationExpiresAt: { $gt: new Date() },\n })\n .select(\"+emailVerificationTokenHash\")\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async markEmailVerified(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: { emailVerified: true },\n $unset: {\n emailVerificationTokenHash: \"\",\n emailVerificationExpiresAt: \"\",\n },\n },\n )\n .exec();\n }\n\n async setResetToken(\n id: string,\n tokenHash: string,\n expiresAt: Date,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $set: { resetTokenHash: tokenHash, resetTokenExpiresAt: expiresAt } },\n )\n .exec();\n }\n\n async findByResetTokenHash(\n tokenHash: string,\n ): Promise<AuthAccountWithSecrets | null> {\n const doc = await this.authModel\n .findOne({\n resetTokenHash: tokenHash,\n resetTokenExpiresAt: { $gt: new Date() },\n })\n .select(\"+passwordHash +resetTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async clearResetToken(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $unset: { resetTokenHash: \"\", resetTokenExpiresAt: \"\" } },\n )\n .exec();\n }\n\n async recordLoginSuccess(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: {\n lastLoginAt: new Date(),\n failedLoginAttempts: 0,\n lockedUntil: null,\n },\n },\n )\n .exec();\n }\n\n async recordLoginFailure(\n id: string,\n lockout?: AuthLockoutOptions,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n\n const maxAttempts = lockout?.maxAttempts ?? DEFAULT_LOCKOUT_MAX_ATTEMPTS;\n const lockoutDurationMs =\n lockout?.lockoutDurationMs ?? DEFAULT_LOCKOUT_DURATION_MS;\n\n const doc = await this.authModel\n .findById(id)\n .select(\"failedLoginAttempts\")\n .exec();\n if (doc == null) return;\n\n const attempts = (doc.failedLoginAttempts ?? 0) + 1;\n const update: Record<string, unknown> = { failedLoginAttempts: attempts };\n\n if (attempts >= maxAttempts) {\n update.lockedUntil = new Date(Date.now() + lockoutDurationMs);\n }\n\n await this.authModel.updateOne({ _id: id }, { $set: update }).exec();\n }\n\n private identifierQuery(identifier: string) {\n const normalized = normalizeIdentifier(identifier);\n return this.identifierField === \"email\"\n ? { email: normalized }\n : { username: normalized };\n }\n}\n","import { Prop, Schema, SchemaFactory } from \"@nestjs/mongoose\";\nimport type { HydratedDocument } from \"mongoose\";\n\nexport const BASE_AUTH_ACCOUNT_MODEL = \"AuthAccount\";\n\n@Schema({\n timestamps: true,\n collection: \"auth_accounts\",\n})\nexport class BaseAuthAccountSchema {\n @Prop({ type: String, required: false, trim: true, lowercase: true })\n email?: string;\n\n @Prop({ type: String, required: false, trim: true, lowercase: true })\n username?: string;\n\n @Prop({ type: String, required: true, select: false })\n passwordHash!: string;\n\n @Prop({ type: String, required: false, select: false, default: null })\n refreshTokenHash?: string | null;\n\n @Prop({ type: Boolean, default: false })\n emailVerified!: boolean;\n\n @Prop({ type: Boolean, default: false })\n disabled!: boolean;\n\n @Prop({ type: String, required: false, select: false })\n emailVerificationTokenHash?: string;\n\n @Prop({ type: Date, required: false })\n emailVerificationExpiresAt?: Date;\n\n @Prop({ type: String, required: false, select: false })\n resetTokenHash?: string;\n\n @Prop({ type: Date, required: false })\n resetTokenExpiresAt?: Date;\n\n @Prop({ type: Number, default: 0 })\n failedLoginAttempts!: number;\n\n @Prop({ type: Date, required: false, default: null })\n lockedUntil?: Date | null;\n\n @Prop({ type: Date, required: false })\n lastLoginAt?: Date;\n\n @Prop({ type: Date, required: false })\n passwordChangedAt?: Date;\n\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport type BaseAuthAccountDocument = HydratedDocument<BaseAuthAccountSchema>;\n\nexport const baseAuthAccountSchema =\n SchemaFactory.createForClass(BaseAuthAccountSchema);\n\nbaseAuthAccountSchema.index(\n { email: 1 },\n {\n unique: true,\n partialFilterExpression: { email: { $type: \"string\" } },\n },\n);\n\nbaseAuthAccountSchema.index(\n { username: 1 },\n {\n unique: true,\n partialFilterExpression: { username: { $type: \"string\" } },\n },\n);\n\nbaseAuthAccountSchema.index({ emailVerificationTokenHash: 1 });\nbaseAuthAccountSchema.index({ resetTokenHash: 1 });\nbaseAuthAccountSchema.index({ emailVerificationExpiresAt: 1 });\nbaseAuthAccountSchema.index({ resetTokenExpiresAt: 1 });\n","import { DynamicModule, Module } from \"@nestjs/common\";\nimport { getModelToken } from \"@nestjs/mongoose\";\nimport { MongooseModule } from \"@nestjs/mongoose\";\nimport type { Model, Schema } from \"mongoose\";\n\nimport { AUTH_MODULE_OPTIONS, AUTH_REPOSITORY } from \"../constants/tokens\";\nimport type { AuthIdentifierField } from \"../interfaces/auth-config.interface\";\nimport type { AuthModuleOptions } from \"../interfaces/auth-config.interface\";\nimport { MongoAuthRepository } from \"./mongo-auth.repository\";\nimport {\n BASE_AUTH_ACCOUNT_MODEL,\n baseAuthAccountSchema,\n type BaseAuthAccountDocument,\n} from \"./schemas/base-auth-account.schema\";\n\nexport interface MongoAuthFeatureOptions {\n /** Mongoose model name. Default: `AuthAccount`. */\n name?: string;\n /** Custom schema (e.g. extended with orgId, roleId). Default: base auth schema. */\n schema?: Schema;\n /** Identifier field when AuthModule options are not yet available. Default: `email`. */\n identifierField?: AuthIdentifierField;\n}\n\n@Module({})\nexport class MongoAuthModule {\n static forFeature(options?: MongoAuthFeatureOptions): DynamicModule {\n const name = options?.name ?? BASE_AUTH_ACCOUNT_MODEL;\n const schema = options?.schema ?? baseAuthAccountSchema;\n const identifierField = options?.identifierField ?? \"email\";\n\n return {\n module: MongoAuthModule,\n imports: [MongooseModule.forFeature([{ name, schema }])],\n providers: [\n {\n provide: MongoAuthRepository,\n useFactory: (\n model: Model<BaseAuthAccountDocument>,\n authOptions?: AuthModuleOptions,\n ) =>\n new MongoAuthRepository(\n model,\n authOptions?.identifierField ?? identifierField,\n ),\n inject: [\n getModelToken(name),\n { token: AUTH_MODULE_OPTIONS, optional: true },\n ],\n },\n {\n provide: AUTH_REPOSITORY,\n useExisting: MongoAuthRepository,\n },\n ],\n exports: [AUTH_REPOSITORY, MongooseModule],\n };\n }\n}"]}
|
|
1
|
+
{"version":3,"sources":["../../src/constants/lockout.constants.ts","../../src/mongo/mongo-auth.repository.ts","../../src/mongo/schemas/base-auth-account.schema.ts","../../src/mongo/mongo-auth.module.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export const DEFAULT_LOCKOUT_MAX_ATTEMPTS = 5;\nexport const DEFAULT_LOCKOUT_DURATION_MS = 15 * 60 * 1000;\n","import { Injectable } from \"@nestjs/common\";\nimport { Types, type Model } from \"mongoose\";\n\nimport type { AuthIdentifierField } from \"../interfaces/auth-config.interface\";\nimport type { AuthLockoutOptions } from \"../interfaces/auth-config.interface\";\nimport type {\n AuthAccountWithSecrets,\n BaseAuthAccount,\n} from \"../interfaces/auth-hooks.interface\";\nimport type {\n CreateAccountData,\n IAuthRepository,\n} from \"../interfaces/auth-repository.interface\";\nimport {\n normalizeIdentifier,\n resolveRegisterIdentifier,\n} from \"../utils/identifier.util\";\nimport {\n DEFAULT_LOCKOUT_DURATION_MS,\n DEFAULT_LOCKOUT_MAX_ATTEMPTS,\n} from \"../constants/lockout.constants\";\nimport type { BaseAuthAccountDocument } from \"./schemas/base-auth-account.schema\";\n\nfunction toAccount(doc: BaseAuthAccountDocument): BaseAuthAccount {\n return {\n id: doc._id.toString(),\n ...(doc.email != null ? { email: doc.email } : {}),\n ...(doc.username != null ? { username: doc.username } : {}),\n emailVerified: doc.emailVerified,\n disabled: doc.disabled,\n ...(doc.lastLoginAt != null ? { lastLoginAt: doc.lastLoginAt } : {}),\n ...(doc.passwordChangedAt != null\n ? { passwordChangedAt: doc.passwordChangedAt }\n : {}),\n ...(doc.lockedUntil != null ? { lockedUntil: doc.lockedUntil } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n };\n}\n\nfunction toAccountWithSecrets(\n doc: BaseAuthAccountDocument,\n): AuthAccountWithSecrets {\n return {\n ...toAccount(doc),\n passwordHash: doc.passwordHash,\n refreshTokenHash: doc.refreshTokenHash ?? null,\n failedLoginAttempts: doc.failedLoginAttempts ?? 0,\n lockedUntil: doc.lockedUntil ?? null,\n };\n}\n\n@Injectable()\nexport class MongoAuthRepository implements IAuthRepository {\n constructor(\n private readonly authModel: Model<BaseAuthAccountDocument>,\n private readonly identifierField: AuthIdentifierField = \"email\",\n ) {}\n\n async create(data: CreateAccountData): Promise<BaseAuthAccount> {\n const identifier = resolveRegisterIdentifier(\n data,\n this.identifierField,\n );\n\n const created = new this.authModel({\n email:\n this.identifierField === \"email\"\n ? identifier\n : data.email != null\n ? normalizeIdentifier(data.email)\n : undefined,\n username:\n this.identifierField === \"username\"\n ? identifier\n : data.username != null\n ? normalizeIdentifier(data.username)\n : undefined,\n passwordHash: data.passwordHash,\n emailVerified: data.emailVerified ?? false,\n disabled: false,\n });\n\n const saved = await created.save();\n return toAccount(saved);\n }\n\n async findByEmail(email: string): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne({ email: normalizeIdentifier(email) })\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdentifier(identifier: string): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne(this.identifierQuery(identifier))\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdentifierWithSecrets(\n identifier: string,\n ): Promise<AuthAccountWithSecrets | null> {\n const doc = await this.authModel\n .findOne(this.identifierQuery(identifier))\n .select(\"+passwordHash +refreshTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async findById(id: string): Promise<BaseAuthAccount | null> {\n if (!Types.ObjectId.isValid(id)) return null;\n const doc = await this.authModel.findById(id).exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async findByIdWithSecrets(id: string): Promise<AuthAccountWithSecrets | null> {\n if (!Types.ObjectId.isValid(id)) return null;\n const doc = await this.authModel\n .findById(id)\n .select(\"+passwordHash +refreshTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async updateRefreshTokenHash(\n id: string,\n hash: string | null,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne({ _id: id }, { $set: { refreshTokenHash: hash } })\n .exec();\n }\n\n async rotateRefreshTokenHashIfMatch(\n id: string,\n expectedHash: string,\n newHash: string,\n ): Promise<boolean> {\n if (!Types.ObjectId.isValid(id)) return false;\n\n const result = await this.authModel\n .updateOne(\n { _id: id, refreshTokenHash: expectedHash },\n { $set: { refreshTokenHash: newHash } },\n )\n .exec();\n\n return result.matchedCount > 0;\n }\n\n async updatePasswordHash(id: string, passwordHash: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $set: { passwordHash, passwordChangedAt: new Date() } },\n )\n .exec();\n }\n\n async setEmailVerificationToken(\n id: string,\n tokenHash: string,\n expiresAt: Date,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: {\n emailVerificationTokenHash: tokenHash,\n emailVerificationExpiresAt: expiresAt,\n },\n },\n )\n .exec();\n }\n\n async findByEmailVerificationTokenHash(\n tokenHash: string,\n ): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne({\n emailVerificationTokenHash: tokenHash,\n emailVerificationExpiresAt: { $gt: new Date() },\n })\n .select(\"+emailVerificationTokenHash\")\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n async markEmailVerified(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: { emailVerified: true },\n $unset: {\n emailVerificationTokenHash: \"\",\n emailVerificationExpiresAt: \"\",\n },\n },\n )\n .exec();\n }\n\n async setResetToken(\n id: string,\n tokenHash: string,\n expiresAt: Date,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $set: { resetTokenHash: tokenHash, resetTokenExpiresAt: expiresAt } },\n )\n .exec();\n }\n\n async findByResetTokenHash(\n tokenHash: string,\n ): Promise<AuthAccountWithSecrets | null> {\n const doc = await this.authModel\n .findOne({\n resetTokenHash: tokenHash,\n resetTokenExpiresAt: { $gt: new Date() },\n })\n .select(\"+passwordHash +resetTokenHash\")\n .exec();\n return doc != null ? toAccountWithSecrets(doc) : null;\n }\n\n async clearResetToken(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n { $unset: { resetTokenHash: \"\", resetTokenExpiresAt: \"\" } },\n )\n .exec();\n }\n\n async recordLoginSuccess(id: string): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne(\n { _id: id },\n {\n $set: {\n lastLoginAt: new Date(),\n failedLoginAttempts: 0,\n lockedUntil: null,\n },\n },\n )\n .exec();\n }\n\n async recordLoginFailure(\n id: string,\n lockout?: AuthLockoutOptions,\n ): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n\n const maxAttempts = lockout?.maxAttempts ?? DEFAULT_LOCKOUT_MAX_ATTEMPTS;\n const lockoutDurationMs =\n lockout?.lockoutDurationMs ?? DEFAULT_LOCKOUT_DURATION_MS;\n\n const doc = await this.authModel\n .findOneAndUpdate(\n { _id: id },\n { $inc: { failedLoginAttempts: 1 } },\n { new: true },\n )\n .select(\"failedLoginAttempts\")\n .exec();\n\n if (doc == null) return;\n\n if (doc.failedLoginAttempts >= maxAttempts) {\n await this.authModel\n .updateOne(\n { _id: id },\n { $set: { lockedUntil: new Date(Date.now() + lockoutDurationMs) } },\n )\n .exec();\n }\n }\n\n async setAccountDisabled(id: string, disabled: boolean): Promise<void> {\n if (!Types.ObjectId.isValid(id)) return;\n await this.authModel\n .updateOne({ _id: id }, { $set: { disabled } })\n .exec();\n }\n\n async findUnverifiedByEmail(email: string): Promise<BaseAuthAccount | null> {\n const doc = await this.authModel\n .findOne({\n email: normalizeIdentifier(email),\n emailVerified: false,\n })\n .exec();\n return doc != null ? toAccount(doc) : null;\n }\n\n private identifierQuery(identifier: string) {\n const normalized = normalizeIdentifier(identifier);\n return this.identifierField === \"email\"\n ? { email: normalized }\n : { username: normalized };\n }\n}\n","import { Prop, Schema, SchemaFactory } from \"@nestjs/mongoose\";\nimport type { HydratedDocument } from \"mongoose\";\n\nexport const BASE_AUTH_ACCOUNT_MODEL = \"AuthAccount\";\n\n@Schema({\n timestamps: true,\n collection: \"auth_accounts\",\n})\nexport class BaseAuthAccountSchema {\n @Prop({ type: String, required: false, trim: true, lowercase: true })\n email?: string;\n\n @Prop({ type: String, required: false, trim: true, lowercase: true })\n username?: string;\n\n @Prop({ type: String, required: true, select: false })\n passwordHash!: string;\n\n @Prop({ type: String, required: false, select: false, default: null })\n refreshTokenHash?: string | null;\n\n @Prop({ type: Boolean, default: false })\n emailVerified!: boolean;\n\n @Prop({ type: Boolean, default: false })\n disabled!: boolean;\n\n @Prop({ type: String, required: false, select: false })\n emailVerificationTokenHash?: string;\n\n @Prop({ type: Date, required: false })\n emailVerificationExpiresAt?: Date;\n\n @Prop({ type: String, required: false, select: false })\n resetTokenHash?: string;\n\n @Prop({ type: Date, required: false })\n resetTokenExpiresAt?: Date;\n\n @Prop({ type: Number, default: 0 })\n failedLoginAttempts!: number;\n\n @Prop({ type: Date, required: false, default: null })\n lockedUntil?: Date | null;\n\n @Prop({ type: Date, required: false })\n lastLoginAt?: Date;\n\n @Prop({ type: Date, required: false })\n passwordChangedAt?: Date;\n\n createdAt!: Date;\n updatedAt!: Date;\n}\n\nexport type BaseAuthAccountDocument = HydratedDocument<BaseAuthAccountSchema>;\n\nexport const baseAuthAccountSchema =\n SchemaFactory.createForClass(BaseAuthAccountSchema);\n\nbaseAuthAccountSchema.index(\n { email: 1 },\n {\n unique: true,\n partialFilterExpression: { email: { $type: \"string\" } },\n },\n);\n\nbaseAuthAccountSchema.index(\n { username: 1 },\n {\n unique: true,\n partialFilterExpression: { username: { $type: \"string\" } },\n },\n);\n\nbaseAuthAccountSchema.index(\n { email: 1, disabled: 1 },\n { partialFilterExpression: { email: { $type: \"string\" } } },\n);\n\nbaseAuthAccountSchema.index({ emailVerificationTokenHash: 1 });\nbaseAuthAccountSchema.index({ resetTokenHash: 1 });\n// Expiry enforced at query time ($gt: now) — do not use document TTL (would delete accounts).\nbaseAuthAccountSchema.index({ emailVerificationExpiresAt: 1 });\nbaseAuthAccountSchema.index({ resetTokenExpiresAt: 1 });\n","import { DynamicModule, Module } from \"@nestjs/common\";\nimport { getModelToken } from \"@nestjs/mongoose\";\nimport { MongooseModule } from \"@nestjs/mongoose\";\nimport type { Model, Schema } from \"mongoose\";\n\nimport { AUTH_MODULE_OPTIONS, AUTH_REPOSITORY } from \"../constants/tokens\";\nimport type { AuthIdentifierField } from \"../interfaces/auth-config.interface\";\nimport type { AuthModuleOptions } from \"../interfaces/auth-config.interface\";\nimport { MongoAuthRepository } from \"./mongo-auth.repository\";\nimport {\n BASE_AUTH_ACCOUNT_MODEL,\n baseAuthAccountSchema,\n type BaseAuthAccountDocument,\n} from \"./schemas/base-auth-account.schema\";\n\nexport interface MongoAuthFeatureOptions {\n /** Mongoose model name. Default: `AuthAccount`. */\n name?: string;\n /** Custom schema (e.g. extended with orgId, roleId). Default: base auth schema. */\n schema?: Schema;\n /** Identifier field when AuthModule options are not yet available. Default: `email`. */\n identifierField?: AuthIdentifierField;\n}\n\n@Module({})\nexport class MongoAuthModule {\n static forFeature(options?: MongoAuthFeatureOptions): DynamicModule {\n const name = options?.name ?? BASE_AUTH_ACCOUNT_MODEL;\n const schema = options?.schema ?? baseAuthAccountSchema;\n const identifierField = options?.identifierField ?? \"email\";\n\n return {\n module: MongoAuthModule,\n imports: [MongooseModule.forFeature([{ name, schema }])],\n providers: [\n {\n provide: MongoAuthRepository,\n useFactory: (\n model: Model<BaseAuthAccountDocument>,\n authOptions?: AuthModuleOptions,\n ) =>\n new MongoAuthRepository(\n model,\n authOptions?.identifierField ?? identifierField,\n ),\n inject: [\n getModelToken(name),\n { token: AUTH_MODULE_OPTIONS, optional: true },\n ],\n },\n {\n provide: AUTH_REPOSITORY,\n useExisting: MongoAuthRepository,\n },\n ],\n exports: [AUTH_REPOSITORY, MongooseModule],\n };\n }\n}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aranzatech/aranza-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Módulo de autenticación extensible para NestJS — JWT, refresh tokens, register/login y adapter MongoDB",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AranzaTech",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"test": "vitest run",
|
|
56
56
|
"test:watch": "vitest",
|
|
57
57
|
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"ci": "npm run lint && npm run test:coverage && node scripts/coverage-threshold.mjs && npm audit --audit-level=high && npm run build",
|
|
58
59
|
"deploy:npm": "npm install && npm run build && npm publish --access public"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"@nestjs/jwt": ">=11",
|
|
64
65
|
"@nestjs/mongoose": ">=11",
|
|
65
66
|
"@nestjs/passport": ">=11",
|
|
67
|
+
"@nestjs/swagger": ">=11",
|
|
66
68
|
"bcryptjs": ">=2",
|
|
67
69
|
"class-transformer": ">=0.5",
|
|
68
70
|
"class-validator": ">=0.14",
|
|
@@ -85,6 +87,10 @@
|
|
|
85
87
|
"@nestjs/jwt": "^11.0.2",
|
|
86
88
|
"@nestjs/mongoose": "^11.0.4",
|
|
87
89
|
"@nestjs/passport": "^11.0.5",
|
|
90
|
+
"@nestjs/platform-express": "^11.0.1",
|
|
91
|
+
"@nestjs/swagger": "^11.4.5",
|
|
92
|
+
"@nestjs/testing": "^11.0.1",
|
|
93
|
+
"@types/supertest": "^6.0.3",
|
|
88
94
|
"@types/bcryptjs": "^2.4.6",
|
|
89
95
|
"@types/passport-jwt": "^4.0.1",
|
|
90
96
|
"@vitest/coverage-v8": "^4.1.7",
|
|
@@ -96,8 +102,13 @@
|
|
|
96
102
|
"passport-jwt": "^4.0.1",
|
|
97
103
|
"reflect-metadata": "^0.2.2",
|
|
98
104
|
"rxjs": "^7.8.2",
|
|
105
|
+
"supertest": "^7.1.1",
|
|
99
106
|
"tsup": "^8.1.0",
|
|
100
107
|
"typescript": "^5.4.5",
|
|
101
108
|
"vitest": "^4.1.5"
|
|
109
|
+
},
|
|
110
|
+
"overrides": {
|
|
111
|
+
"esbuild": "^0.28.1",
|
|
112
|
+
"multer": "^2.2.0"
|
|
102
113
|
}
|
|
103
114
|
}
|