如何禁止显示浏览器的身份验证对话框?

我的 Web 应用程序有一个登录页面,该页面通过 AJAX 调用提交身份验证凭据。如果用户输入了正确的用户名和密码,那么一切都没问题,但如果没有,则会发生以下情况:

  1. Web 服务器确定,尽管请求包含格式良好的 Authorization 头,但头中的凭据不能成功地进行身份验证。
  2. Web 服务器返回一个401状态码,并包括一个或多个列出支持的身份验证类型的 WWW-Authenticate 头。
  3. 浏览器检测到对 XMLHttpRequest 对象的调用的响应是401,并且响应包括 WWW-Authenticate 头。然后它会弹出一个身份验证对话框,再次询问用户名和密码。

在第三步之前,一切都很好。我不想弹出对话框,我想在我的 AJAX 回调函数中处理401响应。(例如,在登录页面上显示错误消息。)当然,我希望用户重新输入他们的用户名和密码,但我希望他们看到的是我友好、可靠的登录表单,而不是浏览器丑陋的默认认证对话框。

顺便说一句,我对服务器没有控制权,因此让它返回一个自定义状态代码(即,除了401以外的其他代码)是不可取的。

有什么办法可以禁止身份验证对话框?特别是,我是否可以在 Firefox2或更高版本中禁止显示“身份验证必需”对话框?有没有办法在 IE6或更高版本中取消连接到 [主持人]对话框?


剪辑
来自作者的补充信息(9月18日) :
我应该补充的是,浏览器的身份验证对话框弹出的真正问题是,它给用户提供的信息不足。

用户刚刚通过登录页面上的表单输入了用户名和密码,他相信他已经正确地输入了这两个名字,并且他已经单击了提交按钮或者点击回车。他的期望是,他将被带到下一页,或者可能被告知,他输入的信息是错误的,应该再试一次。但是,他却看到了一个意想不到的对话框。

这个对话框没有确认他只是 是的输入用户名和密码的事实。它没有明确说明出现了问题,他应该再试一次。相反,对话框会向用户显示诸如“站点说: ‘ [王国]’”之类的隐晦信息其中 [王国]是一个只有程序员才会喜欢的简短域名。

Web 浏览器设计者需要注意: 如果对话框本身更加用户友好,那么没有人会问如何取消身份验证对话框。我使用登录表单的 完整的原因是我们的产品管理团队认为浏览器的身份验证对话框非常糟糕。

65628 次浏览

What server technology do you use and is there a particular product you use for authentication?

Since the browser is only doing its job, I believe you have to change things on the server side to not return a 401 status code. This could be done using custom authentication forms that simply return the form again when the authentication fails.

I don't think this is possible -- if you use the browser's HTTP client implementation, it will always pop up that dialog. Two hacks come to mind:

  1. Maybe Flash handles this differently (I haven't tried yet), so having a flash movie make the request might help.

  2. You can set up a 'proxie' for the service that you're accessing on your own server, and have it modify the authentication headers a bit, so that the browser doesn't recognise them.

In Mozilla you can achieve it with the following script when you create the XMLHttpRequest object:

xmlHttp=new XMLHttpRequest();
xmlHttp.mozBackgroundRequest = true;
xmlHttp.open("GET",URL,true,USERNAME,PASSWORD);
xmlHttp.send(null);

The 2nd line prevents the dialog box....

In Mozilla land, setting the mozBackgroundRequest parameter of XMLHttpRequest (docs) to true suppresses those dialogs and causes the requests to simply fail. However, I don't know how good cross-browser support is (including whether the the quality of the error info on those failed requests is very good across browsers.)

jan.vdbergh has the truth, if you can change the 401 on server side for another status code, the browser won't catch and paint the pop-up. Another solution could be change the WWW-Authenticate header for another custom header. I dont't believe why the different browser can't support it, in a few versions of Firefox we can do the xhr request with mozBackgroundRequest, but in the other browsers?? here, there is an interesting link with this issue in Chromium.

I encountered the same issue here, and the backend engineer at my company implemented a behavior that is apparently considered a good practice : when a call to a URL returns a 401, if the client has set the header X-Requested-With: XMLHttpRequest, the server drops the www-authenticate header in its response.

The side effect is that the default authentication popup does not appear.

Make sure that your API call has the X-Requested-With header set to XMLHttpRequest. If so there is nothing to do except changing the server behavior according to this good practice...

The browser pops up a login prompt when both of the following conditions are met:

  1. HTTP status is 401
  2. WWW-Authenticate header is present in the response

If you can control the HTTP response, then you can remove the WWW-Authenticate header from the response, and the browser won't popup the login dialog.

If you can't control the response, you can setup a proxy to filter out the WWW-Authenticate header from the response.

As far as I know (feel free to correct me if I'm wrong), there is no way to prevent the login prompt once the browser receives the WWW-Authenticate header.

I have this same issue with MVC 5 and VPN where whenever we are outside the DMZ using the VPN, we find ourselves having to answer this browser message. Using .net I simply handle the routing of the error using

<customErrors defaultRedirect="~/Error"  >
<error statusCode="401" redirect="~/Index"/>
</customErrors>

thus far it has worked because the Index action under the home controller validates the user. The view in this action, if logon is unsuccessful, has login controls that I use to log the user in using using LDAP query passed into Directory Services:

      DirectoryEntry entry = new DirectoryEntry("LDAP://OurDomain");
DirectorySearcher Dsearch = new DirectorySearcher(entry);
Dsearch.Filter = "(SAMAccountName=" + UserID + ")";
Dsearch.PropertiesToLoad.Add("cn");

While this has worked fine thus far, and I must let you know that I am still testing it and the above code has had no reason to run so it's subject to removal... testing currently includes trying to discover a case where the second set of code is of any more use. Again, this is a work in progress, but since it could be of some assistance or jog your brain for some ideas, I decided to add it now... I will update it with the final results once all testing is done.

For those unsing C# here's ActionAttribute that returns 400 instead of 401, and 'swallows' Basic auth dialog.

public class NoBasicAuthDialogAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new HttpStatusCodeResult(400);
}
}

use like following:

[NoBasicAuthDialogAuthorize(Roles = "A-Team")]
public ActionResult CarType()
{
// your code goes here
}

Hope this saves you some time.

I realize that this question and its answers are very old. But, I ended up here. Perhaps others will as well.

If you have access to the code for the web service that is returning the 401. Simply change the service to return a 403 (Forbidden) in this situation instead 401. The browser will not prompt for credentials in response to a 403. 403 is the correct code for an authenticated user that is not authorized for a specific resource. Which seems to be the situation of the OP.

From the IETF document on 403:

A server that receives valid credentials that are not adequate to gain access ought to respond with the 403 (Forbidden) status code

I'm using Node, Express & Passport and was struggling with the same issue. I got it to work by explicitly setting the www-authenticate header to an empty string. In my case, it looked like this:

(err, req, res, next) => {
if (err) {
res._headers['www-authenticate'] = ''
return res.json(err)
}
}

I hope that helps someone!

I recently encountered the similar situation while developing a web app for Samsung Tizen Smart TV. It was required to scan the complete local network but few IP addresses were returning "401 Unauthorized" response with "www-authenticate" header attached. It was popping up a browser authentication pop requiring user to enter "Username" & "Password" because of "Basic" authentication type (https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).

To get rid from this, the simple thing which worked for me is setting credentials: 'omit' for Fetc Api Call (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). Official documentation says that:

To instead ensure browsers don’t include credentials in the request, use credentials: 'omit'

fetch('https://example.com', {
credentials: 'omit'
})