@tstdl/base 0.93.181 → 0.93.183
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/api/server/api-request-token.provider.js +1 -1
- package/api/server/gateway.js +8 -3
- package/authentication/authentication.api.d.ts +13 -40
- package/authentication/authentication.api.js +5 -14
- package/authentication/client/authentication.service.d.ts +6 -14
- package/authentication/client/authentication.service.js +22 -4
- package/authentication/client/module.d.ts +1 -1
- package/authentication/client/module.js +4 -4
- package/authentication/models/index.d.ts +1 -0
- package/authentication/models/index.js +1 -0
- package/authentication/models/totp-results.model.d.ts +11 -0
- package/authentication/models/totp-results.model.js +37 -0
- package/authentication/server/authentication.api-controller.d.ts +3 -3
- package/authentication/server/authentication.api-controller.js +31 -4
- package/authentication/server/authentication.service.d.ts +5 -14
- package/authentication/server/authentication.service.js +6 -4
- package/core.d.ts +0 -5
- package/core.js +0 -8
- package/document-management/api/document-management.api.d.ts +2 -2
- package/document-management/service-models/document.service-model.d.ts +1 -1
- package/examples/config.d.ts +25 -0
- package/examples/config.js +26 -0
- package/notification/server/module.d.ts +1 -1
- package/notification/server/module.js +1 -1
- package/package.json +5 -5
- package/signals/api.d.ts +5 -1
- package/signals/api.js +3 -1
- package/signals/implementation/api.d.ts +13 -5
- package/signals/implementation/api.js +7 -1
- package/signals/implementation/asserts.d.ts +2 -2
- package/signals/implementation/asserts.js +3 -3
- package/signals/implementation/computed.d.ts +7 -34
- package/signals/implementation/computed.js +14 -83
- package/signals/implementation/configure.js +6 -2
- package/signals/implementation/effect.d.ts +65 -46
- package/signals/implementation/effect.js +97 -62
- package/signals/implementation/index.d.ts +2 -4
- package/signals/implementation/index.js +2 -4
- package/signals/implementation/linked_signal.d.ts +36 -0
- package/signals/implementation/linked_signal.js +34 -0
- package/signals/implementation/primitive/computed.d.ts +55 -0
- package/signals/implementation/primitive/computed.js +107 -0
- package/signals/implementation/primitive/effect.d.ts +26 -0
- package/signals/implementation/primitive/effect.js +31 -0
- package/signals/implementation/{equality.d.ts → primitive/equality.d.ts} +1 -1
- package/signals/implementation/{equality.js → primitive/equality.js} +1 -1
- package/signals/implementation/primitive/errors.d.ts +10 -0
- package/signals/implementation/{errors.js → primitive/errors.js} +3 -4
- package/signals/implementation/primitive/formatter.d.ts +19 -0
- package/signals/implementation/primitive/formatter.js +136 -0
- package/signals/implementation/{graph.d.ts → primitive/graph.d.ts} +68 -36
- package/signals/implementation/primitive/graph.js +386 -0
- package/signals/implementation/primitive/linked_signal.d.ts +46 -0
- package/signals/implementation/primitive/linked_signal.js +110 -0
- package/signals/implementation/primitive/signal.d.ts +31 -0
- package/signals/implementation/primitive/signal.js +80 -0
- package/signals/implementation/primitive/untracked.d.ts +12 -0
- package/signals/implementation/primitive/untracked.js +23 -0
- package/signals/implementation/{watch.d.ts → primitive/watch.d.ts} +1 -2
- package/signals/implementation/{watch.js → primitive/watch.js} +22 -16
- package/signals/implementation/resource/api.d.ts +275 -0
- package/signals/implementation/resource/api.js +26 -0
- package/signals/implementation/resource/debounce.d.ts +13 -0
- package/signals/implementation/resource/debounce.js +113 -0
- package/signals/implementation/resource/from_snapshots.d.ts +16 -0
- package/signals/implementation/resource/from_snapshots.js +44 -0
- package/signals/implementation/resource/index.d.ts +11 -0
- package/signals/implementation/resource/index.js +11 -0
- package/signals/implementation/resource/resource.d.ts +110 -0
- package/signals/implementation/resource/resource.js +402 -0
- package/signals/implementation/root_effect_scheduler.d.ts +50 -0
- package/signals/implementation/root_effect_scheduler.js +66 -0
- package/signals/implementation/signal.d.ts +42 -18
- package/signals/implementation/signal.js +29 -49
- package/signals/implementation/to-observable.d.ts +12 -5
- package/signals/implementation/to-observable.js +12 -2
- package/signals/implementation/to-signal.d.ts +9 -18
- package/signals/implementation/to-signal.js +46 -13
- package/signals/implementation/untracked.d.ts +1 -1
- package/signals/implementation/untracked.js +3 -11
- package/signals/operators/debounce.d.ts +8 -0
- package/signals/operators/debounce.js +19 -0
- package/signals/operators/derive-async.js +43 -15
- package/signals/operators/index.d.ts +2 -0
- package/signals/operators/index.js +2 -0
- package/signals/operators/throttle.d.ts +8 -0
- package/signals/operators/throttle.js +31 -0
- package/ai/genkit/tests/multi-region.test.d.ts +0 -2
- package/ai/genkit/tests/multi-region.test.js +0 -179
- package/ai/genkit/tests/token-limit-fallback.test.d.ts +0 -2
- package/ai/genkit/tests/token-limit-fallback.test.js +0 -209
- package/ai/prompts/tests/prompt-builder.test.d.ts +0 -1
- package/ai/prompts/tests/prompt-builder.test.js +0 -22
- package/ai/tests/instructions-formatter.test.d.ts +0 -1
- package/ai/tests/instructions-formatter.test.js +0 -116
- package/ai/tests/steering.test.d.ts +0 -1
- package/ai/tests/steering.test.js +0 -37
- package/api/client/tests/api-client.test.d.ts +0 -1
- package/api/client/tests/api-client.test.js +0 -194
- package/api/server/tests/csrf.middleware.test.d.ts +0 -1
- package/api/server/tests/csrf.middleware.test.js +0 -91
- package/authentication/tests/authentication-password-requirements.validator.test.d.ts +0 -1
- package/authentication/tests/authentication-password-requirements.validator.test.js +0 -29
- package/authentication/tests/authentication.api-controller.test.d.ts +0 -1
- package/authentication/tests/authentication.api-controller.test.js +0 -156
- package/authentication/tests/authentication.api-request-token.provider.test.d.ts +0 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +0 -48
- package/authentication/tests/authentication.client-error-handling.test.d.ts +0 -1
- package/authentication/tests/authentication.client-error-handling.test.js +0 -123
- package/authentication/tests/authentication.client-middleware.test.d.ts +0 -1
- package/authentication/tests/authentication.client-middleware.test.js +0 -118
- package/authentication/tests/authentication.client-service-methods.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-methods.test.js +0 -177
- package/authentication/tests/authentication.client-service-refresh.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +0 -153
- package/authentication/tests/authentication.client-service.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service.test.js +0 -76
- package/authentication/tests/authentication.refresh-busy-loop.test.d.ts +0 -1
- package/authentication/tests/authentication.refresh-busy-loop.test.js +0 -84
- package/authentication/tests/authentication.service.test.d.ts +0 -1
- package/authentication/tests/authentication.service.test.js +0 -167
- package/authentication/tests/authentication.test-ancillary-service.d.ts +0 -9
- package/authentication/tests/authentication.test-ancillary-service.js +0 -27
- package/authentication/tests/brute-force-protection.test.d.ts +0 -1
- package/authentication/tests/brute-force-protection.test.js +0 -211
- package/authentication/tests/helper.test.d.ts +0 -1
- package/authentication/tests/helper.test.js +0 -122
- package/authentication/tests/password-requirements.error.test.d.ts +0 -1
- package/authentication/tests/password-requirements.error.test.js +0 -14
- package/authentication/tests/remember.api.test.d.ts +0 -1
- package/authentication/tests/remember.api.test.js +0 -117
- package/authentication/tests/remember.service.test.d.ts +0 -1
- package/authentication/tests/remember.service.test.js +0 -83
- package/authentication/tests/subject.service.test.d.ts +0 -1
- package/authentication/tests/subject.service.test.js +0 -140
- package/authentication/tests/suspended-subject.test.d.ts +0 -1
- package/authentication/tests/suspended-subject.test.js +0 -120
- package/authentication/tests/totp.enrollment.test.d.ts +0 -1
- package/authentication/tests/totp.enrollment.test.js +0 -123
- package/authentication/tests/totp.login.test.d.ts +0 -1
- package/authentication/tests/totp.login.test.js +0 -213
- package/authentication/tests/totp.recovery-codes.test.d.ts +0 -1
- package/authentication/tests/totp.recovery-codes.test.js +0 -97
- package/authentication/tests/totp.status.test.d.ts +0 -1
- package/authentication/tests/totp.status.test.js +0 -72
- package/cancellation/tests/coverage.test.d.ts +0 -1
- package/cancellation/tests/coverage.test.js +0 -49
- package/cancellation/tests/leak.test.d.ts +0 -1
- package/cancellation/tests/leak.test.js +0 -35
- package/cancellation/tests/token.test.d.ts +0 -1
- package/cancellation/tests/token.test.js +0 -136
- package/circuit-breaker/tests/circuit-breaker.test.d.ts +0 -1
- package/circuit-breaker/tests/circuit-breaker.test.js +0 -116
- package/cryptography/tests/cryptography.test.d.ts +0 -1
- package/cryptography/tests/cryptography.test.js +0 -175
- package/cryptography/tests/jwt.test.d.ts +0 -1
- package/cryptography/tests/jwt.test.js +0 -54
- package/cryptography/tests/modern.test.d.ts +0 -1
- package/cryptography/tests/modern.test.js +0 -105
- package/cryptography/tests/module.test.d.ts +0 -1
- package/cryptography/tests/module.test.js +0 -100
- package/cryptography/tests/totp.test.d.ts +0 -1
- package/cryptography/tests/totp.test.js +0 -108
- package/document-management/tests/ai-config-hierarchy.test.d.ts +0 -1
- package/document-management/tests/ai-config-hierarchy.test.js +0 -59
- package/document-management/tests/ai-config-integration.test.d.ts +0 -1
- package/document-management/tests/ai-config-integration.test.js +0 -125
- package/document-management/tests/ai-config-merge.test.d.ts +0 -1
- package/document-management/tests/ai-config-merge.test.js +0 -46
- package/document-management/tests/document-management-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-management-ai-overrides.test.js +0 -63
- package/document-management/tests/document-management-core.test.d.ts +0 -1
- package/document-management/tests/document-management-core.test.js +0 -157
- package/document-management/tests/document-management.api.test.d.ts +0 -1
- package/document-management/tests/document-management.api.test.js +0 -101
- package/document-management/tests/document-statistics.service.test.d.ts +0 -1
- package/document-management/tests/document-statistics.service.test.js +0 -498
- package/document-management/tests/document-validation-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -87
- package/document-management/tests/document.service.test.d.ts +0 -1
- package/document-management/tests/document.service.test.js +0 -143
- package/document-management/tests/enum-helpers.test.d.ts +0 -1
- package/document-management/tests/enum-helpers.test.js +0 -452
- package/document-management/tests/helper.d.ts +0 -24
- package/document-management/tests/helper.js +0 -39
- package/errors/tests/format.test.d.ts +0 -1
- package/errors/tests/format.test.js +0 -84
- package/http/tests/server-timing.test.d.ts +0 -1
- package/http/tests/server-timing.test.js +0 -42
- package/injector/tests/advanced.test.d.ts +0 -1
- package/injector/tests/advanced.test.js +0 -116
- package/injector/tests/async-init.test.d.ts +0 -1
- package/injector/tests/async-init.test.js +0 -77
- package/injector/tests/basic.test.d.ts +0 -1
- package/injector/tests/basic.test.js +0 -114
- package/injector/tests/hierarchical.test.d.ts +0 -1
- package/injector/tests/hierarchical.test.js +0 -59
- package/injector/tests/leak.test.d.ts +0 -1
- package/injector/tests/leak.test.js +0 -45
- package/injector/tests/lifecycles.test.d.ts +0 -1
- package/injector/tests/lifecycles.test.js +0 -109
- package/logger/tests/pretty-print.test.d.ts +0 -1
- package/logger/tests/pretty-print.test.js +0 -60
- package/notification/tests/notification-api.test.d.ts +0 -1
- package/notification/tests/notification-api.test.js +0 -124
- package/notification/tests/notification-client.test.d.ts +0 -1
- package/notification/tests/notification-client.test.js +0 -101
- package/notification/tests/notification-flow.test.d.ts +0 -1
- package/notification/tests/notification-flow.test.js +0 -296
- package/notification/tests/notification-sse.service.test.d.ts +0 -1
- package/notification/tests/notification-sse.service.test.js +0 -43
- package/notification/tests/notification-type.service.test.d.ts +0 -1
- package/notification/tests/notification-type.service.test.js +0 -41
- package/object-storage/s3/tests/s3.object-storage.integration.test.d.ts +0 -1
- package/object-storage/s3/tests/s3.object-storage.integration.test.js +0 -303
- package/orm/tests/build-jsonb.test.d.ts +0 -1
- package/orm/tests/build-jsonb.test.js +0 -39
- package/orm/tests/data-types.test.d.ts +0 -1
- package/orm/tests/data-types.test.js +0 -39
- package/orm/tests/database-extension.test.d.ts +0 -1
- package/orm/tests/database-extension.test.js +0 -63
- package/orm/tests/database-migration.test.d.ts +0 -1
- package/orm/tests/database-migration.test.js +0 -83
- package/orm/tests/decorators.test.d.ts +0 -1
- package/orm/tests/decorators.test.js +0 -77
- package/orm/tests/encryption.test.d.ts +0 -1
- package/orm/tests/encryption.test.js +0 -31
- package/orm/tests/query-complex.test.d.ts +0 -1
- package/orm/tests/query-complex.test.js +0 -172
- package/orm/tests/query-converter-complex.test.d.ts +0 -1
- package/orm/tests/query-converter-complex.test.js +0 -131
- package/orm/tests/query-converter.test.d.ts +0 -1
- package/orm/tests/query-converter.test.js +0 -123
- package/orm/tests/repository-advanced.test.d.ts +0 -1
- package/orm/tests/repository-advanced.test.js +0 -189
- package/orm/tests/repository-attributes.test.d.ts +0 -1
- package/orm/tests/repository-attributes.test.js +0 -83
- package/orm/tests/repository-compound-primary-key.test.d.ts +0 -2
- package/orm/tests/repository-compound-primary-key.test.js +0 -226
- package/orm/tests/repository-comprehensive.test.d.ts +0 -1
- package/orm/tests/repository-comprehensive.test.js +0 -162
- package/orm/tests/repository-coverage.test.d.ts +0 -2
- package/orm/tests/repository-coverage.test.js +0 -242
- package/orm/tests/repository-cti-complex.test.d.ts +0 -1
- package/orm/tests/repository-cti-complex.test.js +0 -151
- package/orm/tests/repository-cti-embedded.test.d.ts +0 -1
- package/orm/tests/repository-cti-embedded.test.js +0 -178
- package/orm/tests/repository-cti-extensive.test.d.ts +0 -2
- package/orm/tests/repository-cti-extensive.test.js +0 -279
- package/orm/tests/repository-cti-mapping.test.d.ts +0 -2
- package/orm/tests/repository-cti-mapping.test.js +0 -108
- package/orm/tests/repository-cti-search.test.d.ts +0 -1
- package/orm/tests/repository-cti-search.test.js +0 -141
- package/orm/tests/repository-cti-soft-delete.test.d.ts +0 -2
- package/orm/tests/repository-cti-soft-delete.test.js +0 -103
- package/orm/tests/repository-cti-transactions.test.d.ts +0 -1
- package/orm/tests/repository-cti-transactions.test.js +0 -112
- package/orm/tests/repository-cti-upsert-many.test.d.ts +0 -2
- package/orm/tests/repository-cti-upsert-many.test.js +0 -115
- package/orm/tests/repository-cti.test.d.ts +0 -2
- package/orm/tests/repository-cti.test.js +0 -390
- package/orm/tests/repository-edge-cases.test.d.ts +0 -1
- package/orm/tests/repository-edge-cases.test.js +0 -178
- package/orm/tests/repository-expiration.test.d.ts +0 -2
- package/orm/tests/repository-expiration.test.js +0 -140
- package/orm/tests/repository-extra-coverage.test.d.ts +0 -2
- package/orm/tests/repository-extra-coverage.test.js +0 -402
- package/orm/tests/repository-mapping.test.d.ts +0 -2
- package/orm/tests/repository-mapping.test.js +0 -65
- package/orm/tests/repository-regression.test.d.ts +0 -1
- package/orm/tests/repository-regression.test.js +0 -288
- package/orm/tests/repository-search-coverage.test.d.ts +0 -1
- package/orm/tests/repository-search-coverage.test.js +0 -107
- package/orm/tests/repository-search.test.d.ts +0 -1
- package/orm/tests/repository-search.test.js +0 -105
- package/orm/tests/repository-soft-delete.test.d.ts +0 -1
- package/orm/tests/repository-soft-delete.test.js +0 -118
- package/orm/tests/repository-transactions-nested.test.d.ts +0 -1
- package/orm/tests/repository-transactions-nested.test.js +0 -178
- package/orm/tests/repository-types.test.d.ts +0 -1
- package/orm/tests/repository-types.test.js +0 -184
- package/orm/tests/repository-undelete.test.d.ts +0 -2
- package/orm/tests/repository-undelete.test.js +0 -201
- package/orm/tests/schema-converter.test.d.ts +0 -1
- package/orm/tests/schema-converter.test.js +0 -82
- package/orm/tests/schema-generation.test.d.ts +0 -2
- package/orm/tests/schema-generation.test.js +0 -174
- package/orm/tests/sql-helpers.test.d.ts +0 -1
- package/orm/tests/sql-helpers.test.js +0 -67
- package/orm/tests/transaction-safety.test.d.ts +0 -1
- package/orm/tests/transaction-safety.test.js +0 -81
- package/orm/tests/transactional.test.d.ts +0 -1
- package/orm/tests/transactional.test.js +0 -215
- package/orm/tests/utils.test.d.ts +0 -1
- package/orm/tests/utils.test.js +0 -70
- package/pdf/tests/utils.test.d.ts +0 -1
- package/pdf/tests/utils.test.js +0 -187
- package/process/tests/spawn.test.d.ts +0 -1
- package/process/tests/spawn.test.js +0 -182
- package/rate-limit/tests/postgres-rate-limiter.test.d.ts +0 -1
- package/rate-limit/tests/postgres-rate-limiter.test.js +0 -84
- package/renderer/tests/renderer.test.d.ts +0 -1
- package/renderer/tests/renderer.test.js +0 -88
- package/rpc/tests/rpc.integration.test.d.ts +0 -1
- package/rpc/tests/rpc.integration.test.js +0 -615
- package/signals/implementation/errors.d.ts +0 -2
- package/signals/implementation/graph.js +0 -312
- package/signals/implementation/writable-signal.d.ts +0 -48
- package/signals/implementation/writable-signal.js +0 -32
- package/task-queue/tests/coverage-branch.test.d.ts +0 -1
- package/task-queue/tests/coverage-branch.test.js +0 -395
- package/task-queue/tests/coverage-enhancement.test.d.ts +0 -1
- package/task-queue/tests/coverage-enhancement.test.js +0 -150
- package/task-queue/tests/dag.test.d.ts +0 -1
- package/task-queue/tests/dag.test.js +0 -188
- package/task-queue/tests/dependencies.test.d.ts +0 -1
- package/task-queue/tests/dependencies.test.js +0 -296
- package/task-queue/tests/enqueue-batch.test.d.ts +0 -1
- package/task-queue/tests/enqueue-batch.test.js +0 -125
- package/task-queue/tests/enqueue-item.test.d.ts +0 -1
- package/task-queue/tests/enqueue-item.test.js +0 -12
- package/task-queue/tests/fan-out-spawning.test.d.ts +0 -1
- package/task-queue/tests/fan-out-spawning.test.js +0 -94
- package/task-queue/tests/idempotent-replacement.test.d.ts +0 -1
- package/task-queue/tests/idempotent-replacement.test.js +0 -114
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +0 -1
- package/task-queue/tests/missing-idempotent-tasks.test.js +0 -39
- package/task-queue/tests/optimization-edge-cases.test.d.ts +0 -1
- package/task-queue/tests/optimization-edge-cases.test.js +0 -124
- package/task-queue/tests/queue-generic.test.d.ts +0 -1
- package/task-queue/tests/queue-generic.test.js +0 -8
- package/task-queue/tests/queue.test.d.ts +0 -1
- package/task-queue/tests/queue.test.js +0 -756
- package/task-queue/tests/shutdown.test.d.ts +0 -1
- package/task-queue/tests/shutdown.test.js +0 -41
- package/task-queue/tests/task-context.test.d.ts +0 -1
- package/task-queue/tests/task-context.test.js +0 -7
- package/task-queue/tests/task-union.test.d.ts +0 -1
- package/task-queue/tests/task-union.test.js +0 -18
- package/task-queue/tests/transactions.test.d.ts +0 -1
- package/task-queue/tests/transactions.test.js +0 -47
- package/task-queue/tests/typing.test.d.ts +0 -1
- package/task-queue/tests/typing.test.js +0 -9
- package/task-queue/tests/worker.test.d.ts +0 -1
- package/task-queue/tests/worker.test.js +0 -258
- package/task-queue/tests/zombie-parent.test.d.ts +0 -1
- package/task-queue/tests/zombie-parent.test.js +0 -45
- package/task-queue/tests/zombie-recovery.test.d.ts +0 -1
- package/task-queue/tests/zombie-recovery.test.js +0 -51
- package/utils/tests/backoff.test.d.ts +0 -1
- package/utils/tests/backoff.test.js +0 -41
- package/utils/tests/retry-with-backoff.test.d.ts +0 -1
- package/utils/tests/retry-with-backoff.test.js +0 -49
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
-
import { ActorType, Auditor } from '../../audit/index.js';
|
|
3
|
-
import { importKey } from '../../cryptography/index.js';
|
|
4
|
-
import { generateTotpToken } from '../../cryptography/totp.js';
|
|
5
|
-
import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
6
|
-
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
7
|
-
import { AuthenticationService } from '../server/authentication.service.js';
|
|
8
|
-
import { SubjectService } from '../server/subject.service.js';
|
|
9
|
-
import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
|
|
10
|
-
describe('TOTP Login', () => {
|
|
11
|
-
let injector;
|
|
12
|
-
let database;
|
|
13
|
-
let authenticationService;
|
|
14
|
-
let subjectService;
|
|
15
|
-
let auditor;
|
|
16
|
-
const schema = 'authentication';
|
|
17
|
-
const tenantId = crypto.randomUUID();
|
|
18
|
-
const password = crypto.randomUUID();
|
|
19
|
-
beforeAll(async () => {
|
|
20
|
-
({ injector, database } = await setupIntegrationTest({
|
|
21
|
-
modules: { authentication: true },
|
|
22
|
-
authentication: {
|
|
23
|
-
ancillaryService: DefaultAuthenticationAncillaryService,
|
|
24
|
-
},
|
|
25
|
-
}));
|
|
26
|
-
authenticationService = await injector.resolveAsync(AuthenticationService);
|
|
27
|
-
subjectService = await injector.resolveAsync(SubjectService);
|
|
28
|
-
auditor = injector.resolve(Auditor);
|
|
29
|
-
});
|
|
30
|
-
afterAll(async () => {
|
|
31
|
-
await injector?.dispose();
|
|
32
|
-
});
|
|
33
|
-
beforeEach(async () => {
|
|
34
|
-
await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
|
|
35
|
-
});
|
|
36
|
-
test('loginVerifyTotp should fail if the same token is used twice (replay attack)', async () => {
|
|
37
|
-
const user = await subjectService.createUser({
|
|
38
|
-
tenantId,
|
|
39
|
-
email: 'replay-totp@example.com',
|
|
40
|
-
firstName: 'Replay',
|
|
41
|
-
lastName: 'Totp',
|
|
42
|
-
});
|
|
43
|
-
await authenticationService.setPassword(user, password);
|
|
44
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
45
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
46
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
47
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
48
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
49
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
50
|
-
const loginResult1 = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
51
|
-
const loginResult2 = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
52
|
-
const challengeToken1 = loginResult1.challengeToken;
|
|
53
|
-
const challengeToken2 = loginResult2.challengeToken;
|
|
54
|
-
const totpToken = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
55
|
-
// First use: success
|
|
56
|
-
const verifyResult1 = await authenticationService.loginVerifyTotp(challengeToken1, totpToken, auditor);
|
|
57
|
-
expect(verifyResult1.type).toBe('success');
|
|
58
|
-
// Second use (replay): fail
|
|
59
|
-
await expect(authenticationService.loginVerifyTotp(challengeToken2, totpToken, auditor)).rejects.toThrow();
|
|
60
|
-
});
|
|
61
|
-
test('login should return success if TOTP is not enabled', async () => {
|
|
62
|
-
const user = await subjectService.createUser({
|
|
63
|
-
tenantId,
|
|
64
|
-
email: 'no-totp@example.com',
|
|
65
|
-
firstName: 'No',
|
|
66
|
-
lastName: 'Totp',
|
|
67
|
-
});
|
|
68
|
-
await authenticationService.setPassword(user, password);
|
|
69
|
-
const result = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
70
|
-
expect(result.type).toBe('success');
|
|
71
|
-
});
|
|
72
|
-
test('login should return totp challenge if TOTP is enabled', async () => {
|
|
73
|
-
const user = await subjectService.createUser({
|
|
74
|
-
tenantId,
|
|
75
|
-
email: 'with-totp@example.com',
|
|
76
|
-
firstName: 'With',
|
|
77
|
-
lastName: 'Totp',
|
|
78
|
-
});
|
|
79
|
-
await authenticationService.setPassword(user, password);
|
|
80
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
81
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
82
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
83
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
84
|
-
const token = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
85
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
|
|
86
|
-
const result = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
87
|
-
expect(result.type).toBe('totp');
|
|
88
|
-
if (result.type !== 'totp') {
|
|
89
|
-
throw new Error('Expected TOTP result');
|
|
90
|
-
}
|
|
91
|
-
expect(result.challengeToken).toBeDefined();
|
|
92
|
-
});
|
|
93
|
-
test('loginVerifyTotp should return success with valid token', async () => {
|
|
94
|
-
const user = await subjectService.createUser({
|
|
95
|
-
tenantId,
|
|
96
|
-
email: 'verify-totp@example.com',
|
|
97
|
-
firstName: 'Verify',
|
|
98
|
-
lastName: 'Totp',
|
|
99
|
-
});
|
|
100
|
-
await authenticationService.setPassword(user, password);
|
|
101
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
102
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
103
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
104
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
105
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
106
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
107
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor, true);
|
|
108
|
-
expect(loginResult.type).toBe('totp');
|
|
109
|
-
if (loginResult.type !== 'totp') {
|
|
110
|
-
throw new Error('Expected TOTP result');
|
|
111
|
-
}
|
|
112
|
-
const challengeToken = loginResult.challengeToken;
|
|
113
|
-
const totpToken = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
114
|
-
const verifyResult = await authenticationService.loginVerifyTotp(challengeToken, totpToken, auditor);
|
|
115
|
-
expect(verifyResult.type).toBe('success');
|
|
116
|
-
expect(verifyResult.result.remember).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
test('loginVerifyTotp should fail with recovery code (decoupled)', async () => {
|
|
119
|
-
const user = await subjectService.createUser({
|
|
120
|
-
tenantId,
|
|
121
|
-
email: 'verify-recovery-fail@example.com',
|
|
122
|
-
firstName: 'Verify',
|
|
123
|
-
lastName: 'RecoveryFail',
|
|
124
|
-
});
|
|
125
|
-
await authenticationService.setPassword(user, password);
|
|
126
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
127
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
128
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
129
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
130
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
131
|
-
const { recoveryCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
132
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
133
|
-
expect(loginResult.type).toBe('totp');
|
|
134
|
-
if (loginResult.type !== 'totp') {
|
|
135
|
-
throw new Error('Expected TOTP result');
|
|
136
|
-
}
|
|
137
|
-
const challengeToken = loginResult.challengeToken;
|
|
138
|
-
await expect(authenticationService.loginVerifyTotp(challengeToken, recoveryCodes[0], auditor)).rejects.toThrow();
|
|
139
|
-
});
|
|
140
|
-
test('loginRecovery should return success with valid recovery code', async () => {
|
|
141
|
-
const user = await subjectService.createUser({
|
|
142
|
-
tenantId,
|
|
143
|
-
email: 'verify-recovery-success@example.com',
|
|
144
|
-
firstName: 'Verify',
|
|
145
|
-
lastName: 'RecoverySuccess',
|
|
146
|
-
});
|
|
147
|
-
await authenticationService.setPassword(user, password);
|
|
148
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
149
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
150
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
151
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
152
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
153
|
-
const { recoveryCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
154
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
155
|
-
expect(loginResult.type).toBe('totp');
|
|
156
|
-
if (loginResult.type !== 'totp') {
|
|
157
|
-
throw new Error('Expected TOTP result');
|
|
158
|
-
}
|
|
159
|
-
const challengeToken = loginResult.challengeToken;
|
|
160
|
-
const verifyResult = await authenticationService.loginRecovery(challengeToken, recoveryCodes[0], auditor);
|
|
161
|
-
expect(verifyResult.type).toBe('success');
|
|
162
|
-
expect(verifyResult.lowRecoveryCodesWarning).toBe(false);
|
|
163
|
-
});
|
|
164
|
-
test('loginRecovery should return success and lowRecoveryCodesWarning when few codes remain', async () => {
|
|
165
|
-
const user = await subjectService.createUser({
|
|
166
|
-
tenantId,
|
|
167
|
-
email: 'verify-recovery-warning@example.com',
|
|
168
|
-
firstName: 'Verify',
|
|
169
|
-
lastName: 'RecoveryWarning',
|
|
170
|
-
});
|
|
171
|
-
await authenticationService.setPassword(user, password);
|
|
172
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
173
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
174
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
175
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
176
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
177
|
-
const { recoveryCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
178
|
-
// Use 7 codes to leave 3 unused (assuming 10 total)
|
|
179
|
-
for (let i = 0; i < 7; i++) {
|
|
180
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
181
|
-
const challengeToken = loginResult.challengeToken;
|
|
182
|
-
await authenticationService.loginRecovery(challengeToken, recoveryCodes[i], auditor);
|
|
183
|
-
}
|
|
184
|
-
// Next use should trigger warning
|
|
185
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
186
|
-
const challengeToken = loginResult.challengeToken;
|
|
187
|
-
const verifyResult = await authenticationService.loginRecovery(challengeToken, recoveryCodes[7], auditor);
|
|
188
|
-
expect(verifyResult.type).toBe('success');
|
|
189
|
-
expect(verifyResult.lowRecoveryCodesWarning).toBe(true);
|
|
190
|
-
});
|
|
191
|
-
test('loginVerifyTotp should fail with invalid token', async () => {
|
|
192
|
-
const user = await subjectService.createUser({
|
|
193
|
-
tenantId,
|
|
194
|
-
email: 'verify-fail@example.com',
|
|
195
|
-
firstName: 'Verify',
|
|
196
|
-
lastName: 'Fail',
|
|
197
|
-
});
|
|
198
|
-
await authenticationService.setPassword(user, password);
|
|
199
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
200
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
201
|
-
const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
|
|
202
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
203
|
-
const enrollmentToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
|
|
204
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, enrollmentToken, userAuditor);
|
|
205
|
-
const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
|
|
206
|
-
expect(loginResult.type).toBe('totp');
|
|
207
|
-
if (loginResult.type !== 'totp') {
|
|
208
|
-
throw new Error('Expected TOTP result');
|
|
209
|
-
}
|
|
210
|
-
const challengeToken = loginResult.challengeToken;
|
|
211
|
-
await expect(authenticationService.loginVerifyTotp(challengeToken, '000000', auditor)).rejects.toThrow();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
-
import { ActorType, Auditor } from '../../audit/index.js';
|
|
3
|
-
import { importKey } from '../../cryptography/index.js';
|
|
4
|
-
import { generateTotpToken } from '../../cryptography/totp.js';
|
|
5
|
-
import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
6
|
-
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
7
|
-
import { AuthenticationService } from '../server/authentication.service.js';
|
|
8
|
-
import { SubjectService } from '../server/subject.service.js';
|
|
9
|
-
import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
|
|
10
|
-
describe('TOTP Recovery Codes Regeneration', () => {
|
|
11
|
-
let injector;
|
|
12
|
-
let database;
|
|
13
|
-
let authenticationService;
|
|
14
|
-
let subjectService;
|
|
15
|
-
let auditor;
|
|
16
|
-
const schema = 'authentication';
|
|
17
|
-
const tenantId = crypto.randomUUID();
|
|
18
|
-
beforeAll(async () => {
|
|
19
|
-
({ injector, database } = await setupIntegrationTest({
|
|
20
|
-
modules: { authentication: true },
|
|
21
|
-
authentication: {
|
|
22
|
-
ancillaryService: DefaultAuthenticationAncillaryService,
|
|
23
|
-
},
|
|
24
|
-
}));
|
|
25
|
-
authenticationService = await injector.resolveAsync(AuthenticationService);
|
|
26
|
-
subjectService = await injector.resolveAsync(SubjectService);
|
|
27
|
-
auditor = injector.resolve(Auditor);
|
|
28
|
-
});
|
|
29
|
-
afterAll(async () => {
|
|
30
|
-
await injector?.dispose();
|
|
31
|
-
});
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
|
|
34
|
-
});
|
|
35
|
-
test('regenerateRecoveryCodes should replace existing codes after valid TOTP verification', async () => {
|
|
36
|
-
const user = await subjectService.createUser({
|
|
37
|
-
tenantId,
|
|
38
|
-
email: 'regen-totp@example.com',
|
|
39
|
-
firstName: 'Regen',
|
|
40
|
-
lastName: 'Totp',
|
|
41
|
-
});
|
|
42
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
43
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
44
|
-
const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
|
|
45
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
46
|
-
const token = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
47
|
-
const { recoveryCodes: originalCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
|
|
48
|
-
// Verify original codes work (using disableTotp for now as it uses recovery codes currently)
|
|
49
|
-
// Actually we just want to ensure regeneration works.
|
|
50
|
-
const regenToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() + 30 });
|
|
51
|
-
const { recoveryCodes: newCodes } = await authenticationService.regenerateRecoveryCodes(tenantId, user.id, regenToken, userAuditor);
|
|
52
|
-
expect(newCodes).toHaveLength(10);
|
|
53
|
-
expect(newCodes).not.toEqual(originalCodes);
|
|
54
|
-
// Verify old code fails (should fail standard TOTP verification fallback later, but for now let's just check they are replaced)
|
|
55
|
-
// We can't easily check database content directly here without more boilerplate, but the logic should handle it.
|
|
56
|
-
});
|
|
57
|
-
test('regenerateRecoveryCodes should fail with invalid TOTP token', async () => {
|
|
58
|
-
const user = await subjectService.createUser({
|
|
59
|
-
tenantId,
|
|
60
|
-
email: 'regen-fail@example.com',
|
|
61
|
-
firstName: 'Regen',
|
|
62
|
-
lastName: 'Fail',
|
|
63
|
-
});
|
|
64
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
65
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
66
|
-
const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
|
|
67
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
68
|
-
const token = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
69
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
|
|
70
|
-
await expect(authenticationService.regenerateRecoveryCodes(tenantId, user.id, '000000', userAuditor)).rejects.toThrow();
|
|
71
|
-
});
|
|
72
|
-
test('regenerateRecoveryCodes should optionally invalidate other sessions', async () => {
|
|
73
|
-
const user = await subjectService.createUser({
|
|
74
|
-
tenantId,
|
|
75
|
-
email: 'regen-invalidate@example.com',
|
|
76
|
-
firstName: 'Regen',
|
|
77
|
-
lastName: 'Invalidate',
|
|
78
|
-
});
|
|
79
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
80
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
81
|
-
const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
|
|
82
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
83
|
-
const token = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
84
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
|
|
85
|
-
// Create some sessions
|
|
86
|
-
const session1 = await authenticationService.loginAlreadyValidatedSubject(user, undefined, userAuditor, false);
|
|
87
|
-
const session2 = await authenticationService.loginAlreadyValidatedSubject(user, undefined, userAuditor, false);
|
|
88
|
-
const regenToken = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() + 30 });
|
|
89
|
-
// We need to pass the current session id to NOT invalidate it if we were implementing it for "invalidateOtherSessions"
|
|
90
|
-
// But the spec says "invalidateOtherSessions", usually it means all except current.
|
|
91
|
-
// In this test context, we don't have a "current" session in the service call yet, unless we fork the auditor or similar.
|
|
92
|
-
// Let's see how AuthenticationService handles session invalidation.
|
|
93
|
-
await authenticationService.regenerateRecoveryCodes(tenantId, user.id, regenToken, userAuditor, { invalidateOtherSessions: true });
|
|
94
|
-
// Check sessions (should be invalidated)
|
|
95
|
-
// This part might need adjustment based on how invalidateAllOtherSessions is implemented in the service.
|
|
96
|
-
});
|
|
97
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
-
import { ActorType, Auditor } from '../../audit/index.js';
|
|
3
|
-
import { importKey } from '../../cryptography/index.js';
|
|
4
|
-
import { generateTotpToken } from '../../cryptography/totp.js';
|
|
5
|
-
import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
6
|
-
import { AuthenticationService } from '../server/authentication.service.js';
|
|
7
|
-
import { SubjectService } from '../server/subject.service.js';
|
|
8
|
-
import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
|
|
9
|
-
describe('TOTP Status', () => {
|
|
10
|
-
let injector;
|
|
11
|
-
let database;
|
|
12
|
-
let authenticationService;
|
|
13
|
-
let subjectService;
|
|
14
|
-
let auditor;
|
|
15
|
-
const schema = 'authentication';
|
|
16
|
-
const tenantId = crypto.randomUUID();
|
|
17
|
-
beforeAll(async () => {
|
|
18
|
-
({ injector, database } = await setupIntegrationTest({
|
|
19
|
-
modules: { authentication: true },
|
|
20
|
-
authentication: {
|
|
21
|
-
ancillaryService: DefaultAuthenticationAncillaryService,
|
|
22
|
-
},
|
|
23
|
-
}));
|
|
24
|
-
authenticationService = await injector.resolveAsync(AuthenticationService);
|
|
25
|
-
subjectService = await injector.resolveAsync(SubjectService);
|
|
26
|
-
auditor = injector.resolve(Auditor);
|
|
27
|
-
});
|
|
28
|
-
afterAll(async () => {
|
|
29
|
-
await injector?.dispose();
|
|
30
|
-
});
|
|
31
|
-
beforeEach(async () => {
|
|
32
|
-
await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
|
|
33
|
-
});
|
|
34
|
-
test('getTotpStatus should return false when TOTP is not enrolled', async () => {
|
|
35
|
-
const user = await subjectService.createUser({
|
|
36
|
-
tenantId,
|
|
37
|
-
email: 'no-totp@example.com',
|
|
38
|
-
firstName: 'No',
|
|
39
|
-
lastName: 'Totp',
|
|
40
|
-
});
|
|
41
|
-
const status = await authenticationService.getTotpStatus(tenantId, user.id);
|
|
42
|
-
expect(status.active).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
test('getTotpStatus should return false when TOTP enrollment is pending', async () => {
|
|
45
|
-
const user = await subjectService.createUser({
|
|
46
|
-
tenantId,
|
|
47
|
-
email: 'pending-totp@example.com',
|
|
48
|
-
firstName: 'Pending',
|
|
49
|
-
lastName: 'Totp',
|
|
50
|
-
});
|
|
51
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
52
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
53
|
-
const status = await authenticationService.getTotpStatus(tenantId, user.id);
|
|
54
|
-
expect(status.active).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
test('getTotpStatus should return true when TOTP is active', async () => {
|
|
57
|
-
const user = await subjectService.createUser({
|
|
58
|
-
tenantId,
|
|
59
|
-
email: 'active-totp@example.com',
|
|
60
|
-
firstName: 'Active',
|
|
61
|
-
lastName: 'Totp',
|
|
62
|
-
});
|
|
63
|
-
const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
|
|
64
|
-
await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
|
|
65
|
-
const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
|
|
66
|
-
const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
|
|
67
|
-
const token = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
|
|
68
|
-
await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
|
|
69
|
-
const status = await authenticationService.getTotpStatus(tenantId, user.id);
|
|
70
|
-
expect(status.active).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '../../polyfills.js';
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import '../../polyfills.js';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import * as core from '../../core.js';
|
|
4
|
-
import { CancellationToken, sourcesToAbortSignal } from '../token.js';
|
|
5
|
-
describe('Cancellation Coverage Extra', () => {
|
|
6
|
-
it('should hit dev mode branch (true)', () => {
|
|
7
|
-
vi.spyOn(core, 'isDevMode').mockReturnValue(true);
|
|
8
|
-
const token = new CancellationToken();
|
|
9
|
-
expect(token.isSet).toBe(false);
|
|
10
|
-
});
|
|
11
|
-
it('should hit dev mode branch (false)', () => {
|
|
12
|
-
vi.spyOn(core, 'isDevMode').mockReturnValue(false);
|
|
13
|
-
const token = new CancellationToken();
|
|
14
|
-
expect(token.isSet).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
it('should hit throw: true branch in wait', async () => {
|
|
17
|
-
const token = new CancellationToken();
|
|
18
|
-
token.set('reason');
|
|
19
|
-
await expect(token.wait({ throw: true })).rejects.toThrow();
|
|
20
|
-
});
|
|
21
|
-
it('should hit waitThrow', async () => {
|
|
22
|
-
const token = new CancellationToken();
|
|
23
|
-
token.set('reason');
|
|
24
|
-
await expect(token.waitThrow()).rejects.toThrow();
|
|
25
|
-
});
|
|
26
|
-
it('should hit signals.length == 1 branch', () => {
|
|
27
|
-
const controller = new AbortController();
|
|
28
|
-
const signal = sourcesToAbortSignal(controller.signal);
|
|
29
|
-
expect(signal).toBe(controller.signal);
|
|
30
|
-
});
|
|
31
|
-
it('should hit signals.length > 1 branch', () => {
|
|
32
|
-
const controller1 = new AbortController();
|
|
33
|
-
const controller2 = new AbortController();
|
|
34
|
-
const signal = sourcesToAbortSignal([controller1.signal, controller2.signal]);
|
|
35
|
-
expect(signal).not.toBe(controller1.signal);
|
|
36
|
-
expect(signal).not.toBe(controller2.signal);
|
|
37
|
-
});
|
|
38
|
-
it('should hit sourcesToAbortSignal error branch', () => {
|
|
39
|
-
expect(() => sourcesToAbortSignal([])).toThrow('At least one cancellation source must be provided');
|
|
40
|
-
});
|
|
41
|
-
it('should hit unsubscribe branch in subscribe', () => {
|
|
42
|
-
const token = new CancellationToken();
|
|
43
|
-
const next = vi.fn();
|
|
44
|
-
const subscription = token.subscribe({ next });
|
|
45
|
-
subscription.unsubscribe();
|
|
46
|
-
token.set();
|
|
47
|
-
expect(next).not.toHaveBeenCalled();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { CancellationSignal, CancellationToken } from '../token.js';
|
|
3
|
-
describe('CancellationToken Memory Leak', () => {
|
|
4
|
-
it('should not leak when using inherit and disposing', () => {
|
|
5
|
-
const parent = new CancellationToken();
|
|
6
|
-
for (let i = 0; i < 1000; i++) {
|
|
7
|
-
const child = parent.inherit(new CancellationToken());
|
|
8
|
-
child.dispose();
|
|
9
|
-
}
|
|
10
|
-
expect(parent.isSet).toBe(false);
|
|
11
|
-
});
|
|
12
|
-
it('should follow parent signal', () => {
|
|
13
|
-
const parent = new CancellationToken();
|
|
14
|
-
const child = new CancellationToken(parent);
|
|
15
|
-
expect(child.isSet).toBe(false);
|
|
16
|
-
parent.set();
|
|
17
|
-
expect(child.isSet).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
it('should not affect parent when child is set', () => {
|
|
20
|
-
const parent = new CancellationToken();
|
|
21
|
-
const child = new CancellationToken(parent);
|
|
22
|
-
expect(parent.isSet).toBe(false);
|
|
23
|
-
child.set();
|
|
24
|
-
expect(parent.isSet).toBe(false);
|
|
25
|
-
expect(child.isSet).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it('should clean up when disposed', () => {
|
|
28
|
-
const parent = new CancellationToken();
|
|
29
|
-
const signal = CancellationSignal.from(parent);
|
|
30
|
-
expect(signal.isSet).toBe(false);
|
|
31
|
-
signal.dispose();
|
|
32
|
-
expect(signal.isSet).toBe(true);
|
|
33
|
-
expect(parent.isSet).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '../../polyfills.js';
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import '../../polyfills.js';
|
|
2
|
-
import { firstValueFrom, from } from 'rxjs';
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { CancellationSignal, CancellationToken } from '../token.js';
|
|
5
|
-
describe('CancellationToken', () => {
|
|
6
|
-
it('should be unset by default', () => {
|
|
7
|
-
const token = new CancellationToken();
|
|
8
|
-
expect(token.isSet).toBe(false);
|
|
9
|
-
expect(token.isUnset).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
it('should be set when set() is called', () => {
|
|
12
|
-
const token = new CancellationToken();
|
|
13
|
-
token.set('reason');
|
|
14
|
-
expect(token.isSet).toBe(true);
|
|
15
|
-
expect(token.isUnset).toBe(false);
|
|
16
|
-
expect(token.reason).toBeInstanceOf(Error);
|
|
17
|
-
expect(token.reason.message).toBe('Operation cancelled');
|
|
18
|
-
expect(token.reason.cause).toBe('reason');
|
|
19
|
-
});
|
|
20
|
-
it('should fork a new token', () => {
|
|
21
|
-
const parent = new CancellationToken();
|
|
22
|
-
const child = parent.fork();
|
|
23
|
-
expect(child).toBeInstanceOf(CancellationToken);
|
|
24
|
-
expect(child.isSet).toBe(false);
|
|
25
|
-
parent.set();
|
|
26
|
-
expect(child.isSet).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it('should support multiple parents', () => {
|
|
29
|
-
const parent1 = new CancellationToken();
|
|
30
|
-
const parent2 = new CancellationToken();
|
|
31
|
-
const child = new CancellationToken([parent1, parent2]);
|
|
32
|
-
expect(child.isSet).toBe(false);
|
|
33
|
-
parent2.set();
|
|
34
|
-
expect(child.isSet).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
it('should support single parent', () => {
|
|
37
|
-
const parent = new CancellationToken();
|
|
38
|
-
const child = new CancellationToken(parent);
|
|
39
|
-
expect(child.isSet).toBe(false);
|
|
40
|
-
parent.set();
|
|
41
|
-
expect(child.isSet).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
it('should support AbortController as a source', () => {
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
const signal = CancellationSignal.from(controller);
|
|
46
|
-
expect(signal.isSet).toBe(false);
|
|
47
|
-
controller.abort();
|
|
48
|
-
expect(signal.isSet).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
it('should support AbortSignal as a source', () => {
|
|
51
|
-
const controller = new AbortController();
|
|
52
|
-
const signal = CancellationSignal.from(controller.signal);
|
|
53
|
-
expect(signal.isSet).toBe(false);
|
|
54
|
-
controller.abort();
|
|
55
|
-
expect(signal.isSet).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it('should wait for cancellation (resolve)', async () => {
|
|
58
|
-
const token = new CancellationToken();
|
|
59
|
-
setTimeout(() => token.set(), 10);
|
|
60
|
-
await token.wait();
|
|
61
|
-
expect(token.isSet).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
it('should wait and throw (waitThrow)', async () => {
|
|
64
|
-
const token = new CancellationToken();
|
|
65
|
-
setTimeout(() => token.set('foo'), 10);
|
|
66
|
-
await expect(token.waitThrow()).rejects.toThrow('Operation cancelled');
|
|
67
|
-
});
|
|
68
|
-
it('should resolve immediately if already set', async () => {
|
|
69
|
-
const token = new CancellationToken();
|
|
70
|
-
token.set();
|
|
71
|
-
await token.wait();
|
|
72
|
-
expect(token.isSet).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
it('should throw immediately if already set with waitThrow', async () => {
|
|
75
|
-
const token = new CancellationToken();
|
|
76
|
-
token.set();
|
|
77
|
-
await expect(token.waitThrow()).rejects.toThrow();
|
|
78
|
-
});
|
|
79
|
-
it('should throw immediately if already set', async () => {
|
|
80
|
-
const token = new CancellationToken();
|
|
81
|
-
token.set();
|
|
82
|
-
expect(() => token.throwIfSet()).toThrow();
|
|
83
|
-
});
|
|
84
|
-
it('should not throw if not set', () => {
|
|
85
|
-
const token = new CancellationToken();
|
|
86
|
-
expect(() => token.throwIfSet()).not.toThrow();
|
|
87
|
-
});
|
|
88
|
-
it('should support timeouts', async () => {
|
|
89
|
-
const token = new CancellationToken();
|
|
90
|
-
const timedSignal = token.withTimeout(10);
|
|
91
|
-
expect(timedSignal.isSet).toBe(false);
|
|
92
|
-
await timedSignal.wait();
|
|
93
|
-
expect(timedSignal.isSet).toBe(true);
|
|
94
|
-
expect(token.isSet).toBe(false);
|
|
95
|
-
});
|
|
96
|
-
it('should be observable', async () => {
|
|
97
|
-
const token = new CancellationToken();
|
|
98
|
-
const promise = firstValueFrom(from(token));
|
|
99
|
-
token.set();
|
|
100
|
-
await promise;
|
|
101
|
-
});
|
|
102
|
-
it('should support subscribe', () => {
|
|
103
|
-
const token = new CancellationToken();
|
|
104
|
-
const next = vi.fn();
|
|
105
|
-
const complete = vi.fn();
|
|
106
|
-
const subscription = token.subscribe({ next, complete });
|
|
107
|
-
token.set();
|
|
108
|
-
expect(next).toHaveBeenCalled();
|
|
109
|
-
expect(complete).toHaveBeenCalled();
|
|
110
|
-
subscription.unsubscribe();
|
|
111
|
-
});
|
|
112
|
-
it('should support Symbol.observable', () => {
|
|
113
|
-
const token = new CancellationToken();
|
|
114
|
-
const result = token[Symbol.observable]();
|
|
115
|
-
expect(result).toBe(token);
|
|
116
|
-
});
|
|
117
|
-
it('should return self as signal', () => {
|
|
118
|
-
const token = new CancellationToken();
|
|
119
|
-
expect(token.signal).toBe(token);
|
|
120
|
-
});
|
|
121
|
-
it('should handle subscribe when already set', () => {
|
|
122
|
-
const token = new CancellationToken();
|
|
123
|
-
token.set();
|
|
124
|
-
const next = vi.fn();
|
|
125
|
-
token.subscribe({ next });
|
|
126
|
-
expect(next).toHaveBeenCalled();
|
|
127
|
-
});
|
|
128
|
-
it('should support unsubscribe', () => {
|
|
129
|
-
const token = new CancellationToken();
|
|
130
|
-
const next = vi.fn();
|
|
131
|
-
const subscription = token.subscribe({ next });
|
|
132
|
-
subscription.unsubscribe();
|
|
133
|
-
token.set();
|
|
134
|
-
expect(next).not.toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|