---
url: "https://vueda.dev/v3/reference/api/py/vueda.core.decorators.md"
title: "core.decorators"
---

# core.decorators

## Overview

ViewSet action decorators for dry-run support, confirmation gating, and reauthentication.

## `action` {#action}

Drop-in replacement for DRF's ``@action`` that adds ``bulk`` and ``confirm`` modes.

``bulk=True`` marks the action as a bulk action, which ``VuedaRouter`` also routes at the list
URL (no pk) in addition to the detail URL.

``confirm=True`` declares an always-on consequence warning: every mutating request (method not
in ``SAFE_METHODS``) is gated through ``vueda.core.exceptions.gate_warnings`` before the body
runs, with ``{"non_field_errors": [message]}`` as the warning set. The message comes from a
``confirm_message`` attribute set on the decorated action (``my_action.confirm_message = "..."``
after the definition) and falls back to ``DEFAULT_CONFIRM_MESSAGE``. The unacknowledged first
submit therefore always returns a 409 without executing the body; resubmitting with the digest
in the ``Acknowledge-Warnings`` header runs it. The gate raises regardless of dry-run.

Because ``confirm=True`` gates before the body, it suits input-less consequence actions: any
body-level validation 400 would only surface after the 409 is acknowledged. Actions with input
should instead call ``gate_warnings(request, ...)`` explicitly after
``serializer.is_valid(raise_exception=True)`` so 400s precede the 409.

### Signature {#action-signature}

`action(methods, detail, bulk, confirm, url_path, url_name, kwargs)`

#### Parameters {#action-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="action-param-methods">methods</span> |  | no | `None` |  |
| <span id="action-param-detail">detail</span> |  | no | `None` |  |
| <span id="action-param-bulk">bulk</span> |  | no | `False` |  |
| <span id="action-param-confirm">confirm</span> |  | no | `False` |  |
| <span id="action-param-url_path">url_path</span> |  | no | `None` |  |
| <span id="action-param-url_name">url_name</span> |  | no | `None` |  |
| <span id="action-param-kwargs">kwargs</span> |  | no |  |  |

### Source {#action-source}

`server/vueda/core/decorators.py:30`

## `recent_auth_required` {#recent_auth_required}

Wrap a viewset action so it returns 401 with ``Reauthentication required`` unless the user recently
completed the reauthentication flow their account requires.

``vueda.core.reauthentication.did_recently_authenticate`` decides: a user with an MFA authenticator
must have confirmed a second factor within ``ACCOUNT_REAUTHENTICATION_TIMEOUT``, and a password-only
user must have confirmed their password. ``WhoIsSerializer`` reports the flow this check would ask for
as ``auth_pending_flow``, so the client can send the user to reauthenticate before it calls a guarded
action. The TOTP viewset applies this decorator to device setup, activation, and deletion.

### Signature {#recent_auth_required-signature}

`recent_auth_required(func)`

#### Parameters {#recent_auth_required-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="recent_auth_required-param-func">func</span> |  | yes |  |  |

### Source {#recent_auth_required-source}

`server/vueda/core/decorators.py:83`

## `DEFAULT_CONFIRM_MESSAGE` {#DEFAULT_CONFIRM_MESSAGE}

Warning text used by ``@action(confirm=True)`` when the action sets no ``confirm_message``.

## Source

`server/vueda/core/decorators.py`
