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.
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:
| Group | What it protects |
|---|---|
manage_mfa | Enrolling or removing factors, reading recovery codes |
manage_credentials | Credential changes |
manage_grants | Inviting, revoking, blocking, changing permissions |
manage_identities | Adding or removing an email or phone |
manage_sessions | Revoking sessions |
manage_passwords | Changing 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.
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.

