@tstdl/base 0.93.182 → 0.93.184
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 +6 -1
- 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 +10 -1
- 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,91 +0,0 @@
|
|
|
1
|
-
import { ForbiddenError } from '../../../errors/index.js';
|
|
2
|
-
import { HttpHeaders } from '../../../http/http-headers.js';
|
|
3
|
-
import { HttpServerResponse } from '../../../http/server/index.js';
|
|
4
|
-
import { toArray } from '../../../utils/array/array.js';
|
|
5
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
-
import { csrfMiddleware } from '../middlewares/csrf.middleware.js';
|
|
7
|
-
describe('csrfMiddleware', () => {
|
|
8
|
-
const middleware = csrfMiddleware();
|
|
9
|
-
it('should allow safe methods (GET, HEAD, OPTIONS, TRACE)', async () => {
|
|
10
|
-
const safeMethods = ['GET', 'HEAD', 'OPTIONS', 'TRACE'];
|
|
11
|
-
for (const method of safeMethods) {
|
|
12
|
-
const context = {
|
|
13
|
-
request: { method, headers: new HttpHeaders() },
|
|
14
|
-
response: new HttpServerResponse(),
|
|
15
|
-
};
|
|
16
|
-
const next = vi.fn();
|
|
17
|
-
await middleware(context, next);
|
|
18
|
-
expect(next).toHaveBeenCalled();
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
it('should allow mutating methods (POST, PUT, DELETE, PATCH) without headers (non-browser client)', async () => {
|
|
22
|
-
const mutatingMethods = ['POST', 'PUT', 'DELETE', 'PATCH'];
|
|
23
|
-
for (const method of mutatingMethods) {
|
|
24
|
-
const context = {
|
|
25
|
-
request: { method, headers: new HttpHeaders() },
|
|
26
|
-
response: new HttpServerResponse(),
|
|
27
|
-
};
|
|
28
|
-
const next = vi.fn();
|
|
29
|
-
await middleware(context, next);
|
|
30
|
-
expect(next).toHaveBeenCalled();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
it('should allow same-origin requests via Sec-Fetch-Site', async () => {
|
|
34
|
-
const headers = new HttpHeaders();
|
|
35
|
-
headers.set('Sec-Fetch-Site', 'same-origin');
|
|
36
|
-
const context = {
|
|
37
|
-
request: { method: 'POST', headers },
|
|
38
|
-
response: new HttpServerResponse(),
|
|
39
|
-
};
|
|
40
|
-
const next = vi.fn();
|
|
41
|
-
await middleware(context, next);
|
|
42
|
-
expect(next).toHaveBeenCalled();
|
|
43
|
-
});
|
|
44
|
-
it('should allow user-initiated requests via Sec-Fetch-Site (none) when enabled', async () => {
|
|
45
|
-
const middlewareWithNone = csrfMiddleware({ allowFetchSiteNone: true });
|
|
46
|
-
const headers = new HttpHeaders();
|
|
47
|
-
headers.set('Sec-Fetch-Site', 'none');
|
|
48
|
-
const context = {
|
|
49
|
-
request: { method: 'POST', headers },
|
|
50
|
-
response: new HttpServerResponse(),
|
|
51
|
-
};
|
|
52
|
-
const next = vi.fn();
|
|
53
|
-
await middlewareWithNone(context, next);
|
|
54
|
-
expect(next).toHaveBeenCalled();
|
|
55
|
-
});
|
|
56
|
-
it('should reject cross-site requests via Sec-Fetch-Site', async () => {
|
|
57
|
-
const headers = new HttpHeaders();
|
|
58
|
-
headers.set('Sec-Fetch-Site', 'cross-site');
|
|
59
|
-
const context = {
|
|
60
|
-
request: { method: 'POST', headers },
|
|
61
|
-
response: new HttpServerResponse(),
|
|
62
|
-
};
|
|
63
|
-
const next = vi.fn();
|
|
64
|
-
await expect(middleware(context, next)).rejects.toThrow(ForbiddenError);
|
|
65
|
-
expect(next).not.toHaveBeenCalled();
|
|
66
|
-
});
|
|
67
|
-
it('should allow same-origin requests via Origin fallback', async () => {
|
|
68
|
-
const headers = new HttpHeaders();
|
|
69
|
-
headers.set('Origin', 'http://localhost:8080');
|
|
70
|
-
headers.set('Host', 'localhost:8080');
|
|
71
|
-
const context = {
|
|
72
|
-
request: { method: 'POST', headers },
|
|
73
|
-
response: new HttpServerResponse(),
|
|
74
|
-
};
|
|
75
|
-
const next = vi.fn();
|
|
76
|
-
await middleware(context, next);
|
|
77
|
-
expect(next).toHaveBeenCalled();
|
|
78
|
-
});
|
|
79
|
-
it('should include Vary: Sec-Fetch-Site, Origin for mutating methods', async () => {
|
|
80
|
-
const context = {
|
|
81
|
-
request: { method: 'POST', headers: new HttpHeaders() },
|
|
82
|
-
response: new HttpServerResponse(),
|
|
83
|
-
};
|
|
84
|
-
const next = vi.fn();
|
|
85
|
-
await middleware(context, next);
|
|
86
|
-
const vary = context.response.headers.tryGet('Vary');
|
|
87
|
-
const varyArray = toArray(vary).flatMap((v) => v.split(',').map((s) => s.trim()));
|
|
88
|
-
expect(varyArray).toContain('Sec-Fetch-Site');
|
|
89
|
-
expect(varyArray).toContain('Origin');
|
|
90
|
-
});
|
|
91
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { PasswordRequirementsError } from '../errors/password-requirements.error.js';
|
|
3
|
-
import { DefaultAuthenticationPasswordRequirementsValidator } from '../server/authentication-password-requirements.validator.js';
|
|
4
|
-
describe('DefaultAuthenticationPasswordRequirementsValidator', () => {
|
|
5
|
-
const validator = new DefaultAuthenticationPasswordRequirementsValidator();
|
|
6
|
-
it('should return success when password is strong and not pwned', async () => {
|
|
7
|
-
// A very long random string is unlikely to be pwned and will be strong
|
|
8
|
-
const result = await validator.testPasswordRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()');
|
|
9
|
-
expect(result.success).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
it('should return failure when password is pwned', async () => {
|
|
12
|
-
// "password" is definitely pwned
|
|
13
|
-
const result = await validator.testPasswordRequirements('password');
|
|
14
|
-
expect(result.success).toBe(false);
|
|
15
|
-
expect(result.reason).toContain('exposed in data breach');
|
|
16
|
-
});
|
|
17
|
-
it('should return failure when password is too weak', async () => {
|
|
18
|
-
// "abc" is too weak (and likely pwned)
|
|
19
|
-
const result = await validator.testPasswordRequirements('abc');
|
|
20
|
-
expect(result.success).toBe(false);
|
|
21
|
-
expect(result.reason).toBeDefined();
|
|
22
|
-
});
|
|
23
|
-
it('should throw PasswordRequirementsError on validation failure', async () => {
|
|
24
|
-
await expect(validator.validatePasswordRequirements('abc')).rejects.toThrow(PasswordRequirementsError);
|
|
25
|
-
});
|
|
26
|
-
it('should not throw on validation success', async () => {
|
|
27
|
-
await expect(validator.validatePasswordRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()')).resolves.not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
-
import { Auditor } from '../../audit/index.js';
|
|
3
|
-
import { AuthenticationClientService } from '../../authentication/client/index.js';
|
|
4
|
-
import { AuthenticationAncillaryService, AuthenticationService as AuthenticationServerService } from '../../authentication/server/index.js';
|
|
5
|
-
import { HttpClientOptions } from '../../http/client/index.js';
|
|
6
|
-
import { HttpServer } from '../../http/server/index.js';
|
|
7
|
-
import { runInInjectionContext } from '../../injector/index.js';
|
|
8
|
-
import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
9
|
-
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
10
|
-
import { SubjectService } from '../server/subject.service.js';
|
|
11
|
-
import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
|
|
12
|
-
describe('AuthenticationApiController Integration', () => {
|
|
13
|
-
let injector;
|
|
14
|
-
let database;
|
|
15
|
-
let service;
|
|
16
|
-
let serverService;
|
|
17
|
-
let subjectService;
|
|
18
|
-
let server;
|
|
19
|
-
const schema = 'authentication';
|
|
20
|
-
const tenantId = crypto.randomUUID();
|
|
21
|
-
beforeAll(async () => {
|
|
22
|
-
const storage = new Map();
|
|
23
|
-
globalThis.localStorage = {
|
|
24
|
-
getItem: vi.fn((key) => storage.get(key) ?? null),
|
|
25
|
-
setItem: vi.fn((key, value) => storage.set(key, value)),
|
|
26
|
-
removeItem: vi.fn((key) => storage.delete(key)),
|
|
27
|
-
clear: vi.fn(() => storage.clear()),
|
|
28
|
-
};
|
|
29
|
-
({ injector, database } = await setupIntegrationTest({
|
|
30
|
-
modules: { authentication: true, audit: true, keyValueStore: true, signals: true, api: true, webServer: true },
|
|
31
|
-
authentication: {
|
|
32
|
-
ancillaryService: DefaultAuthenticationAncillaryService,
|
|
33
|
-
},
|
|
34
|
-
}));
|
|
35
|
-
await runInInjectionContext(injector, async () => {
|
|
36
|
-
server = injector.resolve(HttpServer);
|
|
37
|
-
const httpClientOptions = injector.resolve(HttpClientOptions);
|
|
38
|
-
httpClientOptions.baseUrl = `http://localhost:${server.port}`;
|
|
39
|
-
serverService = await injector.resolveAsync(AuthenticationServerService);
|
|
40
|
-
subjectService = await injector.resolveAsync(SubjectService);
|
|
41
|
-
service = await injector.resolveAsync(AuthenticationClientService);
|
|
42
|
-
service.initialize();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
afterAll(async () => {
|
|
46
|
-
await server?.close(1000);
|
|
47
|
-
await injector?.dispose();
|
|
48
|
-
});
|
|
49
|
-
beforeEach(async () => {
|
|
50
|
-
globalThis.localStorage?.clear();
|
|
51
|
-
await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
|
|
52
|
-
});
|
|
53
|
-
test('login should work via API client', async () => {
|
|
54
|
-
await runInInjectionContext(injector, async () => {
|
|
55
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-login@example.com', firstName: 'A', lastName: 'L' });
|
|
56
|
-
await serverService.setPassword(user, 'Strong-Password-2026!');
|
|
57
|
-
await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
|
|
58
|
-
expect(service.isLoggedIn()).toBe(true);
|
|
59
|
-
expect(service.subjectId()).toBe(user.id);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
test('login should work even if an expired token is present in the Authorization header', async () => {
|
|
63
|
-
await runInInjectionContext(injector, async () => {
|
|
64
|
-
const user = await subjectService.createUser({ tenantId, email: 'expired-token-login@example.com', firstName: 'E', lastName: 'L' });
|
|
65
|
-
await serverService.setPassword(user, 'Strong-Password-2026!');
|
|
66
|
-
// Create an expired token
|
|
67
|
-
const now = currentTimestampSeconds();
|
|
68
|
-
const expiredTokenResult = await serverService.createToken({
|
|
69
|
-
subject: user,
|
|
70
|
-
sessionId: crypto.randomUUID(),
|
|
71
|
-
impersonator: undefined,
|
|
72
|
-
additionalTokenPayload: {},
|
|
73
|
-
refreshTokenExpiration: now - 3600,
|
|
74
|
-
expiration: now - 3600, // Expired 1 hour ago
|
|
75
|
-
issuedAt: now - 7200,
|
|
76
|
-
timestamp: (now - 7200) * 1000,
|
|
77
|
-
});
|
|
78
|
-
// Inject the expired token into the client service
|
|
79
|
-
service.updateRawTokens(`Bearer ${expiredTokenResult.token}`);
|
|
80
|
-
// Now try to login
|
|
81
|
-
await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
|
|
82
|
-
expect(service.isLoggedIn()).toBe(true);
|
|
83
|
-
expect(service.subjectId()).toBe(user.id);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
test('checkPassword should work via API client', async () => {
|
|
87
|
-
const result = await service.checkPassword('abc');
|
|
88
|
-
expect(result.strength).toBeLessThan(2);
|
|
89
|
-
});
|
|
90
|
-
test('refresh should work via API client', async () => {
|
|
91
|
-
await runInInjectionContext(injector, async () => {
|
|
92
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-refresh@example.com', firstName: 'A', lastName: 'L' });
|
|
93
|
-
await serverService.setPassword(user, 'Strong-Password-2026!');
|
|
94
|
-
await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
|
|
95
|
-
const initialToken = service.token()?.jti;
|
|
96
|
-
await service.refresh();
|
|
97
|
-
expect(service.token()?.jti).not.toBe(initialToken);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
test('impersonation should work via API client', async () => {
|
|
101
|
-
await runInInjectionContext(injector, async () => {
|
|
102
|
-
const admin = await subjectService.createUser({ tenantId, email: 'api-admin@example.com', firstName: 'A', lastName: 'D' });
|
|
103
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-user@example.com', firstName: 'U', lastName: 'S' });
|
|
104
|
-
await serverService.setPassword(admin, 'Admin-Pass-123!');
|
|
105
|
-
expect(await subjectService.exists(tenantId, admin.id)).toBe(true);
|
|
106
|
-
expect(await subjectService.exists(tenantId, user.id)).toBe(true);
|
|
107
|
-
await service.login({ tenantId, subject: admin.id }, 'Admin-Pass-123!');
|
|
108
|
-
await service.impersonate(user.id);
|
|
109
|
-
expect(service.subjectId()).toBe(user.id);
|
|
110
|
-
expect(service.token()?.impersonator).toBe(admin.id);
|
|
111
|
-
await service.unimpersonate();
|
|
112
|
-
expect(service.subjectId()).toBe(admin.id);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
test('password reset should work via API client', async () => {
|
|
116
|
-
await runInInjectionContext(injector, async () => {
|
|
117
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-reset@example.com', firstName: 'A', lastName: 'L' });
|
|
118
|
-
const ancillaryService = await injector.resolveAsync(AuthenticationAncillaryService);
|
|
119
|
-
await service.initPasswordReset({ tenantId, subject: user.id }, undefined);
|
|
120
|
-
await vi.waitFor(() => expect(ancillaryService.lastResetData).toBeDefined(), { timeout: 5000, interval: 50 });
|
|
121
|
-
await service.resetPassword(ancillaryService.lastResetData.token, 'New-API-Pass-123!');
|
|
122
|
-
await service.login({ tenantId, subject: user.id }, 'New-API-Pass-123!');
|
|
123
|
-
expect(service.isLoggedIn()).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
test('listSessions and invalidateAllOtherSessions should work via API client', async () => {
|
|
127
|
-
await runInInjectionContext(injector, async () => {
|
|
128
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-sessions@example.com', firstName: 'S', lastName: 'E' });
|
|
129
|
-
await serverService.setPassword(user, 'Strong-Password-2026!');
|
|
130
|
-
// Create two sessions
|
|
131
|
-
const auditor = injector.resolve(Auditor);
|
|
132
|
-
await serverService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor);
|
|
133
|
-
await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
|
|
134
|
-
const sessions = await service.listSessions();
|
|
135
|
-
expect(sessions.length).toBe(2);
|
|
136
|
-
expect(sessions[0]).toHaveProperty('id');
|
|
137
|
-
expect(sessions[0]).toHaveProperty('begin');
|
|
138
|
-
expect(sessions[0]).toHaveProperty('end');
|
|
139
|
-
await service.invalidateAllOtherSessions();
|
|
140
|
-
const updatedSessions = await service.listSessions();
|
|
141
|
-
// Only the current session should remain
|
|
142
|
-
expect(updatedSessions.length).toBe(1);
|
|
143
|
-
expect(updatedSessions[0].id).toBe(service.sessionId());
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
test('changePassword should work via API client', async () => {
|
|
147
|
-
await runInInjectionContext(injector, async () => {
|
|
148
|
-
const user = await subjectService.createUser({ tenantId, email: 'api-change-secret@example.com', firstName: 'C', lastName: 'S' });
|
|
149
|
-
await serverService.setPassword(user, 'Old-Strong-Password-2026!');
|
|
150
|
-
await service.login({ tenantId, subject: user.id }, 'Old-Strong-Password-2026!');
|
|
151
|
-
await service.changePassword('Old-Strong-Password-2026!', 'New-Strong-Password-2026!');
|
|
152
|
-
const authResult = await serverService.authenticateWithPassword({ tenantId, subject: user.id }, 'New-Strong-Password-2026!');
|
|
153
|
-
expect(authResult.success).toBe(true);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
|
|
2
|
-
import { AuthenticationApiRequestTokenProvider } from '../../authentication/server/authentication-api-request-token.provider.js';
|
|
3
|
-
import { AuthenticationService } from '../../authentication/server/index.js';
|
|
4
|
-
import { runInInjectionContext } from '../../injector/index.js';
|
|
5
|
-
import { setupIntegrationTest } from '../../testing/index.js';
|
|
6
|
-
import { SubjectService } from '../server/subject.service.js';
|
|
7
|
-
describe('AuthenticationApiRequestTokenProvider', () => {
|
|
8
|
-
let injector;
|
|
9
|
-
let authenticationService;
|
|
10
|
-
let subjectService;
|
|
11
|
-
let tokenProvider;
|
|
12
|
-
const tenantId = crypto.randomUUID();
|
|
13
|
-
beforeAll(async () => {
|
|
14
|
-
({ injector } = await setupIntegrationTest({ modules: { authentication: true, signals: true } }));
|
|
15
|
-
authenticationService = await injector.resolveAsync(AuthenticationService);
|
|
16
|
-
subjectService = await injector.resolveAsync(SubjectService);
|
|
17
|
-
tokenProvider = injector.resolve(AuthenticationApiRequestTokenProvider);
|
|
18
|
-
});
|
|
19
|
-
afterAll(async () => {
|
|
20
|
-
await injector?.dispose();
|
|
21
|
-
});
|
|
22
|
-
test('should extract and validate token', async () => {
|
|
23
|
-
await runInInjectionContext(injector, async () => {
|
|
24
|
-
const user = await subjectService.createUser({ tenantId, email: 'provider-test@example.com', firstName: 'P', lastName: 'T' });
|
|
25
|
-
const tokenResult = await authenticationService.getToken(user, undefined);
|
|
26
|
-
const request = {
|
|
27
|
-
headers: {
|
|
28
|
-
tryGet: (name) => name == 'Authorization' ? `Bearer ${tokenResult.token}` : undefined,
|
|
29
|
-
},
|
|
30
|
-
cookies: {
|
|
31
|
-
tryGet: () => undefined,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
const data = { request };
|
|
35
|
-
const token = await tokenProvider.tryGetToken(data);
|
|
36
|
-
expect(token.payload.subject).toBe(user.id);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
test('should return null if no token', async () => {
|
|
40
|
-
const request = {
|
|
41
|
-
headers: { tryGet: () => undefined },
|
|
42
|
-
cookies: { tryGet: () => undefined },
|
|
43
|
-
};
|
|
44
|
-
const data = { request };
|
|
45
|
-
const token = await tokenProvider.tryGetToken(data);
|
|
46
|
-
expect(token).toBeNull();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { Subject } from 'rxjs';
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
3
|
-
import { AuthenticationClientService } from '../../authentication/client/authentication.service.js';
|
|
4
|
-
import { AUTHENTICATION_API_CLIENT } from '../../authentication/client/tokens.js';
|
|
5
|
-
import { CancellationSignal, CancellationToken } from '../../cancellation/token.js';
|
|
6
|
-
import { BadRequestError } from '../../errors/bad-request.error.js';
|
|
7
|
-
import { ForbiddenError } from '../../errors/forbidden.error.js';
|
|
8
|
-
import { InvalidTokenError } from '../../errors/invalid-token.error.js';
|
|
9
|
-
import { NotFoundError } from '../../errors/not-found.error.js';
|
|
10
|
-
import { NotSupportedError } from '../../errors/not-supported.error.js';
|
|
11
|
-
import { UnauthorizedError } from '../../errors/unauthorized.error.js';
|
|
12
|
-
import { Injector } from '../../injector/index.js';
|
|
13
|
-
import { Lock } from '../../lock/index.js';
|
|
14
|
-
import { Logger } from '../../logger/index.js';
|
|
15
|
-
import { MessageBus } from '../../message-bus/index.js';
|
|
16
|
-
import { configureDefaultSignalsImplementation } from '../../signals/implementation/configure.js';
|
|
17
|
-
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
18
|
-
import { timeout } from '../../utils/timing.js';
|
|
19
|
-
describe('AuthenticationClientService Error Handling & Stuck States', () => {
|
|
20
|
-
let injector;
|
|
21
|
-
let service;
|
|
22
|
-
let mockApiClient;
|
|
23
|
-
let mockLock;
|
|
24
|
-
let mockMessageBus;
|
|
25
|
-
let mockLogger;
|
|
26
|
-
let disposeToken;
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
const storage = new Map();
|
|
29
|
-
globalThis.localStorage = {
|
|
30
|
-
getItem: vi.fn((key) => storage.get(key) ?? null),
|
|
31
|
-
setItem: vi.fn((key, value) => storage.set(key, value)),
|
|
32
|
-
removeItem: vi.fn((key) => storage.delete(key)),
|
|
33
|
-
clear: vi.fn(() => storage.clear()),
|
|
34
|
-
};
|
|
35
|
-
configureDefaultSignalsImplementation();
|
|
36
|
-
injector = new Injector('Test');
|
|
37
|
-
mockApiClient = {
|
|
38
|
-
login: vi.fn(),
|
|
39
|
-
refresh: vi.fn(),
|
|
40
|
-
timestamp: vi.fn().mockResolvedValue(currentTimestampSeconds()),
|
|
41
|
-
endSession: vi.fn().mockResolvedValue(undefined),
|
|
42
|
-
};
|
|
43
|
-
mockLock = {
|
|
44
|
-
tryUse: vi.fn(async (_timeout, callback) => {
|
|
45
|
-
const result = await callback({ lost: false });
|
|
46
|
-
return { success: true, result };
|
|
47
|
-
}),
|
|
48
|
-
use: vi.fn(async (_timeout, callback) => {
|
|
49
|
-
return await callback({ lost: false });
|
|
50
|
-
}),
|
|
51
|
-
};
|
|
52
|
-
mockMessageBus = {
|
|
53
|
-
publishAndForget: vi.fn(),
|
|
54
|
-
messages$: new Subject(),
|
|
55
|
-
dispose: vi.fn(),
|
|
56
|
-
allMessages$: new Subject(),
|
|
57
|
-
};
|
|
58
|
-
mockLogger = {
|
|
59
|
-
error: vi.fn(),
|
|
60
|
-
warn: vi.fn(),
|
|
61
|
-
info: vi.fn(),
|
|
62
|
-
debug: vi.fn(),
|
|
63
|
-
};
|
|
64
|
-
injector.register(AUTHENTICATION_API_CLIENT, { useValue: mockApiClient });
|
|
65
|
-
injector.register(Lock, { useValue: mockLock });
|
|
66
|
-
injector.register(MessageBus, { useValue: mockMessageBus });
|
|
67
|
-
injector.register(Logger, { useValue: mockLogger });
|
|
68
|
-
disposeToken = new CancellationToken();
|
|
69
|
-
injector.register(CancellationSignal, { useValue: disposeToken.signal });
|
|
70
|
-
});
|
|
71
|
-
afterEach(async () => {
|
|
72
|
-
disposeToken.set();
|
|
73
|
-
await injector.dispose();
|
|
74
|
-
});
|
|
75
|
-
function setupServiceWithToken(iatOffset = -10, expOffset = 5) {
|
|
76
|
-
const now = currentTimestampSeconds();
|
|
77
|
-
const initialToken = { iat: now + iatOffset, exp: now + expOffset, jti: 'initial' };
|
|
78
|
-
globalThis.localStorage.setItem('AuthenticationService:token', JSON.stringify(initialToken));
|
|
79
|
-
service = injector.resolve(AuthenticationClientService);
|
|
80
|
-
}
|
|
81
|
-
test('Corrupt Storage: should handle invalid JSON in storage gracefully', async () => {
|
|
82
|
-
// initialize with corrupt token
|
|
83
|
-
globalThis.localStorage.setItem('AuthenticationService:token', '{ invalid-json');
|
|
84
|
-
service = injector.resolve(AuthenticationClientService);
|
|
85
|
-
expect(service.token()).toBeUndefined();
|
|
86
|
-
expect(mockLogger.warn).toHaveBeenCalled(); // Should warn about parse error
|
|
87
|
-
});
|
|
88
|
-
test.each([
|
|
89
|
-
['InvalidTokenError', new InvalidTokenError()],
|
|
90
|
-
['NotFoundError', new NotFoundError('Session not found')],
|
|
91
|
-
['ForbiddenError', new ForbiddenError()],
|
|
92
|
-
['UnauthorizedError', new UnauthorizedError()],
|
|
93
|
-
['NotSupportedError', new NotSupportedError()],
|
|
94
|
-
['BadRequestError', new BadRequestError()],
|
|
95
|
-
])('Unrecoverable Errors: should logout on %s during refresh', async (_, error) => {
|
|
96
|
-
setupServiceWithToken();
|
|
97
|
-
mockApiClient.refresh.mockRejectedValue(error);
|
|
98
|
-
// Wait for loop to pick up refresh
|
|
99
|
-
await timeout(20);
|
|
100
|
-
expect(mockApiClient.endSession).toHaveBeenCalled();
|
|
101
|
-
expect(service.token()).toBeUndefined();
|
|
102
|
-
});
|
|
103
|
-
test('Recoverable Errors: should NOT logout on generic Error during refresh', async () => {
|
|
104
|
-
setupServiceWithToken();
|
|
105
|
-
mockApiClient.refresh.mockRejectedValue(new Error('Network failure'));
|
|
106
|
-
await timeout(20);
|
|
107
|
-
// Should NOT have called endSession (logout)
|
|
108
|
-
expect(mockApiClient.endSession).not.toHaveBeenCalled();
|
|
109
|
-
// Token should still be present (retry logic handles it)
|
|
110
|
-
expect(service.token()).toBeDefined();
|
|
111
|
-
// Trigger immediate retry to finish test quickly
|
|
112
|
-
service.requestRefresh();
|
|
113
|
-
await timeout(20);
|
|
114
|
-
});
|
|
115
|
-
test('Logout Failure: should clear local token even if server logout fails', async () => {
|
|
116
|
-
setupServiceWithToken(-1000, 1000);
|
|
117
|
-
mockApiClient.endSession.mockRejectedValue(new Error('Network error during logout'));
|
|
118
|
-
await service.logout();
|
|
119
|
-
expect(mockApiClient.endSession).toHaveBeenCalled();
|
|
120
|
-
expect(service.token()).toBeUndefined();
|
|
121
|
-
expect(mockMessageBus.publishAndForget).toHaveBeenCalled(); // loggedOutBus
|
|
122
|
-
});
|
|
123
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { of } from 'rxjs';
|
|
2
|
-
import { describe, expect, test, vi } from 'vitest';
|
|
3
|
-
import { HttpClientRequest, HttpClientResponse, HttpError, HttpErrorReason } from '../../http/index.js';
|
|
4
|
-
import { dontWaitForValidToken } from '../authentication.api.js';
|
|
5
|
-
import { logoutOnUnauthorizedMiddleware, waitForAuthenticationMiddleware } from '../client/http-client.middleware.js';
|
|
6
|
-
describe('waitForAuthenticationMiddleware', () => {
|
|
7
|
-
test('should wait for token and call next', async () => {
|
|
8
|
-
const authenticationServiceMock = {
|
|
9
|
-
token: vi.fn().mockReturnValue({ jti: 'test-token' }),
|
|
10
|
-
validToken$: of({ jti: 'test-token' }),
|
|
11
|
-
hasValidToken: true,
|
|
12
|
-
};
|
|
13
|
-
const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
|
|
14
|
-
const request = new HttpClientRequest('http://localhost');
|
|
15
|
-
request.context = {
|
|
16
|
-
endpoint: {
|
|
17
|
-
credentials: true,
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
const next = vi.fn().mockResolvedValue(undefined);
|
|
21
|
-
await middleware({ request }, next);
|
|
22
|
-
expect(next).toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
test('should NOT wait if endpoint has dontWaitForValidToken', async () => {
|
|
25
|
-
const authenticationServiceMock = {
|
|
26
|
-
isLoggedIn: vi.fn().mockReturnValue(true),
|
|
27
|
-
hasValidToken: false,
|
|
28
|
-
};
|
|
29
|
-
const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
|
|
30
|
-
const request = new HttpClientRequest('http://localhost');
|
|
31
|
-
request.context = {
|
|
32
|
-
endpoint: {
|
|
33
|
-
credentials: true,
|
|
34
|
-
data: {
|
|
35
|
-
[dontWaitForValidToken]: true,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
const next = vi.fn().mockResolvedValue(undefined);
|
|
40
|
-
await middleware({ request }, next);
|
|
41
|
-
expect(next).toHaveBeenCalled();
|
|
42
|
-
});
|
|
43
|
-
test('should NOT wait if credentials is NOT true', async () => {
|
|
44
|
-
const authenticationServiceMock = {
|
|
45
|
-
isLoggedIn: vi.fn().mockReturnValue(true),
|
|
46
|
-
hasValidToken: false,
|
|
47
|
-
};
|
|
48
|
-
const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
|
|
49
|
-
const request = new HttpClientRequest('http://localhost');
|
|
50
|
-
request.context = {
|
|
51
|
-
endpoint: {
|
|
52
|
-
credentials: false,
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
const next = vi.fn().mockResolvedValue(undefined);
|
|
56
|
-
await middleware({ request }, next);
|
|
57
|
-
expect(next).toHaveBeenCalled();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe('logoutOnUnauthorizedMiddleware', () => {
|
|
61
|
-
test('should call logout on 401 error', async () => {
|
|
62
|
-
const authenticationServiceMock = {
|
|
63
|
-
logout: vi.fn().mockResolvedValue(undefined),
|
|
64
|
-
};
|
|
65
|
-
const middleware = logoutOnUnauthorizedMiddleware(authenticationServiceMock);
|
|
66
|
-
const request = new HttpClientRequest('http://localhost');
|
|
67
|
-
const response = new HttpClientResponse({
|
|
68
|
-
request,
|
|
69
|
-
statusCode: 401,
|
|
70
|
-
statusMessage: 'Unauthorized',
|
|
71
|
-
headers: {},
|
|
72
|
-
body: undefined,
|
|
73
|
-
closeHandler: () => { }
|
|
74
|
-
});
|
|
75
|
-
const error = new HttpError(HttpErrorReason.StatusCode, request, { response });
|
|
76
|
-
const next = vi.fn().mockRejectedValue(error);
|
|
77
|
-
await expect(middleware({ request }, next)).rejects.toThrow(HttpError);
|
|
78
|
-
expect(authenticationServiceMock.logout).toHaveBeenCalled();
|
|
79
|
-
});
|
|
80
|
-
test('should NOT call logout on 401 error if endpoint has dontWaitForValidToken', async () => {
|
|
81
|
-
const authenticationServiceMock = {
|
|
82
|
-
logout: vi.fn().mockResolvedValue(undefined),
|
|
83
|
-
};
|
|
84
|
-
const middleware = logoutOnUnauthorizedMiddleware(authenticationServiceMock);
|
|
85
|
-
const request = new HttpClientRequest('http://localhost');
|
|
86
|
-
request.context = {
|
|
87
|
-
endpoint: {
|
|
88
|
-
resource: 'end-session',
|
|
89
|
-
data: {
|
|
90
|
-
[dontWaitForValidToken]: true,
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
const response = new HttpClientResponse({
|
|
95
|
-
request,
|
|
96
|
-
statusCode: 401,
|
|
97
|
-
statusMessage: 'Unauthorized',
|
|
98
|
-
headers: {},
|
|
99
|
-
body: undefined,
|
|
100
|
-
closeHandler: () => { }
|
|
101
|
-
});
|
|
102
|
-
const error = new HttpError(HttpErrorReason.StatusCode, request, { response });
|
|
103
|
-
const next = vi.fn().mockRejectedValue(error);
|
|
104
|
-
await expect(middleware({ request }, next)).rejects.toThrow(HttpError);
|
|
105
|
-
expect(authenticationServiceMock.logout).not.toHaveBeenCalled();
|
|
106
|
-
});
|
|
107
|
-
test('should not call logout on other errors', async () => {
|
|
108
|
-
const authenticationServiceMock = {
|
|
109
|
-
logout: vi.fn().mockResolvedValue(undefined),
|
|
110
|
-
};
|
|
111
|
-
const middleware = logoutOnUnauthorizedMiddleware(authenticationServiceMock);
|
|
112
|
-
const request = new HttpClientRequest('http://localhost');
|
|
113
|
-
const error = new Error('Some other error');
|
|
114
|
-
const next = vi.fn().mockRejectedValue(error);
|
|
115
|
-
await expect(middleware({ request }, next)).rejects.toThrow('Some other error');
|
|
116
|
-
expect(authenticationServiceMock.logout).not.toHaveBeenCalled();
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|