# Authentication scenario — authoring format for AIs

A **high-level JSON** describing an **editable authentication scenario** for the Jarroba Tools
*Authentication Flow Lab*. It is meant to be generated by an **AI** (reading this document) and
**pasted** by a person into the **"Import scenario"** box of the tool, where it is drawn on a canvas
and run with **real cryptography** (Web Crypto: real RSA keys, real signing and verification — the
attacks manipulate a genuine JWT, they are not mocked).

A scenario is a small graph plus an ordered script:

```jsonc
{
  "v": 1,
  "actores": [ /* the boxes on the canvas */ ],
  "enlaces": [ /* the wires between them */ ],
  "guion":   [ /* the ORDERED steps that play out */ ],
  "ataques": [ /* attacks that point at a step or an actor (optional) */ ]
}
```

## `actores` — the participants

```jsonc
{ "id": "cliente", "tipo": "cliente", "etiqueta": "Client", "x": 90, "y": 220,
  "config": { "postura": "vulnerable" } }
```

- `id` (required): unique, referenced by enlaces, guion and ataques.
- `tipo` (required): one of `cliente`, `emisor`, `verificador`, `atacante`, `generico`.
  - `emisor` **issues** the token (signs a real JWT).
  - `verificador` **checks** the token. Give it `config.postura`: `"vulnerable"` (the token decides how
    it is checked — the real-world design bug) or `"segura"` (the algorithm is pinned in advance, per
    RFC 8725, and claims like the audience are validated).
  - `atacante` intercepts the token (see `ataques`).
- `etiqueta` (required): the label shown in the box.
- `x`, `y` (optional): canvas position. Omit and the tool lays them out.
- `config.postura`: only for `verificador`.

## `enlaces` — the wires

```jsonc
{ "id": "w-login", "de": "cliente", "a": "auth" }
```

One wire per pair of actors. Steps reference a wire; several steps can travel over the same wire in
either direction. `de`/`a` are actor ids.

## `guion` — the ordered script (this is the timeline the player walks)

```jsonc
{ "id": "peticion", "enlace": "w-api", "sentido": "directo", "tipo": "porta",
  "texto": "The client calls the API with the token" }
```

- `id` (required): unique, referenced by `ataques` whose objetivo is a step.
- `enlace` (required): which wire the step travels over.
- `sentido`: `"directo"` (from the wire's `de` to its `a`) or `"inverso"` (the other way). Default `directo`.
- `tipo` (required): what happens in the step:
  - `mensaje` — something travels, no token yet (e.g. credentials).
  - `emite` — the **origin** actor (an `emisor`) signs a real JWT; it becomes the token in flight.
  - `porta` — the token in flight travels; an attack pointing here transforms it first.
  - `verifica` — the **destination** actor (a `verificador`) checks the token and produces the verdict.
- `texto`: the sentence shown for the step. Write it for a reader.

## `ataques` — what the attacker does (optional)

```jsonc
{ "id": "atk", "atacante": "atacante", "ataque": "alg-none",
  "objetivo": { "tipo": "paso", "id": "peticion" } }
```

- `atacante` (required): id of an actor of `tipo: "atacante"`.
- `objetivo` (required): `{ "tipo": "paso", "id": <step id> }` to intercept **that** step, or
  `{ "tipo": "actor", "id": <actor id> }` to intercept **every step that carries the token toward that
  actor**. Only steps of `tipo: "porta"` can be intercepted.
- `ataque` (required): one of
  - `alg-none` — the token claims it has no signature (Tim McLean, 2015).
  - `confusion-rs-hs` — the server's public key reused as an HMAC secret (RS256→HS256 confusion).
  - `jwk-embebida` — the attacker signs with their own key and embeds its public part in the header.
  - `kid-traversal` — a path traversal in the `kid` header points at a file with known contents
    (`/dev/null`), and the token is HS256-signed with that known secret (PortSwigger, kid path traversal).
  - `clave-debil` — a weak HMAC secret that gets brute-forced from a dictionary (needs the emisor to
    use a weak key; the tool arranges that when this attack is present).
  - `audiencia-cruzada` — a perfectly signed token, but issued for a different audience (`aud`).
  - `emisor-cruzado` — a perfectly signed token from the same platform, but a different issuer (`iss`).
  - `tipo-cruzado` — a perfectly signed token of a different type (`typ`), reused on this endpoint.
  - `sin-verificar` — the payload is edited and the server never checks the signature.

## Ground rules

- Return **only** the JSON object, no prose and no triple backticks around it.
- Every `enlace.de`/`.a`, every `paso.enlace`, and every `ataque.objetivo.id` must reference an id
  that exists. The tool shows a live list of problems; fix them until it is clean.
- A scenario needs at least one `verifica` step to reach a verdict.
- Nothing leaves the browser: the keys are generated locally and never included in the scenario.

## Worked example — the classic three-actor flow, attacked with alg=none

```json
{
  "v": 1,
  "actores": [
    { "id": "cliente", "tipo": "cliente", "etiqueta": "Client", "x": 90, "y": 220 },
    { "id": "auth", "tipo": "emisor", "etiqueta": "Authorization Server", "x": 440, "y": 90 },
    { "id": "recursos", "tipo": "verificador", "etiqueta": "Resource Server", "x": 440, "y": 350,
      "config": { "postura": "vulnerable" } },
    { "id": "atacante", "tipo": "atacante", "etiqueta": "Attacker", "x": 90, "y": 380 }
  ],
  "enlaces": [
    { "id": "w-login", "de": "cliente", "a": "auth" },
    { "id": "w-api", "de": "cliente", "a": "recursos" }
  ],
  "guion": [
    { "id": "credenciales", "enlace": "w-login", "sentido": "directo", "tipo": "mensaje",
      "texto": "The client sends its credentials to the Authorization Server" },
    { "id": "emision", "enlace": "w-login", "sentido": "inverso", "tipo": "emite",
      "texto": "The Authorization Server issues a signed JWT" },
    { "id": "peticion", "enlace": "w-api", "sentido": "directo", "tipo": "porta",
      "texto": "The client calls the Resource Server's API with that token" },
    { "id": "veredicto", "enlace": "w-api", "sentido": "directo", "tipo": "verifica",
      "texto": "The Resource Server checks the token and decides whether to accept it" }
  ],
  "ataques": [
    { "id": "atk", "atacante": "atacante", "ataque": "alg-none",
      "objetivo": { "tipo": "paso", "id": "peticion" } }
  ]
}
```

Paste it, press play, and watch a vulnerable verifier accept a token with no signature — then switch
the Resource Server to "segura" and watch the same token be rejected.
