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
- Pass only relative/local return URLs (starting with ~/ or /) to ExternalChallenge.
- Register the redirect URI in IdentityServer client configuration if it must be absolute.
- 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
- Use only relative return URLs (~/path or /path) in SSO flows.
- Register absolute redirect URIs in IdentityServer client config if needed.
- Never accept returnUrl from untrusted user input without validation.
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
- NoDomainHintProvided
- SsoOrganizationIdMismatch
- AcrMissingOrInvalid
- UserIdAndTokenMismatch
- InvalidSsoToken
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/5cb449a40f77ecb7.
Report an issue: GitHub.