Sudo mode and step-up authentication: asking again before the dangerous things

A session says who signed in. It does not say who is at the keyboard now. Here is how Nulvless closes that gap without signing anyone out.

Gleisson MatosFounder & CEO3 min read
On this page

A session is a statement about the past: at some point, this person proved who they were on this device. It says nothing about the present. The laptop could be unlocked on a desk, the token could have been copied, or a family member could have picked up the phone.

For most of what a user does, that is fine. For a short list of actions it is not, because those actions are exactly what an attacker needs to keep an account: turning off a second factor, adding their own email, changing the password, revoking the real owner's sessions.

Sudo mode is how Nulvless handles that list.

A short window, re-proven

Sudo mode is a short window in which the person has just proven, again, that they are the one at the keyboard. It does not sign them out and it does not replace the session. It adds a fresh proof on top of it, with an expiry.

Which actions need it is configuration, set per environment:

GroupWhat it protects
manage_mfaEnrolling or removing factors, reading recovery codes
manage_credentialsCredential changes
manage_grantsInviting, revoking, blocking, changing permissions
manage_identitiesAdding or removing an email or phone
manage_sessionsRevoking sessions
manage_passwordsChanging the password

A master switch turns sudo mode on, and a timeout sets how long an elevation lasts.

Do not hard-code which calls need it

Because it is configuration, an environment can protect another group next month. Code that predicts which calls need sudo breaks the day that happens. Instead, the SDK tells you when a call was rejected for this reason:

nulvless.auth.on('sudo-required', async () => {
  await nulvless.auth.sessions.sudo();
  openSudoPrompt();
});

One handler covers every protected call in the app. On the wire it is a 403 with the code protected_action_denied, if you handle errors directly.

Two ways to satisfy it

Both start the same way: sessions.sudo() sends a code and returns a challenge. What changes is what the code is spent on.

Elevate the session when the user is about to do several sensitive things in a row, like working through a security settings screen:

await nulvless.auth.sessions.sudo();
const { data } = await nulvless.auth.sessions.sudoVerify({ code });

The session is re-issued with sudo active, and every protected call passes until the window closes.

Authorize one call when there is a single destructive action:

await nulvless.auth.sessions.sudo();
await nulvless.auth.grants.revoke({ id, nonce: code });

The code is spent on that request alone. The session is never elevated, so nothing else becomes possible as a side effect. This is the safer default.

The nonce is the code

nonce is not a separate token. It is the code the user typed, checked against the sudo challenge already pending for their session. It is single use and travels as the nlv-sudo-nonce header.

The same guarantee on your own API

Your API probably has actions the platform knows nothing about: deleting a workspace, rotating one of your own API keys. They deserve the same check. The browser gets the code exactly as above and sends it along, and your handler passes it to the protected call:

app.delete('/keys/:id', requireAuth, async (req, res) => {
  const { error } = await nulvless.auth.sessions.revoke({
    id: req.params.id,
    nonce: req.headers['nlv-sudo-nonce'] as string,
  });

  if (error) return res.status(403).json({ error });
  res.status(204).end();
});

Build this with your agent

A ready prompt for Claude Code, Cursor, ChatGPT or any coding agent. It points the agent at this guide and the docs, so it follows the real API instead of guessing.

Protect the sensitive actions in my app with Nulvless sudo mode.

Follow these pages. They document the real API, so do not guess method names:
- https://www.nulvless.com/llms.mdx/blog/sudo-mode/content.md
- https://www.nulvless.com/llms.mdx/docs/guides/sudo/content.md
- https://www.nulvless.com/llms.mdx/docs/sdk/sessions/content.md

Requirements:
- Do not hard-code which calls need sudo. Register one handler for the sudo-required event that calls auth.sessions.sudo() and opens a prompt for the code.
- For a single destructive action, pass the code as nonce on that call instead of elevating the session.
- On my own API, read the nlv-sudo-nonce header and pass it to the protected Nulvless call. Return 403 when it fails.
- Keep authorization checks separate. Sudo proves the person is present, not that they are allowed.
or open in

What sudo is not

Sudo is not a permission check. It answers "is this person still here?", not "are they allowed to do this?". A user with a freshly elevated session still fails an authorization check they do not hold the grant for. The two run independently, and a sensitive action usually wants both.

Nulvless uses sudo mode on itself, too: merging configuration from one environment into another asks for it, and the confirmation lasts an hour so a series of promotions only asks once. You can read about that flow in Why staging users can never reach production, and about every option in the sudo mode guide. To require sudo inside an edge function, see Authenticate users in edge functions.

Keep reading