{
  "schema_version": "1.0",
  "baseline": "A-feature-flags-batch-4-readiness",
  "phase": "2b+ → 2c readiness",
  "updated_at": "2026-04-19",
  "owner": "session_a",
  "purpose_th": "ตัวอย่าง JWT claims · verified/decode-only/expired/invalid-signature/audience-mismatch/issuer-mismatch/kid-miss · ใช้เป็น fixture สำหรับ parity test เมื่อ verified mode พร้อม",
  "purpose_en": "JWT claim examples covering verified, decode-only, expired, invalid-signature, audience-mismatch, issuer-mismatch and kid-miss cases. Usable as fixtures once verified mode is wired. No real private keys. Signatures in these examples are placeholders (not computed).",
  "honest_note": "These are fixture rows, not live tokens. The 'sig_placeholder' field is illustrative — do NOT treat as a valid signature. Phase 2c harness must compute real signatures with test keys generated via docker/ai/keys/generate_keys.sh.",
  "reference_claims_contract": "docs/runtime/feature-flags-service/jwt_verification_notes.json (claim_expectation section)",
  "examples": [
    {
      "id": "JWT-EX-01",
      "label": "verified · happy path · staff role · tenant-pilot",
      "expected_mode": "verified",
      "expected_outcome": { "allow": true, "auth_source": "jwt", "warnings": [], "verified": true },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1001",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["staff"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/",
        "scope": ["flags.read", "flags.evaluate"],
        "approvals": []
      },
      "sig_placeholder": "<valid-rs256-for-kid-ptt-auth-2026-01>"
    },
    {
      "id": "JWT-EX-02",
      "label": "decode-only · JWKS unreachable · same claims as EX-01",
      "expected_mode": "decode-only",
      "expected_outcome": { "allow": true, "auth_source": "jwt_unverified", "warnings": ["auth_not_verified", "jwks_unreachable"], "verified": false },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1001",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["staff"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<any-rs256-bytes-ignored-in-decode-only>"
    },
    {
      "id": "JWT-EX-03",
      "label": "expired · exp in the past",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "token_expired", "http": 401 },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1002",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["member"],
        "iat": 1700000000,
        "exp": 1700003600,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<valid-but-token-is-expired>"
    },
    {
      "id": "JWT-EX-04",
      "label": "invalid signature · verified mode must deny",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "signature_invalid", "http": 401 },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1003",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["member"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<tampered-or-forged>"
    },
    {
      "id": "JWT-EX-05",
      "label": "audience mismatch · aud=pty-admin instead of pty-feature-flags",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "token_audience_mismatch", "http": 401 },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1004",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["admin"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-admin-policy",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<valid-signature-but-wrong-audience>"
    },
    {
      "id": "JWT-EX-06",
      "label": "issuer mismatch · iss from unexpected host",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "token_issuer_mismatch", "http": 401 },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1005",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["member"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://rogue.example.com/"
      },
      "sig_placeholder": "<valid-for-rogue-but-issuer-wrong>"
    },
    {
      "id": "JWT-EX-07",
      "label": "kid miss · refresh fails · falls through to decode-only",
      "expected_mode": "verified → decode-only (fallback)",
      "expected_outcome": { "allow": true, "auth_source": "jwt_unverified", "warnings": ["auth_not_verified", "kid_not_found"], "verified": false },
      "header": { "alg": "RS256", "kid": "ptt-auth-2025-12-retired", "typ": "JWT" },
      "payload": {
        "sub": "user_U1006",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["member"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<valid-for-retired-kid>"
    },
    {
      "id": "JWT-EX-08",
      "label": "algorithm mismatch · alg=HS256 with public-key JWKS · MUST deny",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "algorithm_mismatch", "http": 401 },
      "header": { "alg": "HS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "sub": "user_U1007",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["member"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<hmac-sha256-computed-with-public-key-bytes-ATTACK>"
    },
    {
      "id": "JWT-EX-09",
      "label": "alg=none attack · MUST deny in every mode except anonymous path",
      "expected_mode": "verified",
      "expected_outcome": { "allow": false, "code": "algorithm_mismatch", "http": 401 },
      "header": { "alg": "none", "typ": "JWT" },
      "payload": {
        "sub": "user_U1008",
        "tenant_id": "T-pty-pilot-01",
        "roles": ["admin"],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": ""
    },
    {
      "id": "JWT-EX-10",
      "label": "missing sub · treat as anonymous (per jwt_verification_notes on_missing_claim)",
      "expected_mode": "decode-only",
      "expected_outcome": { "allow": true, "auth_source": "none", "warnings": ["missing_claim:sub"], "verified": false },
      "header": { "alg": "RS256", "kid": "ptt-auth-2026-01", "typ": "JWT" },
      "payload": {
        "tenant_id": "T-pty-pilot-01",
        "roles": [],
        "iat": 1745049600,
        "exp": 9999999999,
        "aud": "pty-feature-flags",
        "iss": "https://auth.pattayatogether.internal/"
      },
      "sig_placeholder": "<valid-rs256-for-malformed-payload-missing-sub>"
    }
  ],
  "usage_note": "When the parity harness runs in Phase 2c, each row must be replayed against both legacy (decode-only) and verified paths. Outcomes recorded against parity_test_matrix.json. See cutover_readiness_checklist.json for the pass criteria.",
  "pair_with": {
    "jwks_notes": "docs/runtime/feature-flags-service/jwks_integration_notes.json",
    "verification_mode_matrix": "docs/runtime/feature-flags-service/verification_mode_matrix.json",
    "parity_matrix": "docs/runtime/feature-flags-service/parity_test_matrix.json"
  }
}
