Como posso suprimir a caixa de diálogo de autenticação do navegador?

85

Meu aplicativo da web tem uma página de login que envia credenciais de autenticação por meio de uma chamada AJAX. Se o usuário inserir o nome de usuário e a senha corretos, está tudo bem, mas se não, acontece o seguinte:

  1. O servidor da web determina que, embora a solicitação inclua um cabeçalho de autorização bem formado, as credenciais no cabeçalho não são autenticadas com êxito.
  2. O servidor da web retorna um código de status 401 e inclui um ou mais cabeçalhos WWW-Authenticate listando os tipos de autenticação suportados.
  3. O navegador detecta que a resposta à minha chamada no objeto XMLHttpRequest é um 401 e a resposta inclui cabeçalhos WWW-Authenticate. Em seguida, ele exibe uma caixa de diálogo de autenticação pedindo, novamente, o nome de usuário e a senha.

Isso está tudo bem até a etapa 3. Não quero que a caixa de diálogo seja exibida, quero lidar com a resposta 401 em minha função de retorno de chamada AJAX. (Por exemplo, exibindo uma mensagem de erro na página de login.) Quero que o usuário insira novamente seu nome de usuário e senha, é claro, mas quero que eles vejam meu formulário de login amigável e reconfortante, não o padrão feio do navegador caixa de diálogo de autenticação.

A propósito, não tenho controle sobre o servidor, portanto, fazer com que ele retorne um código de status personalizado (ou seja, algo diferente de 401) não é uma opção.

Existe alguma maneira de suprimir a caixa de diálogo de autenticação? Em particular, posso suprimir a caixa de diálogo Autenticação necessária no Firefox 2 ou posterior? Existe alguma maneira de suprimir a caixa de diálogo Conectar a [host] no IE 6 e posterior?


Editar
Informações adicionais do autor (18 de setembro):
Devo acrescentar que o verdadeiro problema com a caixa de diálogo de autenticação do navegador que aparece é que ela fornece informações insuficientes ao usuário.

O usuário acabou de inserir um nome de usuário e senha através do formulário na página de login, ele acredita que digitou ambos corretamente e clicou no botão enviar ou apertou enter. Sua expectativa é que ele seja levado para a próxima página ou talvez seja informado de que inseriu suas informações incorretamente e deve tentar novamente. No entanto, ele é apresentado a uma caixa de diálogo inesperada.

O diálogo não faz reconhecimento do fato de que ele apenas fez entrar um nome de usuário e senha. Não indica claramente que houve um problema e que ele deve tentar novamente. Em vez disso, a caixa de diálogo apresenta ao usuário informações criptografadas como "O site diz: ' [reino] '." Onde [realm] é um nome curto de domínio que apenas um programador poderia amar.

Os designers do navegador da Web, tome nota: ninguém perguntaria como suprimir a caixa de diálogo de autenticação se a caixa de diálogo em si fosse simplesmente mais amigável. O toda razão que eu estou fazendo um formulário de login é que nossa equipe de gerenciamento de produto considera justamente diálogos de autenticação dos navegadores para ser terrível.

dgvid
fonte
1
A resposta é que não existe uma boa resposta. Os hacks sugeridos por Marijn são o mais próximo possível. É claro que usar uma autenticação personalizada entendida pelo servidor e seu JavaScript, mas não pelo navegador, também resolverá o problema, se possível.
dgvid
Eu estive no mesmo problema e encontrei este link em um comentário aqui no stackoverflow (não no meu blog): loudvchar.blogspot.ca/2010/11/… Espero que ajude você.
gies0r

Respostas:

17

Eu não acho que isso seja possível - se você usar a implementação de cliente HTTP do navegador, essa caixa de diálogo sempre aparecerá. Dois hacks vêm à mente:

  1. Talvez o Flash lide com isso de forma diferente (eu não tentei ainda), então ter um filme em flash para fazer o pedido pode ajudar.

  2. Você pode configurar um 'proxy' para o serviço que está acessando em seu próprio servidor e fazer com que ele modifique um pouco os cabeçalhos de autenticação, para que o navegador não os reconheça.

Marijn
fonte
"Impossível" parece ser a resposta correta, embora eu suspeite que o hack "proxie" resolverá o problema.
dgvid
@Stobor A resposta aceita para a duplicata que você postou, na verdade, leva de volta a esta pergunta como sua resposta!
8bitjunkie
3
@ 7SpecialGems Boa pegada. Eu não estava sugerindo que fosse um idiota, eu vinculei a resposta específica (WWW-Authenticate) que foi postada 4 anos após a resposta aceita. Embora, em retrospecto, não me lembre por que estava olhando para isso, ou como o testei.
Stobor
1
Tento detectar o erro 401 não autorizado por este código: $ .ajaxSetup ({statusCode: {401: function () {RedirectToLogin ();}}}); Mas o IE sempre mostra a caixa de diálogo de autenticação do navegador. Como posso suprimir essa caixa de diálogo no ASP.Net MVC 2?
PaulP
@PaulP, como já foi dito, você precisa de um proxy que irá retirar as respostas do código de status 401 de seu cabeçalho WWW-Authenticate ou algum tipo de implementação de servidor customizada.
Motes
51

Encontrei o mesmo problema aqui, e o engenheiro de back-end da minha empresa implementou um comportamento que aparentemente é considerado uma boa prática: quando uma chamada para um URL retorna um 401, se o cliente definiu o cabeçalho X-Requested-With: XMLHttpRequest, o servidor descarta o www-authenticatecabeçalho em resposta.

O efeito colateral é que o pop-up de autenticação padrão não aparece.

Certifique-se de que sua chamada de API tenha o X-Requested-Withcabeçalho definido como XMLHttpRequest. Nesse caso, não há nada a fazer exceto mudar o comportamento do servidor de acordo com esta boa prática ...

Antoine Banctel-Chevrel
fonte
3
Se o back-end for baseado em Java / Spring, o DelegatingAuthenticationEntryPointtratará desse comportamento para você.
Derek Slife
Obrigado por isso. Muitas bibliotecas adotaram esse comportamento, incluindo Devise
josephnvu
1
alguma ideia de como fazer isso no nginx? "quando uma chamada para um URL retorna um 401, se o cliente configurou o cabeçalho X-Requested-With: XMLHttpRequest, o servidor descarta o cabeçalho www-authenticate em sua resposta."
markmnl
18

O navegador exibe um prompt de login quando as duas condições a seguir são atendidas:

  1. O status HTTP é 4xx
  2. WWW-Authenticate cabeçalho está presente na resposta

Se você puder controlar a resposta HTTP, poderá remover o WWW-Authenticatecabeçalho da resposta e o navegador não abrirá a caixa de diálogo de login.

Se você não pode controlar a resposta, você pode configurar um proxy para filtrar o WWW-Authenticatecabeçalho da resposta.

Pelo que eu sei (sinta-se à vontade para me corrigir se eu estiver errado), não há como evitar o prompt de login depois que o navegador recebe o WWW-Authenticatecabeçalho.

enferrujado
fonte
Boa informação. Com relação aos WWW-Authenticatevalores de cabeçalho válidos , consulte stackoverflow.com/a/1748451/225217
Brice Roncace
6

Eu percebo que essa pergunta e suas respostas são muito antigas. Mas, acabei aqui. Talvez outros o façam também.

Se você tiver acesso ao código para o serviço da web que está retornando o 401. Basta alterar o serviço para retornar um 403 (Proibido) nesta situação em vez de 401. O navegador não solicitará as credenciais em resposta a um 403. 403 é o código correto para um usuário autenticado que não está autorizado para um recurso específico. Qual parece ser a situação do OP.

Do documento IETF em 403:

Um servidor que recebe credenciais válidas que não são adequadas para obter acesso deve responder com o código de status 403 (Proibido)

Jim Reineri
fonte
4

No Mozilla, você pode conseguir isso com o seguinte script ao criar o objeto XMLHttpRequest:

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

A 2ª linha impede a caixa de diálogo ....

HNygard
fonte
1
Isso parece não fazer nada no Firefox 2. Isso resulta em um erro de segurança DOM, NS_ERROR_DOM_SECURITY_ERR código 1000, no Firefox 3.
dgvid
2

Qual tecnologia de servidor você usa e há um produto específico que você usa para autenticação?

Como o navegador está apenas fazendo seu trabalho, acredito que você tenha que mudar as coisas no lado do servidor para não retornar um código de status 401. Isso pode ser feito usando formulários de autenticação personalizados que simplesmente retornam o formulário novamente quando a autenticação falha.

jan.vdbergh
fonte
2

No Mozilla Land, definir o parâmetro mozBackgroundRequest de XMLHttpRequest ( docs ) como true suprime esses diálogos e faz com que as solicitações simplesmente falhem. No entanto, não sei quão bom é o suporte para vários navegadores (incluindo se a qualidade das informações de erro nessas solicitações com falha é muito boa em todos os navegadores).

rakslice
fonte
O prefixo moz- implica em nenhum suporte cross-browser (a menos que você possa encontrar parâmetros semelhantes que funcionam em cada um dos outros navegadores).
Brilliand
2

jan.vdbergh tem a verdade, se você puder alterar o 401 no lado do servidor por outro código de status, o navegador não pegará e pintará o pop-up. Outra solução poderia ser alterar o cabeçalho WWW-Authenticate para outro cabeçalho personalizado. Não acredito porque o navegador diferente não suporta isso, em algumas versões do Firefox podemos fazer a solicitação xhr com mozBackgroundRequest, mas nos outros navegadores ?? aqui, há um link interessante com esse problema no Chromium.

Kalamarico
fonte
1

Eu tenho esse mesmo problema com MVC 5 e VPN, onde sempre que estamos fora da DMZ usando a VPN, temos que responder a esta mensagem do navegador. Usando .net, eu simplesmente lido com o roteamento do erro usando

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

até agora funcionou porque a ação Index no controlador home valida o usuário. A visualização nesta ação, se o logon não for bem-sucedido, tem controles de login que eu uso para fazer o login do usuário usando a consulta LDAP passada nos serviços de diretório:

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

Embora tenha funcionado bem até agora, devo informar que ainda estou testando e o código acima não teve nenhum motivo para ser executado, por isso está sujeito a remoção ... o teste atualmente inclui tentar descobrir um caso em que o segundo conjunto de código é mais útil. Novamente, este é um trabalho em andamento, mas como pode ser de alguma ajuda ou estimular seu cérebro para algumas ideias, decidi adicioná-lo agora ... Vou atualizá-lo com os resultados finais quando todos os testes forem concluídos.

Clarence
fonte
1

Estou usando o Node, Express e Passport e estava lutando com o mesmo problema. Comecei a trabalhar definindo explicitamente o www-authenticatecabeçalho como uma string vazia. No meu caso, era assim:

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

Espero que ajude alguém!

John Knotts
fonte
0

Para aqueles C # unsing, aqui está ActionAttributeque retorna em 400vez de 401e 'engole' a caixa de diálogo de autenticação básica.

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

use como a seguir:

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

Espero que isso economize algum tempo.

Matas Vaitkevicius
fonte