HeyPuter/puter · error · HttpError

oidc_revalidation_required

oidc_revalidation_required

Error message

OIDC revalidation required

What it means

Raised when an OIDC-only account (password is null — SSO-managed) submitted a `password` field in the request body. Password verification only applies to password accounts; OIDC accounts must revalidate identity through their OIDC provider. The error carries a `fields.revalidate_url` the GUI should open as a popup to re-run the OIDC flow.

Source

Thrown at src/backend/core/http/middleware/userProtected.ts:221

        const isTemp = user.password === null && user.email === null;
        if (isTemp) {
            if (allowTemp) return next();
            throw new HttpError(403, 'Temporary account', {
                legacyCode: 'temporary_account',
            });
        }

        const bodyPassword =
            typeof req.body?.password === 'string' ? req.body.password : null;
        if (bodyPassword) {
            if (user.password === null) {
                const fields = await buildRevalidateFields(
                    config,
                    oidcService,
                    user,
                );
                throw new HttpError(403, 'OIDC revalidation required', {
                    legacyCode: 'oidc_revalidation_required',
                    fields,
                });
            }
            let match = false;
            try {
                match = await bcrypt.compare(
                    bodyPassword,
                    String(user.password),
                );
            } catch {
                match = false;
            }
            if (!match)
                throw new HttpError(400, 'Password mismatch', {
                    legacyCode: 'password_mismatch',
                });
            return next();

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Open `fields.revalidate_url` in a popup to revalidate via the OIDC provider.
  2. Do not send a password for OIDC-only accounts.
  3. Branch the GUI on account type (OIDC vs password) before submitting.

Example fix

// before
fetch('/user', { method:'DELETE', body: JSON.stringify({ password }) });
// after (OIDC account) — surface fields.revalidate_url
if (err.code === 'oidc_revalidation_required') {
  window.open(err.fields.revalidate_url, 'oidc', 'popup');
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Don't send a password for OIDC-only accounts:
if (user.password == null) { delete body.password; }

Type guard

const isOidcOnlyAccount = (u) => !!(u && u.password == null);

Try / catch

try { await call(body); }
catch (e) {
  if (e.code === 'oidc_revalidation_required' && e.fields?.revalidate_url) {
    window.open(e.fields.revalidate_url, 'oidc', 'popup'); return;
  }
  throw e;
}

Prevention

When it happens

Trigger: An SSO/OIDC-linked user posts a password on a userProtected route (e.g. the UI sent a password field by default without checking account type).

Common situations: GUI that always prompts for a password regardless of account type; an OIDC user trying their old pre-SSO password; a generic delete-account form not branched for SSO.

Related errors


AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12). Data as JSON: /api/errors/c9b7d0b4047e370a. Report an issue: GitHub.