{
  "schema_version": "1.0",
  "baseline": "A-document-access-service-v1",
  "phase": "Batch 8 · local/dev auth",
  "updated_at": "2026-04-20",
  "owner": "session_a",
  "purpose_en": "Session/identity contract for the document-access-service. Describes the login, lookup, and logout flows. Sessions are opaque UUIDs kept in-process on the server; they are NOT cryptographically signed.",
  "purpose_th": "สัญญา session · login/lookup/logout · token เป็น UUID แบบ opaque เก็บใน process · ไม่ได้ลงนาม",
  "honest_note": "Local/dev only. No password, no TLS, no signing. Sessions lost on server restart. Server acts as a honest stand-in for a future identity provider so shell code can be written once.",
  "endpoints": [
    {
      "method": "POST",
      "path": "/api/access/login",
      "body": { "email": "string (required)" },
      "returns": { "token": "uuid", "profile": "<user profile>", "mode": "local-dev" },
      "errors": [
        { "status": 400, "reason": "missing_email" },
        { "status": 401, "reason": "unknown_email" },
        { "status": 403, "reason": "disabled_profile" }
      ],
      "side_effects": [
        "Server issues cookie 'ds_session=<uuid>' with HttpOnly=false (so shell JS can read it in dev)",
        "Server stores token→profile_id in-process map"
      ]
    },
    {
      "method": "POST",
      "path": "/api/access/logout",
      "body": null,
      "returns": { "ok": true, "mode": "local-dev" },
      "side_effects": ["Server removes token from map", "Response clears cookie"]
    },
    {
      "method": "GET",
      "path": "/api/access/me",
      "auth": "Cookie ds_session OR query ?token=… OR Authorization: Bearer <uuid>",
      "returns": {
        "authenticated": "bool",
        "profile": "<user profile OR anonymous fallback>",
        "mode": "local-dev",
        "source_of_truth": "file-backed",
        "honest_banner": "string (bilingual)"
      },
      "errors": [],
      "notes": [
        "Missing/invalid token returns the anonymous profile with authenticated=false.",
        "Never 401 on /me — always yields a usable profile."
      ]
    },
    {
      "method": "GET",
      "path": "/api/access/health",
      "returns": {
        "ok": true,
        "mode": "local-dev",
        "user_store_loaded": "int",
        "started_at": "ISO-8601",
        "honest_banner": "string"
      }
    }
  ],
  "cookie": {
    "name": "ds_session",
    "httponly": false,
    "secure": false,
    "samesite": "Lax",
    "max_age_seconds": 86400,
    "notes": "Dev-only cookie. Set httponly=true + secure=true only when TLS is available."
  },
  "token_format": "UUID v4 · opaque · not signed",
  "token_lifetime": "In-process memory · lost on server restart",
  "fallback_on_no_token": "anonymous profile returned with authenticated=false",
  "cors_policy": {
    "allow_origin": "* (dev only)",
    "allow_credentials": true,
    "allow_methods": ["GET", "POST", "OPTIONS"]
  },
  "non_goals_this_batch": [
    "Password authentication",
    "Email verification",
    "OAuth / SSO",
    "Refresh tokens",
    "Cross-origin production deployment",
    "Token signing (JWT)",
    "Session audit log"
  ],
  "cross_references": {
    "access_contract": "docs/runtime/document-access-service/access_contract.json",
    "user_store":      "docs/runtime/document-access-service/user_store_examples.json",
    "access_schema":   "docs/assets/access/document_access_schema.json",
    "planning_doc":    "docs/planning/document-auth-backend.html"
  }
}
