n8n-io/n8n · error · ForbiddenError

Login is handled by ${currentAuthenticationMethod}. Please c

Error message

Login is handled by ${currentAuthenticationMethod}. Please contact your Identity Provider to reset your password.

What it means

POST /rest/forgot-password is refused when SAML or OIDC is the current authentication method AND the user lacks the `user:resetPassword` global scope AND has not opted into `settings.allowSSOManualLogin`. Password resets are delegated to the identity provider in SSO mode; n8n will not issue a local reset token.

Source

Thrown at packages/cli/src/controllers/password-reset.controller.ts:109

				return;
			}

			if (user.role.slug !== GLOBAL_OWNER_ROLE.slug && !this.license.isWithinUsersLimit()) {
				this.logger.debug(
					'Request to send password reset email failed because the user limit was reached',
				);
				throw new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
			}

			if (
				(isSamlCurrentAuthenticationMethod() || isOidcCurrentAuthenticationMethod()) &&
				!(hasGlobalScope(user, 'user:resetPassword') || user.settings?.allowSSOManualLogin === true)
			) {
				const currentAuthenticationMethod = isSamlCurrentAuthenticationMethod() ? 'SAML' : 'OIDC';
				this.logger.debug(
					`Request to send password reset email failed because login is handled by ${currentAuthenticationMethod}`,
				);
				throw new ForbiddenError(
					`Login is handled by ${currentAuthenticationMethod}. Please contact your Identity Provider to reset your password.`,
				);
			}

			const ldapIdentity = user.authIdentities?.find((i) => i.providerType === 'ldap');
			if (!user.password || (ldapIdentity && user.disabled)) {
				this.logger.debug(
					'Request to send password reset email failed because no user was found for the provided email',
					{ invalidEmail: email },
				);
				return;
			}

			if (this.license.isLdapEnabled() && ldapIdentity) {
				throw new UnprocessableRequestError('forgotPassword.ldapUserPasswordResetUnavailable');
			}

			const url = this.authService.generatePasswordResetUrl(user);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Direct the user to the identity provider's self-service password reset.
  2. If local reset must stay available, grant the user the `user:resetPassword` global scope or set `allowSSOManualLogin: true` in their settings.
  3. Confirm the intended auth method — if SSO was enabled by mistake, switch back via config.
Defensive patterns

Strategy: validation

Validate before calling

// If SSO is the auth method, route to the IdP unless the user has reset scope or manual login.
const ssoActive = settings.authMethod === 'saml' || settings.authMethod === 'oidc';
if (ssoActive && !me.canResetPassword && !me.allowSSOManualLogin) {
  redirectToIdpReset();
  return;
}
await restApi.post('/forgot-password', { email });

Try / catch

try {
  await restApi.post('/forgot-password', { email });
} catch (e) {
  if (e.response?.status === 403 && /login is handled by/i.test(e.response.data.message)) {
    showIdpRedirectNotice(e.response.data.message);
  } else throw e;
}

Prevention

When it happens

Trigger: forgotPassword when `isSamlCurrentAuthenticationMethod()` or `isOidcCurrentAuthenticationMethod()` is true, the user has no `user:resetPassword` scope, and `user.settings.allowSSOManualLogin !== true`.

Common situations: SSO migration where users still try the local forgot-password link; an SSO-only user without manual-login allowance; admin hasn't granted the reset scope.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/f17e954924c9cafa. Report an issue: GitHub.