{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.qauth.dev/interaction/v1-rc.0/steps/consent.json",
  "title": "urn:qauth:ia:consent:v1-rc.0",
  "description": "The OAuth consent decision (#629). The interaction’s `client` names the client, verified domain first (ADR-019 Decision 6). The grant is exactly the scope set shown here, which the interaction holds: the answer cannot add a scope. A dangerous scope and `prompt=consent` always reach this step; a first-party client may skip it only through the administrative waiver (ADR-017 Decision 9). Works without JavaScript.",
  "x-qauth-transports": [
    "interaction"
  ],
  "type": "object",
  "properties": {
    "type": {
      "const": "urn:qauth:ia:consent:v1-rc.0",
      "description": "The step name."
    },
    "params": {
      "$ref": "#/$defs/params"
    },
    "answer": {
      "$ref": "#/$defs/answer"
    },
    "personal": {
      "$ref": "#/$defs/personal"
    },
    "amr": {
      "$ref": "#/$defs/amr"
    }
  },
  "required": [
    "type",
    "params",
    "answer",
    "personal",
    "amr"
  ],
  "examples": [
    {
      "type": "urn:qauth:ia:consent:v1-rc.0",
      "params": {
        "scopes": [
          {
            "scope": "openid",
            "sensitive": false,
            "dangerous": false
          },
          {
            "scope": "notes:read",
            "text": "Read your notes",
            "sensitive": false,
            "dangerous": false
          },
          {
            "scope": "write:notes",
            "text": "Change and delete your notes",
            "sensitive": true,
            "dangerous": true
          }
        ],
        "resources": [
          "https://notes.example.com/"
        ],
        "remember_default": true
      },
      "answer": {
        "decision": "allow",
        "remember": true
      },
      "personal": {
        "identifier": "alice@example.com"
      },
      "amr": []
    }
  ],
  "$defs": {
    "params": {
      "type": "object",
      "properties": {
        "scopes": {
          "type": "array",
          "description": "The scopes asked for, in the order the screen lists them.",
          "minItems": 1,
          "maxItems": 64,
          "items": {
            "type": "object",
            "properties": {
              "scope": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[!#-;=?-\\[\\]-~]+$",
                "description": "The scope value (RFC 6749 §3.3), from its alphabet without `<` and `>`. A requested scope outside it never reaches this step: the request is refused first."
              },
              "text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 280,
                "pattern": "^[^<>\\p{C}\\p{Z}\\u115F\\u1160\\u3164\\uFFA0](?:[^<>\\p{C}\\p{Zl}\\p{Zp}\\u115F\\u1160\\u3164\\uFFA0]*[^<>\\p{C}\\p{Z}\\u115F\\u1160\\u3164\\uFFA0])?$",
                "x-qauth-display": "realm",
                "description": "The consent text the realm’s scope catalog holds for this scope, in the interaction’s locale. Absent when the catalog has none: the screen shows the scope value."
              },
              "sensitive": {
                "type": "boolean",
                "description": "Whether the realm flagged the scope as sensitive."
              },
              "dangerous": {
                "type": "boolean",
                "description": "Whether the scope is dangerous (`write:*`, `agent:admin`, `agent:exec`): it always needs consent and a fresh sign-in."
              }
            },
            "required": [
              "scope",
              "sensitive",
              "dangerous"
            ],
            "additionalProperties": false
          }
        },
        "resources": {
          "type": "array",
          "description": "Where the tokens may be used: the resource indicators (RFC 8707) of the request.",
          "minItems": 0,
          "maxItems": 16,
          "items": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^[a-z][a-z0-9+.-]*:[!#-;=?-\\[\\]-~]+$",
            "not": {
              "type": "string",
              "pattern": "^(?:javascript|data|vbscript|file|blob):"
            },
            "description": "One resource indicator, as the request named it. The request chose it, so the screen marks it as asked for by the app.",
            "x-qauth-declared": true
          }
        },
        "remember_default": {
          "type": "boolean",
          "description": "Whether \"remember this decision\" starts checked."
        }
      },
      "required": [
        "scopes",
        "resources",
        "remember_default"
      ],
      "additionalProperties": false,
      "examples": [
        {
          "scopes": [
            {
              "scope": "openid",
              "sensitive": false,
              "dangerous": false
            },
            {
              "scope": "notes:read",
              "text": "Read your notes",
              "sensitive": false,
              "dangerous": false
            },
            {
              "scope": "write:notes",
              "text": "Change and delete your notes",
              "sensitive": true,
              "dangerous": true
            }
          ],
          "resources": [
            "https://notes.example.com/"
          ],
          "remember_default": true
        }
      ]
    },
    "answer": {
      "type": "object",
      "properties": {
        "decision": {
          "type": "string",
          "enum": [
            "allow",
            "deny"
          ],
          "description": "The decision."
        },
        "remember": {
          "type": "boolean",
          "default": false,
          "description": "Whether to remember an `allow` for later requests that fit it. In a form post the checkbox sends `true`, and its absence means `false`."
        }
      },
      "required": [
        "decision"
      ],
      "additionalProperties": false,
      "examples": [
        {
          "decision": "allow",
          "remember": true
        }
      ]
    },
    "personal": {
      "type": "object",
      "description": "Who the transaction is for, so the screen can show it with a way to switch accounts. Absent until the transaction has an identifier.",
      "properties": {
        "identifier": {
          "type": "string",
          "minLength": 1,
          "maxLength": 254,
          "pattern": "^[^<>\\p{C}\\p{Z}\\u115F\\u1160\\u3164\\uFFA0](?:[^<>\\p{C}\\p{Zl}\\p{Zp}\\u115F\\u1160\\u3164\\uFFA0]*[^<>\\p{C}\\p{Z}\\u115F\\u1160\\u3164\\uFFA0])?$",
          "x-qauth-display": "account",
          "description": "The identifier of the account this transaction is for. Before a first factor succeeds it is the identifier exactly as the person gave it, after the one normalisation every input gets, whether or not an account holds it, so it tells nothing about which accounts exist; the form the account holds appears only after authentication. Personal data: only the personal-data read returns it."
        }
      },
      "additionalProperties": false,
      "examples": [
        {
          "identifier": "alice@example.com"
        }
      ]
    },
    "amr": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "const": [],
      "description": "The RFC 8176 `amr` values a successful answer adds to the transaction: none, because the step proves no authenticator."
    }
  }
}
