bitwarden/server · error · Exception

InvalidReturnUrl

Error message

InvalidReturnUrl

What it means

Thrown in AccountController.ExternalChallenge (line 220) when returnUrl, after whitespace cleaning, is neither a local URL (Url.IsLocalUrl) nor recognized as valid by IdentityServer (_interaction.IsValidReturnUrl). This is an open-redirect and tamper guard.

Source

Thrown at bitwarden_license/src/Sso/Controllers/AccountController.cs:220

            ssoToken
        });
    }

    [HttpGet]
    public IActionResult ExternalChallenge(string scheme, string returnUrl, string state, string userIdentifier, string ssoToken)
    {
        ValidateSchemeAgainstSsoToken(scheme, ssoToken);

        if (string.IsNullOrEmpty(returnUrl))
        {
            returnUrl = "~/";
        }

        // Clean the returnUrl
        returnUrl = CoreHelpers.ReplaceWhiteSpace(returnUrl, string.Empty);
        if (!Url.IsLocalUrl(returnUrl) && !_interaction.IsValidReturnUrl(returnUrl))
        {
            throw new Exception(_i18nService.T("InvalidReturnUrl"));
        }

        var props = new AuthenticationProperties
        {
            RedirectUri = Url.Action(nameof(ExternalCallback)),
            Items =
            {
                // scheme will get serialized into `State` and returned back
                { "scheme", scheme },
                { "return_url", returnUrl },
                { "state", state },
                { "user_identifier", userIdentifier },
            }
        };

        return Challenge(props, scheme);
    }

View on GitHub (pinned to e93b962371)

Solutions

  1. Pass only relative/local return URLs (starting with ~/ or /) to ExternalChallenge.
  2. Register the redirect URI in IdentityServer client configuration if it must be absolute.
  3. Verify the returnUrl originates from the legitimate authorize flow and hasn't been tampered with.

Example fix

// before — absolute external URL fails validation
var returnUrl = "https://external.example.com/callback";
// after — use a local relative path registered with IdentityServer
var returnUrl = "~/sso/callback";
Defensive patterns

Strategy: validation

Validate before calling

returnUrl = CoreHelpers.ReplaceWhiteSpace(returnUrl, string.Empty);
if (!Url.IsLocalUrl(returnUrl) && !_interaction.IsValidReturnUrl(returnUrl))
    return BadRequest("The return URL is not valid.");

Try / catch

try { return ExternalChallenge(scheme, returnUrl, state, userIdentifier, ssoToken); }
catch (Exception ex) when (ex.Message.Contains("InvalidReturnUrl"))
{ /* redirect to home with error toast */ }

Prevention

When it happens

Trigger: ExternalChallenge is invoked with a returnUrl pointing to an external host or a malformed/internal path that IdentityServer does not consider valid.

Common situations: Return URL was manually edited or crafted to point outside the application; client passed an absolute URL instead of a relative path; IdentityServer client configuration doesn't include the redirect URI; returnUrl contains encoded whitespace that survives cleaning.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/5cb449a40f77ecb7. Report an issue: GitHub.