@nathapp/nestjs-auth-2fa
v4.1.1
Published
nestjs-auth-2fa
Readme
nestjs-auth-2fa Library
using this library required to extends TwoFactorySettingProvider to retrieve User 2fa setting
TwoFactorGaurd will skip checking two factor when
- if user is not enable 2fa
- when not login
TwoFactorForceGaurd will force checking two factor regardless user enable 2fa or not
NOTED: both guard is not suitable to use on login, since unable to retreive user information on login request
Guard is not provided in module, required manually provide
TOTP replay protection
Since 4.0.3, the default OtpLibTwoFactorAuthProvider accepts each valid
(secret, time-step) slot only once. Enrollment confirmation consumes the slot
just like login or step-up verification, so reusing that code immediately is
rejected. Retention is (window * 2 + 2) * step seconds: 120 seconds for the
default window: 1 and 30-second step. This is replay-record retention, not an
extension of the code's validity or a blanket 120-second block on new codes.
Replay protection defaults to true. Applications that intentionally allow
same-step reuse can explicitly disable it:
TwoFactorAuthModule.register({
type: TwoFactorType.TOTP,
issuer: 'MyApp',
replayProtection: false,
});Disabling replay protection permits reuse of a valid code throughout its accepted window. Prefer passing proof of the completed MFA challenge through an enrollment/login flow when a second TOTP verification is unnecessary.
Store scope and repeated registration
The default InMemoryTotpReplayStore is provider-local. Direct injection and
the guard's provider map share the default provider within a registration,
but different registrations or manually constructed providers have separate
stores unless a common replayStore is supplied. The memory store holds at
most 10,000 live slots, sweeps expired entries at capacity, and rejects new
slots when full. It does not evict unexpired records to make room.
For repeated module registrations, export one application-owned store and
inject it into every options factory. useExisting aliases that same instance:
import { Module } from '@nestjs/common';
import {
InMemoryTotpReplayStore, TotpReplayStore, TwoFactorAuthModule, TwoFactorType,
} from '@nathapp/nestjs-auth-2fa';
@Module({
providers: [
InMemoryTotpReplayStore,
{ provide: TotpReplayStore, useExisting: InMemoryTotpReplayStore },
],
exports: [TotpReplayStore],
})
export class ReplayStoreModule {}
TwoFactorAuthModule.registerAsync({
imports: [ReplayStoreModule],
inject: [TotpReplayStore],
useFactory: (store: TotpReplayStore) => ({
type: TwoFactorType.TOTP,
issuer: 'MyApp',
replayStore: store,
}),
});Custom multi-provider registrations pass replayProtection / replayStore
through each TOTP registration's options object. Supply the same store there
as well if they must share consumption with the default provider.
Distributed storage
Across processes, implement TotpReplayStore.consume(key, ttlMs) using atomic
shared storage, such as Redis SET key value PX ttlMs NX. Return true only
when the key was newly consumed and false for an existing record. Namespace
keys consistently across applications that need to share protection. The
provider passes a SHA-256 secret hash plus the matched step, never the raw
secret. Store failures must throw/reject, not return success.
consume may return boolean or Promise<boolean>. The default memory store
keeps verify synchronous. When supplying an asynchronous store, direct
callers must await provider.verify(token, { secret }); guards already await
verification. Rate limiting remains the application's responsibility.
