# `route.header_credential`

A route's input reads a header that sign-in or the runtime reads first, such as `Authorization`, a cookie or `Idempotency-Key`.

Area 7, [HTTP](/docs/http.md); not declinable; the next step is usually the agent's.

## Why Jadpo refuses it

Some headers are read before your code runs. Sign-in reads the
credentials, `Authorization` and cookies, and Jadpo's own `X-Jadpo-`
headers, to find out who is calling. The runtime reads `Idempotency-Key`
to answer a retried write once.

A route never sees these as input. If an action could read a credential,
it could copy or log it, or trust a token sign-in never checked. Who is
calling reaches your code only as the caller sign-in found.

## What it looks like

main.jadpo:

```jadpo
type Memo = Text {
    max_length: 200
}

type MemoId = Uuid {}

type AddMemo = Input {
    text: Memo
}

type MemoView = Output {
    text: Memo
}

action add_memo(input: AddMemo) -> MemoView {
    return MemoView {
        text: input.text
    }
}

type TaggedMemo = Input {
    text: Memo
    token: Memo {
        header: "Authorization"
    }
}

action add_tagged_memo(input: TaggedMemo) -> MemoView {
    return MemoView {
        text: input.text
    }
}

policy {
    actions: {
        add_tagged_memo: [Access.public]
    }
}

routes Memos {
    prefix: /memos

    POST / {
        authentication: RouteAuthentication.none
        input: TaggedMemo
        output: MemoView
        run: add_tagged_memo(input)
    }
}
```

```text
error: `Authorization` belongs to authentication
 --> main.jadpo:24:17
   |
24 |         header: "Authorization"
   |                 ^^^^^^^^^^^^^^^ read by authentication
  reason: Credentials (`Authorization`, cookies) and Jadpo's own `X-Jadpo-` headers are read by authentication (area 8), so a route never sees them as input.
  next: Remove the field, and take the caller as the route's `current_principal` (agent)
  rule: route.header_credential
  help: https://jadpo.dev/docs/errors/route.header_credential

check failed: 1 error, 1 file
```

`(agent)` at the end of the `next:` line means your coding agent can make
the fix itself.

A field read from `Idempotency-Key` is refused the same way, because the
runtime answers a retry with the same key before the action runs:

```jadpo
type Memo = Text {
    max_length: 200
}

type RetriedMemo = Input {
    text: Memo
    key: Memo {
        header: "Idempotency-Key"
    }
}

type MemoView = Output {
    text: Memo
}

action add_retried_memo(input: RetriedMemo) -> MemoView {
    return MemoView {
        text: input.text
    }
}

policy {
    actions: {
        add_retried_memo: [Access.public]
    }
}

routes Memos {
    prefix: /memos

    POST / {
        authentication: RouteAuthentication.none
        input: RetriedMemo
        output: MemoView
        run: add_retried_memo(input)
    }
}
```

```text
error: `Idempotency-Key` belongs to the runtime
 --> main.jadpo:8:17
  |
8 |         header: "Idempotency-Key"
  |                 ^^^^^^^^^^^^^^^^^ read by the runtime
  reason: The runtime reads `Idempotency-Key` to answer a retried write once, before anything runs, so the action never sees the key (area 7).
  next: Remove the field; a retry with the same key is answered by the runtime (agent)
  rule: route.header_credential
  help: https://jadpo.dev/docs/errors/route.header_credential

check failed: 1 error, 1 file
```

## How to fix it

Remove the field. The question left is whether the action needs to know
who is calling.

**If the action doesn't need the caller**, the input keeps only the
fields the client sends. `TaggedMemo` becomes:

```jadpo
type TaggedMemo = Input {
    text: Memo
}
```


The route reads only the body's `text`. An `Authorization` header a
client sends no longer reaches the action. The same fix applies to an
`Idempotency-Key` field, and a retry with the same key is still answered
once by the runtime.

**If the action needs to know who is calling**, the route needs a
signed-in caller, so it drops `authentication: RouteAuthentication.none`
and passes `current_principal.user`. The app must sign people in, so this
example adds a principal and `authentication { }`, and lists the action
for anyone signed in:

```jadpo
action add_tagged_memo(input: TaggedMemo, caller: Principal.user) -> MemoView {
    return MemoView {
        text: input.text
        author_id: caller.id
    }
}
```

```jadpo
    POST / {
        input: TaggedMemo
        output: MemoView
        run: add_tagged_memo(input, current_principal.user)
    }
```


Sign-in reads the `Authorization` header and checks the token. A request
without a valid one gets 401 before `add_tagged_memo` runs, and the
action gets the caller sign-in found.

## Related

- [Headers](/docs/one-input.md#headers)
- [Ways to sign in](/docs/ways-to-sign-in.md)
- [Who is calling: `current_principal`](/docs/who-is-calling-current-principal.md)
- [Idempotent writes](/docs/idempotent-writes.md)
