@rineex/auth-core 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/RULES.md ADDED
@@ -0,0 +1,1470 @@
1
+ # Step 7 — Port & Adapter Map (with Bundle-Size & Modularization First)
2
+
3
+ This step defines **what gets compiled together, what is optional, and what can
4
+ be tree-shaken**.
5
+
6
+ If this is wrong, your “install only what you need” promise is impossible.
7
+
8
+ ---
9
+
10
+ ## 1. Hard Requirement (Restated Clearly)
11
+
12
+ Your auth core must satisfy **all** of these:
13
+
14
+ - Zero required adapters
15
+ - Zero required auth methods
16
+ - Zero required storage
17
+ - Domain usable alone
18
+ - Each auth method installable separately
19
+ - Each adapter installable separately
20
+ - No transitive bloat
21
+ - Tree-shakable
22
+ - Side-effect free modules
23
+
24
+ If any package pulls more than it needs → fail.
25
+
26
+ ---
27
+
28
+ ## 2. High-Level Package Topology (Non-Negotiable)
29
+
30
+ This is the **only sane layout**:
31
+
32
+ ```
33
+ @auth/core-domain ← pure domain (mandatory)
34
+ @auth/core-ports ← domain ports (mandatory)
35
+
36
+ @auth/method-password
37
+ @auth/method-passwordless
38
+ @auth/method-otp
39
+ @auth/method-oauth
40
+ @auth/method-passkey
41
+ ...
42
+
43
+ @auth/adapter-memory
44
+ @auth/adapter-postgres
45
+ @auth/adapter-redis
46
+ @auth/adapter-http
47
+ @auth/adapter-jwt
48
+ @auth/adapter-oidc
49
+ ...
50
+ ```
51
+
52
+ ### Rules
53
+
54
+ - `core-domain` depends on **nothing**
55
+ - `core-ports` depends only on `core-domain`
56
+ - Methods depend on `core-domain + core-ports`
57
+ - Adapters depend on `core-ports`
58
+ - No reverse dependencies. Ever.
59
+
60
+ ---
61
+
62
+ ## 3. What Lives in `@auth/core-domain`
63
+
64
+ **Only meaning and rules.**
65
+
66
+ Includes:
67
+
68
+ - Aggregates
69
+ - Entities
70
+ - Value Objects
71
+ - Domain Services
72
+ - Invariants
73
+ - Domain Events
74
+
75
+ Explicitly excluded:
76
+
77
+ - Interfaces to infra
78
+ - Repositories
79
+ - Verification logic
80
+ - Serialization
81
+ - Config loading
82
+
83
+ Bundle size: **tiny** This package should never change often.
84
+
85
+ ---
86
+
87
+ ## 4. What Lives in `@auth/core-ports`
88
+
89
+ This is the **only dependency surface** for extensions.
90
+
91
+ ### 4.1 Persistence Ports
92
+
93
+ ```ts
94
+ PrincipalRepository;
95
+ CredentialRepository;
96
+ AuthenticationAttemptRepository;
97
+ AuthenticationSessionRepository;
98
+ ```
99
+
100
+ Rules:
101
+
102
+ - No SQL
103
+ - No ORM
104
+ - No query builders
105
+ - No pagination logic
106
+
107
+ ---
108
+
109
+ ### 4.2 Capability / SPI Ports
110
+
111
+ ```ts
112
+ AuthMethodRegistry;
113
+ AuthProofVerifier;
114
+ ChallengeIssuer;
115
+ RiskEvaluator;
116
+ TokenIssuer;
117
+ SessionRepresentationFactory;
118
+ ```
119
+
120
+ Rules:
121
+
122
+ - Stateless interfaces
123
+ - No default implementations
124
+ - No optional methods
125
+ - No framework types
126
+
127
+ ---
128
+
129
+ ## 5. Auth Methods as Separately Installable Units
130
+
131
+ Each auth method is **one package**.
132
+
133
+ Example:
134
+
135
+ ```
136
+ @auth/method-password
137
+ ```
138
+
139
+ Contains:
140
+
141
+ - AuthMethodDefinition
142
+ - Required input schema (domain-level)
143
+ - Proof mapping
144
+ - Registration function
145
+
146
+ Does NOT contain:
147
+
148
+ - Hashing logic
149
+ - DB access
150
+ - HTTP handling
151
+ - SDKs
152
+
153
+ This guarantees:
154
+
155
+ - Installing OTP does not install OAuth
156
+ - Installing OAuth does not install JWT
157
+ - Bundle size stays minimal
158
+
159
+ ---
160
+
161
+ ## 6. Adapter Packages (Infra Only)
162
+
163
+ Adapters implement **one port group only**.
164
+
165
+ Bad ❌
166
+
167
+ > one adapter doing DB + HTTP + JWT
168
+
169
+ Good ✅
170
+
171
+ ```
172
+ @auth/adapter-postgres
173
+ @auth/adapter-redis
174
+ @auth/adapter-jwt
175
+ @auth/adapter-http-express
176
+ @auth/adapter-http-hono
177
+ ```
178
+
179
+ Each adapter:
180
+
181
+ - Implements interfaces from `core-ports`
182
+ - Knows nothing about domain internals
183
+ - Can be swapped freely
184
+
185
+ ---
186
+
187
+ ## 7. Dependency Direction (Strict)
188
+
189
+ This diagram must never be violated:
190
+
191
+ ```
192
+ [ core-domain ]
193
+
194
+ [ core-ports ]
195
+
196
+ [ methods ] [ adapters ]
197
+ ```
198
+
199
+ Never:
200
+
201
+ - Adapter → Method
202
+ - Method → Adapter
203
+ - Domain → Port implementation
204
+ - Domain → Method
205
+
206
+ ---
207
+
208
+ ## 8. Tree-Shaking & Bundle Rules
209
+
210
+ To make bundle-size guarantees real:
211
+
212
+ - No side effects in module root
213
+ - No auto-registration
214
+ - Explicit `register()` calls
215
+ - Pure ES modules
216
+ - No global singletons
217
+ - No reflection-based loading
218
+
219
+ If a user doesn’t import it → it must not exist in bundle.
220
+
221
+ ---
222
+
223
+ ## 9. Runtime Composition (User Responsibility)
224
+
225
+ The **consumer** wires things together:
226
+
227
+ - Registers methods
228
+ - Provides adapters
229
+ - Selects flows
230
+ - Configures policies
231
+
232
+ Your core:
233
+
234
+ - Does not auto-wire
235
+ - Does not scan
236
+ - Does not assume environment
237
+
238
+ This keeps:
239
+
240
+ - Core small
241
+ - Behavior explicit
242
+ - Enterprise-safe
243
+
244
+ ---
245
+
246
+ ## 10. Validation Checklist (Must Pass)
247
+
248
+ Before moving forward:
249
+
250
+ ✔ Core domain installs alone ✔ No auth method is mandatory ✔ No adapter is
251
+ mandatory ✔ Importing one method doesn’t pull others ✔ Infra choices are
252
+ replaceable ✔ Bundle analyzer shows only used code
253
+
254
+ If any fails → restructure now.
255
+
256
+ ---
257
+
258
+ ## Step 7 Status
259
+
260
+ ✅ Port boundaries defined ✅ Modular packaging model defined ✅ Bundle-size
261
+ safe ✅ Tree-shakable ✅ Enterprise-usable
262
+
263
+ ---
264
+
265
+ # Step 9 — Minimal End-to-End Walkthrough (Passwordless)
266
+
267
+ > Goal: Show a complete authentication cycle using **Passwordless Email**, fully
268
+ > abstract, modular, and documented.
269
+
270
+ ---
271
+
272
+ ## 1. Module Setup (Separable & Installable)
273
+
274
+ ```
275
+ @auth/core-domain
276
+ @auth/core-ports
277
+ @auth/method-passwordless
278
+ @auth/adapter-memory (optional for demonstration)
279
+ ```
280
+
281
+ Rules:
282
+
283
+ - Core-domain + core-ports always installed
284
+ - Method installed separately
285
+ - Adapter optional
286
+ - No infra dependencies required
287
+
288
+ ---
289
+
290
+ ## 2. Example Domain Objects Involved
291
+
292
+ ```ts
293
+ // AuthenticationAttempt aggregate
294
+ const attempt = AuthenticationAttempt.create({
295
+ flowId: 'passwordless_flow',
296
+ principalId: 'user-123',
297
+ });
298
+
299
+ // Principal entity exists
300
+ const principal = Principal.create({ id: 'user-123' });
301
+
302
+ // Credential for Passwordless (domain-only)
303
+ const credential = Credential.create({
304
+ principalId: principal.id,
305
+ methodType: 'passwordless_email',
306
+ factor: 'possession',
307
+ status: 'Active',
308
+ });
309
+ ```
310
+
311
+ **Notes:**
312
+
313
+ - All objects are **domain-pure**.
314
+ - No secrets stored in domain.
315
+ - All lifecycle and invariants enforced.
316
+
317
+ ---
318
+
319
+ ## 3. AuthMethod Registration (SPI Usage)
320
+
321
+ ```ts
322
+ const registry: IAuthMethodRegistry = new AuthMethodRegistry();
323
+
324
+ // Passwordless Method (installed separately)
325
+ const passwordlessMethod: IAuthMethod<{ email: string }, AuthProof> =
326
+ new PasswordlessEmailMethod();
327
+
328
+ registry.register(passwordlessMethod);
329
+ ```
330
+
331
+ **Documentation Notes:**
332
+
333
+ - Registration explicit, tree-shakable.
334
+ - SPI ensures domain does not know implementation.
335
+ - Developers can see registry calls in code history for traceability.
336
+
337
+ ---
338
+
339
+ ## 4. Authentication Flow Definition (DSL)
340
+
341
+ ```ts
342
+ const passwordlessFlow = new AuthenticationFlow({
343
+ flowId: 'passwordless_flow',
344
+ name: 'Passwordless Email Flow',
345
+ steps: [
346
+ {
347
+ stepId: 'email_link_step',
348
+ authMethodType: 'passwordless_email',
349
+ required: true,
350
+ onSuccess: { targetStepId: 'terminal_success' },
351
+ onFailure: { targetStepId: 'terminal_failed' },
352
+ },
353
+ ],
354
+ entryConditions: [],
355
+ exitConditions: [],
356
+ });
357
+ ```
358
+
359
+ **Documentation Notes:**
360
+
361
+ - Flow is declarative and versionable.
362
+ - Steps link to AuthMethodType.
363
+ - Developers reading code see the exact orchestration chain.
364
+ - Policy references are explicit (future step-up integration).
365
+
366
+ ---
367
+
368
+ ## 5. Creating AuthenticationAttempt
369
+
370
+ ```ts
371
+ attempt.startFlow(passwordlessFlow);
372
+ ```
373
+
374
+ - Creates domain snapshot
375
+ - Status = `InProgress`
376
+ - Step = first step
377
+ - Immutable reference to flow
378
+
379
+ **Documentation Notes:**
380
+
381
+ - All state transitions logged in code via events
382
+ - Chainable domain events can be traced to attempt creation
383
+
384
+ ---
385
+
386
+ ## 6. Issue Challenge (Passwordless Email Example)
387
+
388
+ ```ts
389
+ const challenge = await passwordlessMethod.issueChallenge?.({
390
+ principalId: principal.id,
391
+ attemptId: attempt.id,
392
+ metadata: { email: 'user@example.com' },
393
+ });
394
+ ```
395
+
396
+ - Returns a Challenge object (domain-agnostic)
397
+ - No SMTP logic in domain
398
+ - Code clearly separates **domain vs infra**
399
+ - Challenge metadata traceable in code comments
400
+
401
+ ---
402
+
403
+ ## 7. Verify Challenge / Authenticate Step
404
+
405
+ ```ts
406
+ const proof = await passwordlessMethod.verifyChallenge?.(
407
+ {
408
+ challengeId: challenge.id,
409
+ response: 'user-clicked-link',
410
+ },
411
+ { principalId: principal.id, attemptId: attempt.id },
412
+ );
413
+
414
+ if (proof) {
415
+ attempt.completeStep(proof);
416
+ }
417
+ ```
418
+
419
+ **Documentation Notes:**
420
+
421
+ - Proof is immutable and domain-pure
422
+ - Chainable: attempt -> proof -> session creation
423
+ - Developer reading this sees exact flow progression
424
+
425
+ ---
426
+
427
+ ## 8. Session Creation
428
+
429
+ ```ts
430
+ const session = AuthenticationSession.createFromAttempt(attempt, {
431
+ trustLevel: 'Medium',
432
+ });
433
+ ```
434
+
435
+ - Generates domain session only
436
+ - No tokens, cookies, or DB
437
+ - TrustLevel reflects factors used
438
+ - ContextSnapshot stored for audit
439
+
440
+ **Documentation Notes:**
441
+
442
+ - Sessions are traceable back to attempt
443
+ - Code comments and JSDoc describe domain reasoning
444
+ - Any developer reading can follow entire chain
445
+
446
+ ---
447
+
448
+ ## 9. Policy Integration (Optional Hook)
449
+
450
+ ```ts
451
+ const policyResult = policyEvaluator.evaluate({ attempt, session, context });
452
+ if (policyResult.decision === 'RequireStepUp') {
453
+ // Step-up flow can be chained
454
+ }
455
+ ```
456
+
457
+ **Notes:**
458
+
459
+ - Policies are referenced, not hard-coded
460
+ - Fully traceable, chainable in domain events
461
+
462
+ ---
463
+
464
+ ## 10. Summary of Traceable Chain
465
+
466
+ ```
467
+ Principal -> Credential -> AuthFlow -> AuthenticationAttempt -> Challenge -> AuthProof -> AuthenticationSession
468
+ ```
469
+
470
+ **All events are documented, chainable, and domain-pure**. Developers reading
471
+ code can **trace every step** without touching infra.
472
+
473
+ ---
474
+
475
+ ## 11. Developer Documentation Strategy (Recommended)
476
+
477
+ - Each class/interface has **JSDoc** explaining:
478
+ - Purpose
479
+ - Usage
480
+ - Domain invariants
481
+ - Chainable events
482
+ - Hooks for adapters
483
+
484
+ - SPI registration, flows, attempts, proofs, sessions — **all documented**
485
+ - Link to external docs:
486
+ - Passwordless installation guide
487
+ - Adapter setup
488
+ - Policy DSL examples
489
+
490
+ - Optional: `@link` in JSDoc for chainable navigation
491
+
492
+ ---
493
+
494
+ ## Step 9 Status
495
+
496
+ ✅ Minimal end-to-end flow defined (Passwordless) ✅ Domain-only, infra-agnostic
497
+ ✅ Traceable chain with chainable documentation ✅ Modular, tree-shakable,
498
+ extensible
499
+
500
+ ---
501
+
502
+ # Step 10 — Policy DSL Formalization
503
+
504
+ > Goal: Enable fully declarative policies for authentication, step-up, risk, and
505
+ > multi-factor flows. Policies must be **versionable, chainable, and
506
+ > traceable**.
507
+
508
+ ---
509
+
510
+ ## 1. Core Principles
511
+
512
+ - Policies are **data**, not logic.
513
+ - Evaluated by a domain service (e.g., `AuthenticationPolicyEvaluator`).
514
+ - Traceable: each policy evaluation leaves a chainable record.
515
+ - Extensible: new conditions, actions, or flows can be added without code
516
+ change.
517
+ - Enterprise-ready: multi-tenant, step-up, conditional flows supported.
518
+
519
+ ---
520
+
521
+ ## 2. Policy Structure
522
+
523
+ ```ts
524
+ interface AuthenticationPolicy {
525
+ /**
526
+ * Unique identifier
527
+ */
528
+ policyId: string;
529
+
530
+ /**
531
+ * Human-readable name
532
+ */
533
+ name: string;
534
+
535
+ /**
536
+ * Scope of the policy: global, flow, principal, method
537
+ */
538
+ scope: PolicyScope;
539
+
540
+ /**
541
+ * Ordered rules
542
+ */
543
+ rules: PolicyRule[];
544
+
545
+ /**
546
+ * Optional metadata for auditing
547
+ */
548
+ metadata?: Record<string, unknown>;
549
+ }
550
+ ```
551
+
552
+ ---
553
+
554
+ ## 3. PolicyScope
555
+
556
+ ```ts
557
+ type PolicyScope =
558
+ | 'Global'
559
+ | 'Principal'
560
+ | 'AuthMethod'
561
+ | 'AuthFlow'
562
+ | 'Resource';
563
+ ```
564
+
565
+ - Declarative scope ensures traceability.
566
+ - Policies applied to same subject are evaluated in order.
567
+
568
+ ---
569
+
570
+ ## 4. PolicyRule
571
+
572
+ ```ts
573
+ interface PolicyRule {
574
+ /**
575
+ * Condition to evaluate
576
+ */
577
+ condition: ConditionExpression;
578
+
579
+ /**
580
+ * Action if condition is true
581
+ */
582
+ action: PolicyAction;
583
+
584
+ /**
585
+ * Optional reason for audit trail
586
+ */
587
+ reason?: string;
588
+ }
589
+ ```
590
+
591
+ - Rules are **atomic**, traceable, and versionable.
592
+ - Reason field ensures **chainable audit logging**.
593
+
594
+ ---
595
+
596
+ ## 5. ConditionExpression
597
+
598
+ ```ts
599
+ interface ConditionExpression {
600
+ subject: string; // e.g., 'risk.score', 'device.trusted', 'principal.type'
601
+ operator: Operator; // equals, notEquals, greaterThan, lessThan, in, notIn
602
+ value: unknown; // domain-agnostic value
603
+ }
604
+ ```
605
+
606
+ - Minimal, domain-agnostic
607
+ - Serializable
608
+ - Developers can trace which condition triggered which action
609
+
610
+ ### Operators
611
+
612
+ ```ts
613
+ type Operator =
614
+ | 'equals'
615
+ | 'notEquals'
616
+ | 'greaterThan'
617
+ | 'lessThan'
618
+ | 'in'
619
+ | 'notIn';
620
+ ```
621
+
622
+ ---
623
+
624
+ ## 6. PolicyAction
625
+
626
+ ```ts
627
+ type PolicyAction =
628
+ | { type: 'Allow' }
629
+ | { type: 'Deny' }
630
+ | { type: 'RequireStepUp'; requiredFactors: string[] }
631
+ | { type: 'SelectFlow'; flowId: string }
632
+ | { type: 'LimitTrustLevel'; level: TrustLevel };
633
+ ```
634
+
635
+ - Fully declarative
636
+ - Chainable
637
+ - Traceable in logs
638
+ - Supports step-up, conditional flows, multi-factor, trust downgrades
639
+
640
+ ---
641
+
642
+ ## 7. Policy Evaluation Result
643
+
644
+ ```ts
645
+ interface PolicyEvaluationResult {
646
+ decision: 'Allow' | 'Deny' | 'StepUpRequired' | 'FlowSelected';
647
+ triggeredRules: PolicyRule[];
648
+ metadata?: Record<string, unknown>;
649
+ }
650
+ ```
651
+
652
+ - All rules evaluated or short-circuited
653
+ - `triggeredRules` allows **audit trail chaining**
654
+ - Metadata includes reason, timestamp, and evaluation context
655
+
656
+ ---
657
+
658
+ ## 8. Policy Evaluation Flow (Abstract)
659
+
660
+ ```ts
661
+ class AuthenticationPolicyEvaluator {
662
+ async evaluate(
663
+ context: AuthEvaluationContext,
664
+ policies: AuthenticationPolicy[],
665
+ ): Promise<PolicyEvaluationResult> {
666
+ // Abstract: domain only
667
+ // Implementations will run the rules, produce decision, and trigger events
668
+ }
669
+ }
670
+ ```
671
+
672
+ - `AuthEvaluationContext` includes attempt, session, principal, device, risk,
673
+ etc.
674
+ - Result is **domain-only**, no infra logic.
675
+ - Each evaluation is **traceable, versionable, chainable**.
676
+
677
+ ---
678
+
679
+ ## 9. Example Policy (Passwordless Step-Up)
680
+
681
+ ```ts
682
+ const highRiskStepUpPolicy: AuthenticationPolicy = {
683
+ policyId: 'policy-001',
684
+ name: 'High Risk Step-Up',
685
+ scope: 'Global',
686
+ rules: [
687
+ {
688
+ condition: { subject: 'risk.score', operator: 'greaterThan', value: 70 },
689
+ action: { type: 'RequireStepUp', requiredFactors: ['otp'] },
690
+ reason: 'High-risk login requires additional verification',
691
+ },
692
+ ],
693
+ };
694
+ ```
695
+
696
+ - Traceable in logs (`policyId`, `reason`)
697
+ - Serializable for storage or audit
698
+ - Extensible for enterprise tenants
699
+
700
+ ---
701
+
702
+ ## 10. Multi-Rule & Flow Selection Example
703
+
704
+ ```ts
705
+ const enterpriseFlowPolicy: AuthenticationPolicy = {
706
+ policyId: 'policy-002',
707
+ name: 'Enterprise Flow Selector',
708
+ scope: 'Principal',
709
+ rules: [
710
+ {
711
+ condition: {
712
+ subject: 'principal.type',
713
+ operator: 'equals',
714
+ value: 'Service',
715
+ },
716
+ action: { type: 'SelectFlow', flowId: 'm2m_flow' },
717
+ reason: 'Service accounts must use machine-to-machine flow',
718
+ },
719
+ ],
720
+ };
721
+ ```
722
+
723
+ - Policies **do not execute flows**, just select them
724
+ - Chainable and auditable
725
+
726
+ ---
727
+
728
+ ## 11. Documentation & Traceability Rules
729
+
730
+ - Every policy and rule must have:
731
+ - `policyId`
732
+ - `reason`
733
+ - Optional metadata linking to docs or JSDoc
734
+
735
+ - All evaluations **emit triggeredRules** → chainable audit trail
736
+ - Developers can **follow evaluation chain in code** and see exact reasoning
737
+
738
+ ---
739
+
740
+ ## 12. Extensibility Guarantees
741
+
742
+ - New operators: add to `Operator` enum
743
+ - New actions: extend `PolicyAction` union
744
+ - New condition subjects: extend `AuthEvaluationContext`
745
+ - All changes are **modular, traceable, and versionable**
746
+
747
+ ---
748
+
749
+ ## Step 10 Status
750
+
751
+ ✅ Policy DSL formalized ✅ Declarative, auditable, traceable ✅
752
+ Enterprise-ready (step-up, multi-flow, trust-level) ✅ Chainable in code for
753
+ developer reference
754
+
755
+ ---
756
+
757
+ # Step 11 — Multi-Tenant / Enterprise Extension Model
758
+
759
+ > Goal: Enable the auth core to serve multiple tenants or enterprise clients
760
+ > with isolated configurations, policies, flows, and custom auth methods,
761
+ > without changing core domain.
762
+
763
+ ---
764
+
765
+ ## 1. Core Principles
766
+
767
+ - Each tenant can have **custom flows, policies, and auth methods**.
768
+ - Domain objects remain **tenant-agnostic**.
769
+ - Configuration is **composable**, versioned, and auditable.
770
+ - No core domain changes required for new tenants.
771
+ - Traceable: every domain object records **tenant context**.
772
+
773
+ ---
774
+
775
+ ## 2. Tenant Context (Value Object)
776
+
777
+ ```ts
778
+ interface TenantContext {
779
+ tenantId: string;
780
+ name?: string;
781
+ metadata?: Record<string, unknown>;
782
+ }
783
+ ```
784
+
785
+ - Attached to **Principal, AuthenticationAttempt, AuthenticationSession, Policy
786
+ evaluation**.
787
+ - Provides **traceable chain** of which tenant a domain object belongs to.
788
+ - Supports **multi-tenant logging and auditing**.
789
+
790
+ ---
791
+
792
+ ## 3. Tenant-Specific Flows
793
+
794
+ - Flows can be defined **per tenant**.
795
+ - Example:
796
+
797
+ ```ts
798
+ const tenantFlow = new AuthenticationFlow({
799
+ flowId: 'enterprise_passwordless',
800
+ tenantId: 'tenant-123',
801
+ name: 'Enterprise Passwordless Flow',
802
+ steps: [
803
+ {
804
+ stepId: 'email_link_step',
805
+ authMethodType: 'passwordless_email',
806
+ required: true,
807
+ },
808
+ ],
809
+ });
810
+ ```
811
+
812
+ - `tenantId` ensures flows are isolated.
813
+ - Developers can **trace flows by tenant**.
814
+
815
+ ---
816
+
817
+ ## 4. Tenant-Specific Policies
818
+
819
+ - Policies are scoped per tenant.
820
+ - Example:
821
+
822
+ ```ts
823
+ const enterprisePolicy: AuthenticationPolicy = {
824
+ policyId: 'policy-tenant-001',
825
+ name: 'Tenant 123 MFA Policy',
826
+ scope: 'Principal',
827
+ rules: [
828
+ {
829
+ condition: { subject: 'risk.score', operator: 'greaterThan', value: 50 },
830
+ action: { type: 'RequireStepUp', requiredFactors: ['otp'] },
831
+ reason: 'Tenant requires additional verification for medium-risk logins',
832
+ },
833
+ ],
834
+ metadata: { tenantId: 'tenant-123' },
835
+ };
836
+ ```
837
+
838
+ - Policies are **versioned** and **audit-traceable** per tenant.
839
+ - Chainable evaluation ensures **who read/triggered each policy** is recorded.
840
+
841
+ ---
842
+
843
+ ## 5. Tenant-Specific Auth Methods
844
+
845
+ - Methods can be **enabled or disabled per tenant**.
846
+ - Registration API supports tenant-scoping:
847
+
848
+ ```ts
849
+ tenantRegistry.register('tenant-123', passwordlessMethod);
850
+ ```
851
+
852
+ - Core domain sees only abstract `IAuthMethod`.
853
+ - Tree-shakable: uninstalled methods do not affect bundle size.
854
+
855
+ ---
856
+
857
+ ## 6. Tenant-Specific Credential Configuration
858
+
859
+ - Credential lifecycles can be configured per tenant:
860
+ - Expiration time
861
+ - Rotation rules
862
+ - Step-up requirements
863
+
864
+ - Credentials remain domain-agnostic; tenant-specific behavior enforced via
865
+ policies and adapters.
866
+
867
+ ---
868
+
869
+ ## 7. Tenant Context in AuthenticationAttempt
870
+
871
+ - Attach `tenantId` to each attempt:
872
+
873
+ ```ts
874
+ const attempt = AuthenticationAttempt.create({
875
+ flowId: 'enterprise_passwordless',
876
+ principalId: 'user-456',
877
+ tenantId: 'tenant-123',
878
+ });
879
+ ```
880
+
881
+ - Ensures **flow, policies, session, and proofs** are evaluated within tenant
882
+ scope.
883
+ - Supports **audit and traceability** across multi-tenant systems.
884
+
885
+ ---
886
+
887
+ ## 8. Tenant Context in AuthenticationSession
888
+
889
+ - Session includes tenant metadata:
890
+
891
+ ```ts
892
+ const session = AuthenticationSession.createFromAttempt(attempt, {
893
+ trustLevel: 'Medium',
894
+ tenantContext: { tenantId: 'tenant-123' },
895
+ });
896
+ ```
897
+
898
+ - Downstream authorization can enforce **tenant isolation**.
899
+ - Chainable: developers can trace session → attempt → tenant.
900
+
901
+ ---
902
+
903
+ ## 9. Extensibility & Overrides
904
+
905
+ - Tenants can:
906
+ - Add custom flows
907
+ - Override default policies
908
+ - Enable/disable auth methods
909
+ - Adjust trust-level rules
910
+
911
+ - Core domain is **unchanged**.
912
+ - Chainable documentation ensures each override is traceable.
913
+
914
+ ---
915
+
916
+ ## 10. Multi-Tenant Repository Pattern
917
+
918
+ ```ts
919
+ interface TenantAwareRepository<T> {
920
+ findByTenant(tenantId: string, id: string): Promise<T | null>;
921
+ saveForTenant(tenantId: string, entity: T): Promise<void>;
922
+ }
923
+ ```
924
+
925
+ - Ensures **tenant isolation at domain level**.
926
+ - Infrastructure handles actual storage, domain only sees tenant-scoped
927
+ interfaces.
928
+
929
+ ---
930
+
931
+ ## 11. Audit and Traceability
932
+
933
+ - Every domain event, policy evaluation, session creation, or flow execution:
934
+ - Includes `tenantId`
935
+ - Includes `principalId`
936
+ - Optional metadata for chainable docs or code references
937
+
938
+ - Developers reading code can follow **exact tenant-specific logic**.
939
+
940
+ ---
941
+
942
+ ## 12. Step 11 Status
943
+
944
+ ✅ Multi-tenant support defined ✅ Tenant-scoped flows, policies, auth methods,
945
+ sessions, attempts ✅ Chainable, traceable for audit ✅ Core domain remains
946
+ tenant-agnostic ✅ Enterprise-ready, tree-shakable, modular
947
+
948
+ ---
949
+
950
+ # Step 12 — Additional AuthMethod Implementations (Abstract)
951
+
952
+ > Goal: Provide modular contracts and structure for common auth methods beyond
953
+ > Passwordless, ensuring traceability, enterprise readiness, and minimal bundle
954
+ > size.
955
+
956
+ ---
957
+
958
+ ## 1. Module Layout
959
+
960
+ ```
961
+ @auth/method-otp
962
+ @auth/method-oauth
963
+ @auth/method-passkey
964
+ ```
965
+
966
+ - Each package is **optional**.
967
+ - Depends only on:
968
+ - `@auth/core-domain`
969
+ - `@auth/core-ports`
970
+
971
+ - Does **not depend** on other methods or adapters.
972
+
973
+ ---
974
+
975
+ ## 2. OTP Method (Abstract)
976
+
977
+ ### Purpose
978
+
979
+ - One-time codes delivered via email, SMS, or push.
980
+ - Supports **step-up, MFA, or primary auth**.
981
+
982
+ ### SPI Skeleton
983
+
984
+ ```ts
985
+ export interface IOtpMethod extends IAuthMethod<
986
+ { destination: string },
987
+ AuthProof
988
+ > {
989
+ /**
990
+ * Generate and issue OTP challenge
991
+ */
992
+ issueChallenge(
993
+ context: AuthContext,
994
+ options?: { length?: number; ttl?: number },
995
+ ): Promise<Challenge>;
996
+
997
+ /**
998
+ * Verify OTP response
999
+ */
1000
+ verifyChallenge(
1001
+ response: ChallengeResponse,
1002
+ context: AuthContext,
1003
+ ): Promise<AuthProof>;
1004
+ }
1005
+ ```
1006
+
1007
+ **Documentation Notes:**
1008
+
1009
+ - `destination` is transport-agnostic.
1010
+ - Challenge and proof remain domain-pure.
1011
+ - Developers can trace OTP flow from attempt → challenge → proof → session.
1012
+
1013
+ ---
1014
+
1015
+ ## 3. OAuth Method (Abstract)
1016
+
1017
+ ### Purpose
1018
+
1019
+ - Delegated authentication via third-party providers (Google, Azure, etc.)
1020
+ - Supports SSO and enterprise logins.
1021
+
1022
+ ### SPI Skeleton
1023
+
1024
+ ```ts
1025
+ export interface IOAuthMethod extends IAuthMethod<
1026
+ { code: string; provider: string },
1027
+ AuthProof
1028
+ > {
1029
+ /**
1030
+ * Initiate OAuth authorization
1031
+ */
1032
+ initiateAuth(context: AuthContext, provider: string): Promise<string>; // URL to redirect
1033
+
1034
+ /**
1035
+ * Complete OAuth authentication with provider code
1036
+ */
1037
+ completeAuth(code: string, context: AuthContext): Promise<AuthProof>;
1038
+ }
1039
+ ```
1040
+
1041
+ **Documentation Notes:**
1042
+
1043
+ - Domain sees only `AuthProof`.
1044
+ - No HTTP or token parsing in domain.
1045
+ - Chainable: principal → attempt → proof → session → policy evaluation.
1046
+
1047
+ ---
1048
+
1049
+ ## 4. Passkey / WebAuthn Method (Abstract)
1050
+
1051
+ ### Purpose
1052
+
1053
+ - Hardware-backed or platform credentials (biometrics, security keys)
1054
+ - Supports phishing-resistant enterprise login
1055
+
1056
+ ### SPI Skeleton
1057
+
1058
+ ```ts
1059
+ export interface IPasskeyMethod extends IAuthMethod<
1060
+ { clientData: unknown },
1061
+ AuthProof
1062
+ > {
1063
+ /**
1064
+ * Initiate passkey registration or authentication
1065
+ */
1066
+ initiate(
1067
+ context: AuthContext,
1068
+ options?: { type: 'register' | 'authenticate' },
1069
+ ): Promise<Challenge>;
1070
+
1071
+ /**
1072
+ * Verify passkey response
1073
+ */
1074
+ verify(response: ChallengeResponse, context: AuthContext): Promise<AuthProof>;
1075
+ }
1076
+ ```
1077
+
1078
+ **Documentation Notes:**
1079
+
1080
+ - `clientData` is abstracted; adapter handles platform specifics.
1081
+ - Challenge/proof remain domain-pure.
1082
+ - Developers can trace registration/authentication per attempt.
1083
+
1084
+ ---
1085
+
1086
+ ## 5. Common Principles Across Methods
1087
+
1088
+ - Tree-shakable: install only the methods you need.
1089
+ - Domain-agnostic: all proofs and challenges are abstractions.
1090
+ - Chainable: attempts, proofs, sessions, and policies are auditable.
1091
+ - Extensible: adding new methods requires only a new package implementing
1092
+ `IAuthMethod`.
1093
+
1094
+ ---
1095
+
1096
+ ## 6. Method Registration (Example)
1097
+
1098
+ ```ts
1099
+ const registry = new AuthMethodRegistry();
1100
+ registry.register(otpMethod); // @auth/method-otp
1101
+ registry.register(oauthMethod); // @auth/method-oauth
1102
+ registry.register(passkeyMethod); // @auth/method-passkey
1103
+ ```
1104
+
1105
+ - Explicit registration ensures **tree-shakability**.
1106
+ - Developers can trace **which methods are enabled per tenant or flow**.
1107
+
1108
+ ---
1109
+
1110
+ ## 7. Step 12 Status
1111
+
1112
+ ✅ Additional auth methods abstracted ✅ OTP, OAuth, Passkey supported ✅
1113
+ Modular, optional, installable separately ✅ Traceable chain from attempt →
1114
+ proof → session → policy ✅ Enterprise-ready, extensible, tree-shakable
1115
+
1116
+ ---
1117
+
1118
+ # Step 13 — Adapter Implementation Guides
1119
+
1120
+ > Goal: Provide a **modular, optional, and tree-shakable** adapter structure for
1121
+ > storage, token management, and transport layers.
1122
+
1123
+ ---
1124
+
1125
+ ## 1. Adapter Principles
1126
+
1127
+ - Implements **ports defined in `@auth/core-ports`**.
1128
+ - Tree-shakable: install only what you need.
1129
+ - Fully replaceable: e.g., Postgres ↔ Redis ↔ Memory.
1130
+ - Modular: one adapter per concern.
1131
+ - Traceable: every adapter operation can emit domain events or logs for
1132
+ auditing.
1133
+
1134
+ ---
1135
+
1136
+ ## 2. Adapter Package Layout
1137
+
1138
+ ```
1139
+ @auth/adapter-memory ← in-memory demo & tests
1140
+ @auth/adapter-postgres ← persistence
1141
+ @auth/adapter-redis ← caching / OTP / session storage
1142
+ @auth/adapter-jwt ← token issuance / verification
1143
+ @auth/adapter-oidc ← external SSO / OIDC provider
1144
+ @auth/adapter-http-express ← HTTP integration
1145
+ @auth/adapter-http-hono ← alternative HTTP framework
1146
+ ```
1147
+
1148
+ - Each package is optional.
1149
+ - Depends only on `@auth/core-ports`.
1150
+ - No cross-dependencies.
1151
+
1152
+ ---
1153
+
1154
+ ## 3. Persistence Adapters
1155
+
1156
+ ### 3.1 Credential Repository Adapter
1157
+
1158
+ ```ts
1159
+ export interface CredentialRepositoryAdapter extends CredentialRepository {
1160
+ // Adapter-specific configuration (Postgres table, Redis hash, etc.)
1161
+ }
1162
+ ```
1163
+
1164
+ **Documentation Notes:**
1165
+
1166
+ - Must implement domain interface `CredentialRepository`.
1167
+ - Domain only sees abstract operations:
1168
+ - save()
1169
+ - findById()
1170
+ - revoke()
1171
+
1172
+ - Storage details hidden in adapter.
1173
+ - Traceable via adapter logs or emitted events.
1174
+
1175
+ ---
1176
+
1177
+ ### 3.2 Principal / Session / Attempt Repositories
1178
+
1179
+ - Same pattern: implement `PrincipalRepository`,
1180
+ `AuthenticationSessionRepository`, `AuthenticationAttemptRepository`.
1181
+ - Example adapter: `PostgresPrincipalRepositoryAdapter`.
1182
+ - Optional: emit domain events for auditing.
1183
+
1184
+ ---
1185
+
1186
+ ## 4. Token Adapters
1187
+
1188
+ ### 4.1 JWT Adapter
1189
+
1190
+ ```ts
1191
+ export interface JwtAdapter extends SessionRepresentationFactory {
1192
+ issue(session: AuthenticationSession, options?: JwtOptions): Promise<string>;
1193
+ verify(token: string): Promise<AuthProof>;
1194
+ }
1195
+ ```
1196
+
1197
+ - Purely optional.
1198
+ - Domain never depends on JWT format.
1199
+ - Traceable: JWT issuance linked to sessionId → tenantId.
1200
+
1201
+ ---
1202
+
1203
+ ### 4.2 OIDC / OAuth Adapter
1204
+
1205
+ ```ts
1206
+ export interface OidcAdapter extends IOAuthMethod {
1207
+ initiateAuth(context: AuthContext, provider: string): Promise<string>;
1208
+ completeAuth(code: string, context: AuthContext): Promise<AuthProof>;
1209
+ }
1210
+ ```
1211
+
1212
+ - Adapter handles HTTP redirect, code exchange.
1213
+ - Domain sees only `AuthProof`.
1214
+ - Traceable through context and tenant metadata.
1215
+
1216
+ ---
1217
+
1218
+ ## 5. Caching / Temporary Storage Adapters
1219
+
1220
+ - OTP codes, challenge states, rate limiting.
1221
+ - Interface: implement `ChallengeStore` or `CredentialMaterialStore`.
1222
+ - Example: `RedisOtpAdapter`, `MemoryOtpAdapter`.
1223
+ - Modular: can swap infra without affecting domain.
1224
+
1225
+ ---
1226
+
1227
+ ## 6. HTTP Adapters
1228
+
1229
+ - Optional transport layer integration: Express, Hono, Fastify.
1230
+ - Implement endpoint wiring:
1231
+ - Receive request
1232
+ - Call registered auth method
1233
+ - Return proof/session
1234
+
1235
+ - Domain remains **transport-agnostic**.
1236
+
1237
+ ---
1238
+
1239
+ ## 7. Adapter Best Practices
1240
+
1241
+ 1. **Install separately** — don’t bundle with core.
1242
+ 2. **No domain logic** — only map ports → infra.
1243
+ 3. **Emit events** for auditing / tracing.
1244
+ 4. **Configurable per tenant** — adapter can read tenant-specific metadata.
1245
+ 5. **Tree-shakable** — unused adapters never included in bundle.
1246
+ 6. **Traceable code links** — each adapter operation references domain events or
1247
+ JSDoc for auditing.
1248
+
1249
+ ---
1250
+
1251
+ ## 8. Adapter Documentation Strategy
1252
+
1253
+ - Each adapter includes:
1254
+ - JSDoc explaining mapping to ports
1255
+ - Tenant-aware behavior
1256
+ - Optional emitted events for traceability
1257
+ - Usage example (tree-shakable)
1258
+
1259
+ - Chainable: core-domain → port → adapter → infra action
1260
+ - Developers can trace **who read / executed** each operation.
1261
+
1262
+ ---
1263
+
1264
+ ## Step 13 Status
1265
+
1266
+ ✅ Adapter types and patterns defined ✅ Storage, caching, tokens, HTTP, OIDC,
1267
+ JWT covered ✅ Modular, tree-shakable, optional ✅ Traceable chain from domain →
1268
+ port → adapter ✅ Enterprise-ready and multi-tenant safe
1269
+
1270
+ ---
1271
+
1272
+ # Step 14 — End-to-End Modular Integration Examples
1273
+
1274
+ > Goal: Show how to wire **core-domain, core-ports, methods, adapters, policies,
1275
+ > and multi-tenant context** together in a modular, traceable, and
1276
+ > enterprise-ready way.
1277
+
1278
+ ---
1279
+
1280
+ ## 1. Installable Modules
1281
+
1282
+ ```
1283
+ @auth/core-domain
1284
+ @auth/core-ports
1285
+ @auth/method-passwordless
1286
+ @auth/method-otp
1287
+ @auth/method-oauth
1288
+ @auth/adapter-memory
1289
+ @auth/adapter-jwt
1290
+ ```
1291
+
1292
+ - Each module optional
1293
+ - Installed only if needed → tree-shakable
1294
+
1295
+ ---
1296
+
1297
+ ## 2. Tenant Setup
1298
+
1299
+ ```ts
1300
+ const tenant: TenantContext = { tenantId: 'tenant-123', name: 'Acme Corp' };
1301
+ ```
1302
+
1303
+ - All subsequent flows, policies, and sessions are **scoped to tenant**
1304
+ - Chainable: attempt → session → policy → tenant
1305
+
1306
+ ---
1307
+
1308
+ ## 3. AuthMethod Registration per Tenant
1309
+
1310
+ ```ts
1311
+ const registry = new AuthMethodRegistry();
1312
+
1313
+ registry.registerForTenant(tenant.tenantId, passwordlessMethod);
1314
+ registry.registerForTenant(tenant.tenantId, otpMethod);
1315
+ registry.registerForTenant(tenant.tenantId, oauthMethod);
1316
+ ```
1317
+
1318
+ - Tree-shakable: unused methods not included
1319
+ - Traceable: registry logs tenant + methodType
1320
+
1321
+ ---
1322
+
1323
+ ## 4. Authentication Flow Setup
1324
+
1325
+ ```ts
1326
+ const flow = new AuthenticationFlow({
1327
+ flowId: 'enterprise_flow',
1328
+ tenantId: tenant.tenantId,
1329
+ name: 'Enterprise Multi-Method Flow',
1330
+ steps: [
1331
+ {
1332
+ stepId: 'passwordless_step',
1333
+ authMethodType: 'passwordless_email',
1334
+ required: true,
1335
+ },
1336
+ { stepId: 'otp_step', authMethodType: 'otp', required: false },
1337
+ ],
1338
+ });
1339
+ ```
1340
+
1341
+ - Steps refer to registered methods
1342
+ - Flow scoped per tenant
1343
+ - Chainable: each step → proof → policy evaluation
1344
+
1345
+ ---
1346
+
1347
+ ## 5. Policy Setup per Tenant
1348
+
1349
+ ```ts
1350
+ const policy: AuthenticationPolicy = {
1351
+ policyId: 'policy-tenant-001',
1352
+ name: 'Step-Up for High-Risk Logins',
1353
+ scope: 'Principal',
1354
+ rules: [
1355
+ {
1356
+ condition: { subject: 'risk.score', operator: 'greaterThan', value: 70 },
1357
+ action: { type: 'RequireStepUp', requiredFactors: ['otp'] },
1358
+ reason: 'High-risk logins require OTP',
1359
+ },
1360
+ ],
1361
+ metadata: { tenantId: tenant.tenantId },
1362
+ };
1363
+ ```
1364
+
1365
+ - Policies applied **before or after each step**
1366
+ - Chainable evaluation logged for traceability
1367
+
1368
+ ---
1369
+
1370
+ ## 6. Authentication Attempt & Execution
1371
+
1372
+ ```ts
1373
+ const principal = Principal.create({
1374
+ id: 'user-001',
1375
+ tenantId: tenant.tenantId,
1376
+ });
1377
+ const attempt = AuthenticationAttempt.create({
1378
+ flowId: flow.flowId,
1379
+ principalId: principal.id,
1380
+ tenantId: tenant.tenantId,
1381
+ });
1382
+
1383
+ // Execute passwordless step
1384
+ const passwordlessProof = await passwordlessMethod.authenticate(
1385
+ { email: 'user@example.com' },
1386
+ { principalId: principal.id, attemptId: attempt.id },
1387
+ );
1388
+
1389
+ // Update attempt
1390
+ attempt.completeStep(passwordlessProof);
1391
+
1392
+ // Evaluate policy after step
1393
+ const policyResult = await policyEvaluator.evaluate(
1394
+ { attempt, session: null, context: { tenantId: tenant.tenantId } },
1395
+ [policy],
1396
+ );
1397
+ ```
1398
+
1399
+ - Domain-only, infra-agnostic
1400
+ - Proof → session → policy evaluation chain traceable
1401
+ - Developers can see **full execution path** for audit
1402
+
1403
+ ---
1404
+
1405
+ ## 7. Session Creation
1406
+
1407
+ ```ts
1408
+ const session = AuthenticationSession.createFromAttempt(attempt, {
1409
+ trustLevel: 'Medium',
1410
+ tenantContext: tenant,
1411
+ });
1412
+ ```
1413
+
1414
+ - Captures **tenant, principal, flow, steps, proofs**
1415
+ - Immutable ContextSnapshot stored
1416
+ - Domain-pure, transport-agnostic
1417
+
1418
+ ---
1419
+
1420
+ ## 8. Adapter Wiring Example
1421
+
1422
+ ```ts
1423
+ const credentialRepo = new MemoryCredentialRepositoryAdapter();
1424
+ const sessionRepo = new MemoryAuthenticationSessionRepositoryAdapter();
1425
+ const jwtAdapter = new JwtAdapter({ secret: 'dummy' });
1426
+
1427
+ // Connect adapters to ports
1428
+ // Domain only interacts with repositories via core-ports interfaces
1429
+ ```
1430
+
1431
+ - Tree-shakable: only installed adapters included
1432
+ - Modular: can swap Postgres / Redis / JWT
1433
+ - Traceable: all actions reference tenant + attempt + session
1434
+
1435
+ ---
1436
+
1437
+ ## 9. Chainable Audit Trail
1438
+
1439
+ ```
1440
+ Tenant -> Principal -> AuthenticationAttempt -> AuthMethod Step -> Proof -> Session -> Policy Evaluation -> Adapter Event
1441
+ ```
1442
+
1443
+ - Every step **documented with JSDoc**
1444
+ - Events or logs link **who executed / evaluated**
1445
+ - Developers can trace **full end-to-end path** without touching infra
1446
+
1447
+ ---
1448
+
1449
+ ## 10. Developer Documentation Strategy
1450
+
1451
+ - Use **JSDoc** for:
1452
+ - Modules
1453
+ - SPI methods
1454
+ - Flows and steps
1455
+ - Policy evaluation
1456
+ - Tenant context
1457
+ - Adapter mapping
1458
+
1459
+ - Include `@link` to related domain objects
1460
+ - Chainable audit references included for every step
1461
+
1462
+ ---
1463
+
1464
+ ## Step 14 Status
1465
+
1466
+ ✅ Full modular integration example complete ✅ Multi-tenant, multi-method,
1467
+ policy-driven ✅ Tree-shakable and modular ✅ Domain-only traceable chain ✅
1468
+ Enterprise-ready
1469
+
1470
+ ---