Files
galaxy-game/authsession/PLAN.md
T
2026-04-08 16:23:07 +02:00

1153 lines
30 KiB
Markdown

# Auth / Session Service Implementation Plan
This plan has been already implemented and stays here for historical reasons.
It should NOT be threated as source of truth for service functionality.
## Purpose
This plan describes a detailed, incremental implementation path for
[`Auth / Session Service`](README.md) that integrates with the existing
`Edge Gateway`.
The plan is intentionally atomic.
Each stage should be small enough to implement, review, and test without
overloading development context.
## Global Rules for the Entire Plan
- keep domain logic independent from concrete storage backends;
- keep gateway projection separate from source-of-truth records;
- preserve the existing public auth contract expected by gateway:
- `send-email-code` -> `challenge_id`
- `confirm-email-code` -> `device_session_id`
- keep `confirm-email-code` synchronous;
- do not introduce a pending async session-provisioning model;
- use synchronous internal REST where immediate answer is required;
- use Redis Streams / pub-sub only for session lifecycle propagation and other
event-style side effects;
- keep implementation idempotent where retries are expected;
- design Redis-backed stores behind interfaces so SQL migration remains possible.
## Milestone Structure
Suggested milestones:
1. Domain skeleton and ports
2. In-memory service behavior and tests
3. Redis-backed source-of-truth stores
4. Gateway projection publisher
5. Public HTTP API
6. Internal trusted API
7. Integration with user-service and config-provider ports
8. Revoke/block flows
9. Observability and hardening
10. End-to-end integration with gateway
## Suggested Module Structure
The structure described below is allowed to be changed
during the Plan steps implementation.
```text
authsession/
├── cmd/
│ └── authsession/
│ └── main.go
├── internal/
│ ├── app/
│ │ ├── app.go
│ │ ├── bootstrap.go
│ │ └── wiring.go
│ │
│ ├── config/
│ │ ├── config.go
│ │ ├── env.go
│ │ └── validation.go
│ │
│ ├── domain/
│ │ ├── challenge/
│ │ │ ├── model.go
│ │ │ ├── state.go
│ │ │ ├── policy.go
│ │ │ └── errors.go
│ │ │
│ │ ├── devicesession/
│ │ │ ├── model.go
│ │ │ ├── state.go
│ │ │ ├── revoke.go
│ │ │ └── errors.go
│ │ │
│ │ ├── userresolution/
│ │ │ ├── model.go
│ │ │ └── policy.go
│ │ │
│ │ ├── sessionlimit/
│ │ │ ├── model.go
│ │ │ └── policy.go
│ │ │
│ │ └── common/
│ │ ├── email.go
│ │ ├── time.go
│ │ ├── ids.go
│ │ └── types.go
│ │
│ ├── ports/
│ │ ├── challengestore.go
│ │ ├── sessionstore.go
│ │ ├── userdirectory.go
│ │ ├── configprovider.go
│ │ ├── mailsender.go
│ │ ├── projectionpublisher.go
│ │ ├── clock.go
│ │ ├── idgenerator.go
│ │ ├── codegenerator.go
│ │ └── codehasher.go
│ │
│ ├── service/
│ │ ├── sendemailcode/
│ │ │ └── service.go
│ │ ├── confirmemailcode/
│ │ │ └── service.go
│ │ ├── getsession/
│ │ │ └── service.go
│ │ ├── listusersessions/
│ │ │ └── service.go
│ │ ├── revokedevicesession/
│ │ │ └── service.go
│ │ ├── revokeallusersessions/
│ │ │ └── service.go
│ │ ├── blockuser/
│ │ │ └── service.go
│ │ └── shared/
│ │ ├── normalize.go
│ │ ├── projection.go
│ │ └── publicerrors.go
│ │
│ ├── api/
│ │ ├── publichttp/
│ │ │ ├── handler_send_email_code.go
│ │ │ ├── handler_confirm_email_code.go
│ │ │ ├── dto.go
│ │ │ └── errors.go
│ │ │
│ │ └── internalhttp/
│ │ ├── handler_get_session.go
│ │ ├── handler_list_user_sessions.go
│ │ ├── handler_revoke_device_session.go
│ │ ├── handler_revoke_all_user_sessions.go
│ │ ├── handler_block_user.go
│ │ ├── dto.go
│ │ └── errors.go
│ │
│ ├── adapters/
│ │ ├── redis/
│ │ │ ├── challengestore/
│ │ │ │ └── store.go
│ │ │ ├── sessionstore/
│ │ │ │ └── store.go
│ │ │ ├── configprovider/
│ │ │ │ └── provider.go
│ │ │ └── gatewayprojection/
│ │ │ ├── publisher.go
│ │ │ ├── snapshot.go
│ │ │ └── stream.go
│ │ │
│ │ ├── userservice/
│ │ │ ├── client.go
│ │ │ ├── mapper.go
│ │ │ └── stub.go
│ │ │
│ │ ├── mail/
│ │ │ ├── stub.go
│ │ │ └── rest_client.go
│ │ │
│ │ ├── crypto/
│ │ │ ├── codehasher.go
│ │ │ └── publickey.go
│ │ │
│ │ ├── clock/
│ │ │ └── system.go
│ │ │
│ │ └── id/
│ │ ├── challengeid.go
│ │ └── devicesessionid.go
│ │
│ ├── observability/
│ │ ├── logging.go
│ │ ├── metrics.go
│ │ └── tracing.go
│ │
│ └── testkit/
│ ├── fixtures.go
│ ├── fake_clock.go
│ ├── fake_idgen.go
│ ├── fake_mail.go
│ ├── fake_userdir.go
│ └── fake_projection.go
├── api/
│ ├── public-openapi.yaml
│ └── internal-openapi.yaml
└── README.md
```
### Description
- `cmd/authsession` — service entry point: process startup, configuration loading, application assembly, and HTTP server startup.
- `internal/app` — top-level application orchestration layer: dependency initialization, runtime bootstrap, and component wiring.
- `internal/config` — service configuration loading, normalization, and validation from environment and other sources.
- `internal/domain/challenge` — domain model for the `send_email_code` / `confirm_email_code` challenge flow: states, transitions, TTL/retry policies, and domain errors.
- `internal/domain/devicesession` — domain model for `device_session`: session state, revocation, revoke reasons, and related domain errors.
- `internal/domain/userresolution` — domain model for user resolution by email through user-service: existing user, allowed registration, or blocked user.
- `internal/domain/sessionlimit` — domain model and policy rules for active `device_session` limits.
- `internal/domain/common` — shared domain value objects and helper types: email, time, identifiers, and common primitive types.
- `internal/ports` — interfaces for all external dependencies: source-of-truth stores, user-service, mail delivery, config, projection publisher, clock, generators, and hashing.
- `internal/service/sendemailcode` — use case for sending a code: email normalization, challenge lifecycle, suppression/send decision, and success-shaped public response.
- `internal/service/confirmemailcode` — use case for confirming a code: challenge validation, public-key validation, resolve/create user flow, session-limit enforcement, `device_session` creation, and projection publication.
- `internal/service/getsession` — use case for reading a single `device_session` for the trusted internal API.
- `internal/service/listusersessions` — use case for listing user sessions for the trusted internal API.
- `internal/service/revokedevicesession` — use case for revoking a single device session and publishing the updated gateway projection.
- `internal/service/revokeallusersessions` — use case for revoking all active sessions of a user and publishing the resulting updates.
- `internal/service/blockuser` — use case for blocking a user/email and revoking active sessions according to policy.
- `internal/service/shared` — shared application-layer code: normalization helpers, gateway projection builders, and public error mapping.
- `internal/api/publichttp` — public HTTP API for gateway integration: handlers, DTOs, and error mapping for `send_email_code` and `confirm_email_code`.
- `internal/api/internalhttp` — trusted internal HTTP API: revoke/read/list/block endpoints, DTOs, and separate internal error policy.
- `internal/adapters/redis/challengestore` — Redis adapter for source-of-truth challenge storage.
- `internal/adapters/redis/sessionstore` — Redis adapter for source-of-truth `device_session` storage.
- `internal/adapters/redis/configprovider` — Redis adapter for dynamic configuration, such as active-session limits.
- `internal/adapters/redis/gatewayprojection` — Redis adapter for the `Edge Gateway` integration projection: KV snapshots and lifecycle updates in streams.
- `internal/adapters/userservice` — user-service integration adapter: REST client, response-to-domain mapping, and stub implementation for early stages.
- `internal/adapters/mail` — mail-delivery adapter: development stub and future REST mail-service client.
- `internal/adapters/crypto` — cryptographic adapters: confirmation-code hashing and `client_public_key` validation/parsing.
- `internal/adapters/clock` — system clock implementation.
- `internal/adapters/id` — generation of stable domain identifiers such as `challenge_id` and `device_session_id`.
- `internal/observability` — service logging, metrics, and tracing.
- `internal/testkit` — test fixtures, fake/mock dependencies, and shared helpers for unit and integration tests.
- `api/public-openapi.yaml` — formal specification of the public HTTP API.
- `api/internal-openapi.yaml` — formal specification of the trusted internal HTTP API.
- `README.md` — architectural service description covering its role in the system, contracts, domain rules, and integrations.
---
## ~~Stage 1.~~ Freeze the Service Contract
Status: implemented.
### Goal
Write down the exact service-level contracts before implementation starts.
### Tasks
- freeze public auth use cases:
- `send_email_code`
- `confirm_email_code`
- freeze internal trusted use cases:
- `GetSession`
- `ListUserSessions`
- `RevokeDeviceSession`
- `RevokeAllUserSessions`
- `BlockUser`
- define canonical request/response DTOs for the service boundary;
- define client-safe error classes for the public auth API;
- define richer internal error classes for logs and internal API.
### Deliverables
- service contract notes in repo docs;
- initial error catalog;
- agreement on public vs internal API boundaries.
### Exit Criteria
- no unresolved ambiguity around public auth input/output shapes;
- no unresolved ambiguity around internal revoke/read operations.
---
## ~~Stage 2.~~ Define Core Domain Types
Status: implemented.
### Goal
Create the minimal domain model without any transport or storage code.
### Tasks
- define challenge aggregate concept;
- define device-session aggregate concept;
- define revoke reason model;
- define user resolution result model:
- existing user
- creatable user
- blocked user
- define session-limit decision model;
- define mail-delivery result model;
- define projection snapshot model for gateway integration;
- define domain statuses and allowed transitions.
### Important Constraints
- challenge and session models must not depend on Redis-specific encoding;
- gateway projection model must be separate from domain entities.
### Deliverables
- domain package with types only;
- transition invariants documented in code comments and tests.
### Exit Criteria
- domain package compiles without storage adapters;
- status transitions are covered by unit tests.
---
## ~~Stage 3.~~ Define Service Ports
Status: implemented.
### Goal
Create clean interfaces around every external dependency.
### Tasks
Define interfaces conceptually equivalent to:
- `ChallengeStore`
- `SessionStore`
- `UserDirectory` / `UserResolver`
- `ConfigProvider`
- `MailSender`
- `GatewaySessionProjectionPublisher`
- `Clock`
- `IDGenerator`
- `CodeGenerator`
- `CodeHasher`
### Notes
- `ChallengeStore` and `SessionStore` are source-of-truth ports;
- `GatewaySessionProjectionPublisher` is an integration port, not a domain
store;
- `UserDirectory` must support existing / creatable / blocked decisions and
user creation when allowed;
- `ConfigProvider` must support "limit absent" as a first-class case.
### Deliverables
- interface package or packages;
- port-level test doubles.
### Exit Criteria
- service layer can be implemented against interfaces only.
---
## ~~Stage 4.~~ Implement Pure Domain Services In Memory
Status: implemented.
### Goal
Implement the auth logic once, against in-memory stores and adapters.
### Tasks
Implement core use cases:
- `SendEmailCode`
- `ConfirmEmailCode`
- `GetSession`
- `ListUserSessions`
- `RevokeDeviceSession`
- `RevokeAllUserSessions`
- `BlockUser`
### Required Behaviors
#### SendEmailCode
- normalize email;
- consult `UserDirectory` policy if needed;
- create challenge;
- generate secure code;
- store only hashed code;
- attempt delivery or suppress it;
- always return a success-shaped result with `challenge_id`.
#### ConfirmEmailCode
- load challenge;
- validate expiration and status;
- validate code hash;
- validate `client_public_key` format;
- handle idempotent repeat confirm for same successful challenge and same key;
- resolve/create user through `UserDirectory`;
- reject blocked user;
- load session-limit config;
- count active sessions;
- reject if limit exceeded;
- create session;
- store session;
- move challenge into short-window confirmed state;
- publish session projection;
- return `device_session_id`.
#### Revoke Flows
- update source of truth;
- publish revoked projection for every affected session.
### Deliverables
- service layer with in-memory dependencies;
- unit tests for every public behavior.
### Exit Criteria
- full service logic is testable without Redis or HTTP;
- edge cases are covered by unit tests.
---
## ~~Stage 5.~~ Design Challenge Rules in Detail
Status: implemented.
### Goal
Remove ambiguity from challenge handling before persistent adapters are written.
### Tasks
- define challenge TTL;
- define max confirm attempts;
- define resend behavior policy, if any;
- define short idempotency window after successful confirm;
- define state machine for:
- new challenge
- sent/suppressed
- confirmed
- expired
- failed
- define exact behavior for repeated confirms:
- same code + same key -> same session id
- same code + different key -> fail
- expired challenge -> fail
- too many attempts -> fail
### Deliverables
- explicit challenge policy spec in code comments/tests.
### Exit Criteria
- no hidden challenge behavior remains undecided.
---
## ~~Stage 6.~~ Define Public Error Policy
Status: implemented.
### Goal
Make public auth failures predictable and safe.
### Tasks
Decide exact client-safe categories for:
- malformed e-mail;
- malformed `client_public_key`;
- unknown challenge;
- expired challenge;
- invalid code;
- blocked by policy at confirm stage;
- session limit exceeded;
- temporarily unavailable.
### Additional Rules
- `send_email_code` must not reveal whether the e-mail exists or is blocked;
- public errors should be normalized for gateway passthrough;
- internal logs and traces may keep richer reasons.
### Deliverables
- public error mapping table;
- internal error hierarchy.
### Exit Criteria
- gateway adapter behavior can be implemented without guesswork.
---
## ~~Stage 7.~~ Implement Redis ChallengeStore
Status: implemented.
### Goal
Add the first persistent backend for challenges.
### Tasks
- implement challenge read/write/update operations in Redis KV;
- define Redis key scheme for challenges;
- store hashed codes only;
- store challenge status and timestamps;
- support atomic compare-and-set style updates where required;
- support expiration cleanup through TTL and/or explicit status.
### Important Design Rule
The interface must not expose Redis primitives directly.
### Deliverables
- Redis-backed challenge store adapter;
- adapter integration tests against Redis.
### Exit Criteria
- challenge lifecycle works against Redis under concurrent access assumptions.
---
## ~~Stage 8.~~ Implement Redis SessionStore
Status: implemented.
### Goal
Add the first persistent backend for sessions.
### Tasks
- implement create/read/list/revoke operations;
- define Redis key scheme for sessions;
- support listing all sessions for one user;
- support revoking one session;
- support revoking all sessions for one user;
- support block-related session revocation;
- support active-session counting for limit enforcement;
- store revoke reason and actor metadata.
### Important Design Rule
The session source-of-truth record must remain distinct from gateway projection
encoding.
### Deliverables
- Redis-backed session store adapter;
- adapter integration tests.
### Exit Criteria
- all session lifecycle operations are persistent and testable.
---
## ~~Stage 9.~~ Implement Redis ConfigProvider
Status: implemented.
### Goal
Support dynamic session-limit configuration.
### Tasks
- implement config lookup from Redis KV;
- define config key scheme for auth-service settings;
- support:
- limit present with integer value
- limit absent
- invalid config value
- define fallback behavior for invalid config read.
### Required Behavior
- missing config -> no session-count limit;
- invalid config -> fail closed or fail safe according to explicit decision;
- document the chosen policy.
### Deliverables
- Redis-backed config adapter;
- tests for absent, valid, and invalid values.
### Exit Criteria
- session-limit logic no longer depends on hard-coded constants.
---
## ~~Stage 10.~~ Implement Gateway Session Projection Publisher
Status: implemented.
### Goal
Bridge auth source-of-truth state into gateway-facing cache/projection state.
### Tasks
- define exact projection snapshot structure consumed by gateway;
- define Redis KV key scheme for gateway session lookup;
- define Redis Stream schema for session lifecycle updates;
- implement projection write on session create;
- implement projection update on session revoke;
- implement projection update for bulk revoke/all;
- make publication idempotent and retry-safe.
### Important Constraints
- projection publisher should accept domain session data and transform it;
- it must not force domain logic to know Redis snapshot shape.
### Deliverables
- Redis-backed projection publisher;
- integration tests that emulate gateway expectations.
### Exit Criteria
- created sessions appear in gateway-readable projection;
- revoked sessions produce gateway-readable invalidation/update records.
---
## ~~Stage 11.~~ Implement Stub MailSender
Status: implemented.
### Goal
Introduce the mail-delivery port without coupling auth logic to one concrete delivery transport.
### Tasks
- create a stub adapter with deterministic success/failure modes;
- record delivery attempts for tests;
- support explicit suppression mode for blocked/hidden flows;
- ensure service logic can distinguish:
- sent
- suppressed
- failed
### Deliverables
- stub mail adapter;
- tests around challenge delivery state transitions.
### Exit Criteria
- auth logic is fully testable without real mail infrastructure.
---
## ~~Stage 12.~~ Implement Stub UserDirectory
Status: implemented.
### Goal
Introduce the user-service dependency before its real service exists.
### Tasks
- create an in-memory or stub REST-like adapter that can return:
- existing user
- creatable user
- blocked user
- support create-on-confirm behavior;
- support lookups by normalized email;
- support user block state.
### Deliverables
- stub user-service adapter;
- integration tests for auth flows.
### Exit Criteria
- auth-service no longer needs to fake user decisions internally.
---
## ~~Stage 13.~~ Implement Public HTTP API
Status: implemented.
### Goal
Expose the synchronous public auth flow expected by gateway.
### Tasks
- create HTTP handlers for:
- `send_email_code`
- `confirm_email_code`
- define JSON DTOs matching gateway expectations;
- implement request validation;
- implement response normalization;
- implement mapping from internal errors to public client-safe errors;
- add request timeout handling and structured logging.
### Important Constraints
- keep semantics aligned with gateway adapter expectations;
- do not expose internal admin/session methods on the public listener.
### Deliverables
- public HTTP server;
- handler tests;
- end-to-end tests through HTTP.
### Exit Criteria
- gateway can call the service through a real HTTP adapter.
---
## ~~Stage 14.~~ Implement Internal Trusted API
Status: implemented.
### Goal
Expose lifecycle and read operations for trusted internal callers.
### Tasks
Implement internal endpoints for:
- `GetSession`
- `ListUserSessions`
- `RevokeDeviceSession`
- `RevokeAllUserSessions`
- `BlockUser`
Optional additions later:
- unblock flow;
- challenge inspection.
### Notes
- this may use REST for simplicity;
- authentication/authorization of internal callers can be stubbed initially if
there is not yet a platform-wide internal auth mechanism.
### Deliverables
- internal HTTP API;
- handler tests.
### Exit Criteria
- session lifecycle can be driven without touching Redis manually.
---
## ~~Stage 15.~~ Implement Revoke Logic Thoroughly
Status: implemented.
### Goal
Make revoke behavior explicit and reliable.
### Tasks
For `RevokeDeviceSession`:
- load target session;
- no-op or explicit result if already revoked;
- persist revoke metadata;
- publish revoked projection.
For `RevokeAllUserSessions`:
- list active sessions for user;
- revoke each relevant session;
- publish projection for each affected session;
- preserve reason metadata.
For `BlockUser`:
- mark user blocked through `UserDirectory` or trusted policy adapter;
- revoke all active sessions;
- ensure future auth flow is denied at confirm stage and mail can be suppressed
at send stage.
### Deliverables
- complete revoke implementation;
- tests for single, bulk, and block flows.
### Exit Criteria
- gateway-facing revoke propagation is available for all revoke models.
---
## ~~Stage 16.~~ Add Consistency Safeguards
Status: implemented.
### Goal
Reduce create/revoke drift between source of truth and gateway projection.
### Tasks
- identify all places where source-of-truth write and projection publish happen;
- add retry strategy for projection writes;
- make projection publication idempotent;
- define recovery behavior if projection publish fails after source-of-truth
success;
- add dead-letter or repair strategy placeholder if needed later;
- document the consistency model.
### Preferred Short-Term Outcome
- source-of-truth success is never reported as auth success unless projection
write/publish reached the required success threshold, or the failure handling
policy is explicit and tested.
### Deliverables
- consistency policy document;
- tests for partial failure scenarios.
### Exit Criteria
- known failure windows are explicit and bounded.
---
## ~~Stage 17.~~ Add Public Anti-Abuse Hooks
Status: implemented.
### Goal
Prepare the auth service for safe interaction behind gateway public routing.
### Tasks
- add service-level hooks for challenge resend throttling;
- add max-attempt handling per challenge;
- add metrics for suppressed/blocked/sent flows;
- preserve soft anti-enumeration outward behavior.
### Notes
Gateway already applies public-edge rate limits.
This stage is about auth-specific flow protection, not replacing gateway limits.
### Deliverables
- abuse-control policy inside auth domain;
- tests for throttling and attempt exhaustion.
### Exit Criteria
- auth flow cannot be trivially abused through repeated confirm attempts.
---
## ~~Stage 18.~~ Add Observability
Status: implemented.
### Goal
Make the service operable from the beginning.
### Tasks
- structured logs for all major state transitions;
- metrics for all major operations;
- tracing spans for public auth flow and internal API;
- redact secrets and codes from logs;
- include stable identifiers such as challenge id, device session id, user id,
and reason codes where safe.
### Minimum Metrics
- challenges created;
- deliveries sent/suppressed/failed;
- confirm attempts;
- confirm successes/failures;
- sessions created;
- session limit rejections;
- sessions revoked by reason;
- projection publish failures;
- user-resolution outcomes.
### Deliverables
- metrics endpoint wiring if needed;
- logging/tracing middleware;
- observability tests where practical.
### Exit Criteria
- production debugging is possible without adding ad hoc logs later.
---
## ~~Stage 19.~~ Add Gateway-Compatibility Tests
Status: implemented.
### Goal
Test auth-service not just in isolation, but against gateway expectations.
### Tasks
- verify public auth HTTP DTO compatibility;
- verify `confirm-email-code` returns ready `device_session_id`;
- verify created session projection is readable by a gateway-compatible reader;
- verify revoked projection invalidates session;
- verify repeated confirm returns same session id in idempotency window;
- verify blocked e-mail still keeps `send_email_code` outwardly success-shaped;
- verify session limit exceeded returns stable client-visible error;
- verify malformed `client_public_key` is rejected.
### Deliverables
- integration test suite focused on gateway contract.
### Exit Criteria
- no ambiguity remains about integration with existing gateway behavior.
---
## ~~Stage 20.~~ Add Real REST Adapter to User Service Contract
Status: implemented.
### Goal
Prepare for future extraction of `User Service`.
### Tasks
- define internal REST client for user resolution/create/block operations;
- keep stub implementation for tests;
- add timeout, retry, and error mapping policy;
- define normalized email rules at the boundary.
### Deliverables
- REST client adapter for future user-service;
- compatibility tests using stub server.
### Exit Criteria
- auth-service can later switch from stub to real user-service with no domain
rewrite.
---
## ~~Stage 21.~~ Add Real Mail Adapter Contract
Status: implemented.
### Goal
Prepare for later internal mail-service-backed delivery.
### Tasks
- define mail adapter request/response contract;
- preserve current stub for tests;
- define delivery timeout and error mapping;
- define how suppression vs explicit failure is represented.
### Deliverables
- mail adapter interface finalized;
- optional HTTP client adapter skeleton.
### Exit Criteria
- auth flow is decoupled from the future mail implementation.
---
## ~~Stage 22.~~ Production Hardening Pass
Status: implemented.
### Goal
Review edge cases before calling the service implementation complete.
### Tasks
- test Redis reconnect behavior;
- test duplicate publish behavior;
- test crash/restart around confirm and revoke flows;
- test large numbers of active sessions per user;
- test concurrent confirms against the same challenge;
- test concurrent revoke and confirm races;
- test block-user during active auth flow;
- test expired challenge cleanup strategy.
### Deliverables
- hardening checklist;
- race-condition tests;
- operational notes.
### Exit Criteria
- no major known race remains undocumented.
---
## ~~Stage 23.~~ Optional Cleanup and Migration Readiness
Status: implemented.
### Goal
Make future SQL migration realistic.
### Tasks
- review whether domain services leak Redis assumptions;
- ensure all store interfaces are storage-agnostic;
- isolate key naming, stream naming, and projection serialization;
- add adapter contract tests reusable by future SQL backends.
### Deliverables
- backend-agnostic adapter tests;
- migration readiness notes.
### Exit Criteria
- a future SQL backend can be added without reworking service-layer logic.
---
## Recommended First Working Slice
If implementation needs an aggressively small first milestone, do this subset
first:
1. domain types
2. service ports
3. in-memory service logic
4. stub `UserDirectory`
5. stub `MailSender`
6. public HTTP API
7. Redis `SessionStore`
8. Redis `ChallengeStore`
9. Redis projection publisher
10. gateway-compatibility tests for:
- send-email-code
- confirm-email-code
- session projection after confirm
This gives an end-to-end happy path quickly, without waiting for revoke/admin
and full hardening.
## Recommended Second Slice
1. internal trusted API
2. session-limit config provider
3. revoke-device
4. revoke-all
5. block-user
6. observability
7. consistency safeguards
8. hardening tests
## Final Acceptance Criteria
The service can be considered implementation-ready when all of the following
are true:
- gateway can call public auth routes synchronously;
- `confirm-email-code` returns a ready `device_session_id`;
- the created session appears in gateway-compatible projection storage;
- revoked sessions publish gateway-compatible revoke updates;
- repeated successful confirm returns the same session id during the short
idempotency window;
- session creation respects dynamic limit config;
- user block prevents future auth flow and can revoke active sessions;
- all storage is hidden behind interfaces;
- auth-service is not required on the authenticated command hot path;
- logs, metrics, and tests cover the full lifecycle.
## Implementation Order Summary
```mermaid
flowchart TD
A["Freeze contracts"]
B["Domain model"]
C["Ports"]
D["In-memory service logic"]
E["Redis stores"]
F["Projection publisher"]
G["Public HTTP API"]
H["Internal trusted API"]
I["Revoke and block flows"]
J["Observability and hardening"]
K["Gateway compatibility tests"]
A --> B --> C --> D --> E --> F --> G --> H --> I --> J --> K
```