---
title: Step-up authentication
description: A fresh re-authentication at the moment of an irreversible operation, regardless of session age.
label: Step-up
---

Holding a permission is not always enough. Thirteen permissions additionally
require the session to have **re-authenticated recently** — within
`STEP_UP_TTL_SECONDS`, 300 seconds by default.

:::security The specific thing this prevents
An unattended, still-valid browser session being used to revoke a production
agent, activate a policy, or execute an emergency control.

A twelve-hour session is convenient for reading dashboards and is not an
appropriate credential for an irreversible action. Step-up separates the two
without making people log in constantly.
:::

## Which permissions

| Permission | Why |
| --- | --- |
| `agent:revoke` | Terminal. No un-revoke |
| `agent:decommission` | Terminal |
| `credential:revoke` | Terminal for that credential |
| `credential:rotate` | Invalidates every outstanding lease |
| `policy:activate` | Moves the authorization boundary for every agent in scope |
| `policy:rollback` | Same |
| `security:emergency_action` | Large blast radius by definition |
| `security:break_glass` | Emergency elevated access |
| `sso:update` | Changes how everyone authenticates |
| `organization:delete` | Terminal, for everything |
| `audit:configure_retention` | Shortening it destroys evidence |
| `environment:pause` | Stops all protected execution |

The pattern: **irreversible, or large blast radius, or changes who can do what**.

Note that `policy:activate` is on this list. Activating a policy version changes
what every agent in scope is permitted to do, which puts it in the same category
as revoking an identity.

## How it works

```http
POST /v1/auth/step-up
content-type: application/json

{ "password": "…" }
```

or with an MFA code:

```json
{ "code": "123456" }
```

On success the session is marked as stepped-up for `STEP_UP_TTL_SECONDS`. During
that window, step-up-gated operations proceed. After it, they require another
step-up.

Attempting one without a current step-up returns `403 STEP_UP_REQUIRED`. The
console handles this by prompting; a script should expect it.

## In the CLI

```bash
veefore agents revoke agt_01ABC --reason "Key suspected compromised"
```

The CLI prompts for step-up as part of the flow. In CI, supply
`VEEFORE_PASSWORD` and `VEEFORE_MFA_CODE` as environment variables.

:::warning `--yes` never skips step-up
`--yes` assumes yes for confirmation prompts. It does not bypass step-up
authentication, and there is no flag that does.

If `--yes` could skip step-up, step-up would be a UI inconvenience rather than
a control.
:::

## API keys cannot step up

:::security This is the most consequential consequence
Step-up requires a human re-authenticating. An API key has no human, so a key
holding `policy:activate` **still cannot activate a policy**.

Which means CI cannot activate a policy, revoke an agent, rotate a credential or
execute an emergency control on its own — regardless of the role you give it.
:::

That is deliberate rather than an oversight. Activating a policy moves the
authorization boundary for every agent in scope; it is a decision, not a build
step.

The workable shape:

```yaml
# CI proves the policy is correct and nothing regressed.
- run: veefore test
- run: veefore doctor --strict

# A human activates, with step-up, from their own session.
#   veefore policies deploy pol_01ABC --reason "CHG-4411"
```

[→ Gate your pipeline](/build/ci-gate)

## Step-up on approvals

An approval rule can require the approver to step up at the moment of
approving:

```json
{
  "then": {
    "decision": "REQUIRE_APPROVAL",
    "reason": "Destructive database operations require a human decision.",
    "approval": {
      "routingMode": "SINGLE",
      "requiredApprovals": 1,
      "requireStepUp": true,
      "ttlSeconds": 900
    }
  }
}
```

Use it for anything irreversible. It means an unattended browser session is not
enough to approve a destructive action — the same reasoning as the permission
list, applied to the approval itself.

When several rules both require approval, `requireStepUp` is true if **any** of
them asked for it. Effects merge conservatively.

## Tuning the window

```ini
STEP_UP_TTL_SECONDS=300
```

:::danger Do not raise this for convenience
Five minutes is already generous. The whole point is that the step-up is
contemporaneous with the action, and a long window reintroduces exactly the
problem step-up exists to solve.

If people are stepping up repeatedly and finding it painful, the signal is that
they are performing a lot of irreversible operations — which is worth looking
at on its own terms.
:::

## It is recorded

Every step-up is an audit event, and so is every step-up-gated operation. So the
trail shows both that the operation happened and that a human re-authenticated
to do it.

That pairing is what makes the audit record meaningful: "the policy was
activated" is weaker evidence than "a named person re-authenticated at 14:02 and
activated the policy at 14:03".

## Related

:::cards

:::card RBAC | /secure/rbac
The permission model and the high-impact list.
:::

:::card Emergency controls | /operate/emergency-controls
The operations that need step-up most.
:::

:::card Approvals | /concepts/approvals
`requireStepUp` on an approval rule.
:::

:::card Permissions | /reference/permissions
The authoritative list. Generated.
:::

:::
