{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://aidid.im/schemas/login/signin-message.schema.json",
  "title": "AIDID Canonical Sign-in Message",
  "description": "The CAIP-122 / EIP-4361-style canonical text whose UTF-8 bytes are the signed object (SPEC-LOGIN-v1 §5) — NOT JSON. Only `LF` line separators, no `CR`, no trailing newline, no extra whitespace. The RP MUST rebuild this text from its own stored request and compare byte-for-byte (§7.3); it MUST NOT trust the response's copy. `pattern` summarises the §5.1 line template (optional statement / Expiration Time / Request ID / Resources blocks are conditional).",
  "type": "string",
  "minLength": 1,
  "pattern": "^[^\\r\\n]+ wants you to sign in with your MSG account:\\n[^\\r\\n]+\\n\\n(?:[^\\r\\n]+\\n\\n)?URI: [^\\r\\n]+\\nVersion: [^\\r\\n]+\\nChain ID: [^\\r\\n]+\\nNonce: [^\\r\\n]+\\nIssued At: [^\\r\\n]+(?:\\nExpiration Time: [^\\r\\n]+)?(?:\\nRequest ID: [^\\r\\n]+)?(?:\\nResources:(?:\\n- [^\\r\\n]+)+)?$",
  "examples": [
    "demo-rp.example wants you to sign in with your MSG account:\ndid:msg:qJoURrDO2wbAgVqLlc9jEw\n\nSign in to the offline AIDID demo.\n\nURI: https://demo-rp.example/callback\nVersion: 1.0\nChain ID: msg-chain-1\nNonce: demo-nonce-0123456789abcdef0123456789abcdef\nIssued At: 2026-09-25T08:00:00.000Z\nExpiration Time: 2026-09-25T08:05:00.000Z\nRequest ID: offline-demo-1\nResources:\n- did:auth\n- vc:membership"
  ],
  "x-aidid": {
    "spec": {
      "doc": "AIDID_第三方登录规范_v1.md",
      "section": "§5.1",
      "version": "v1.3"
    },
    "status": "implemented",
    "source": [
      "frontend/packages/aidid-login-sdk/src/message.js#buildSignInMessage",
      "frontend/packages/aidid-login-sdk/src/message.js#buildSignInMessageForRequest",
      "frontend/packages/aidid-login-sdk/src/message.js#parseSignInMessage"
    ],
    "note": "Fixed namespace literal is `MSG`. Fixed labels: `URI: `, `Version: `, `Chain ID: `, `Nonce: `, `Issued At: `, `Expiration Time: `, `Request ID: `, and the final `Resources:` block (`- ` per entry, one per line, order preserved). `{address}` is the DID itself. `{statement}` is a single line (CR/LF rejected). No field is trimmed, URL-decoded, case-folded or Unicode-normalised. `parseSignInMessage` reads `Expiration Time` / `Request ID` / `Resources` as optional, but the shipped `SignInRequest` wire type always carries `expirationTime`, so in the end-to-end flow the line is present.",
    "honestLimit": "The text is implemented and locked by an offline Dilithium-5 vector, but nothing here proves production signing: the in-repo demo build signs with a local ECDSA P-384 bridge and the test keypairs are public demo material."
  }
}
