Network Error no oidc-client: como achar a causa real em cinco minutos
O oidc-client diz Network Error, mas a causa costuma ser CORS, certificado, mixed content ou endereço errado. Veja como ler o painel de rede e corrigir no lugar certo.
O SPA em Angular abre, você clica em Entrar e o console mostra Network Error at XMLHttpRequest.s.onerror (oidc-client.min.js). Alguém sugere atualizar a biblioteca, outro sugere trocar de navegador. Duas horas depois, o erro continua igual. A mensagem é genérica porque o navegador esconde do JavaScript o motivo da falha. O motivo está escrito, com todas as letras, na aba Network e no console, uma linha acima.
A resposta curta: Network Error no oidc-client significa que uma requisição XHR da biblioteca para o provedor de identidade terminou sem resposta legível, com status 0. As causas mais comuns são CORS não liberado para a origem do SPA, certificado HTTPS não confiável no provedor, página HTTPS chamando endereço HTTP e authority apontando para o lugar errado. Abra a requisição vermelha, leia o motivo e corrija na configuração do provedor, não na biblioteca.
Por que o erro chega tão vago
A biblioteca faz várias chamadas XHR por conta própria: baixa o documento de descoberta em /.well-known/openid-configuration, as chaves públicas do endpoint jwks, troca o código pelo token no endpoint de token e, se configurado, consulta o userinfo. Quando qualquer uma falha antes de haver uma resposta que o JavaScript possa ler, o navegador dispara onerror com status 0 e nenhum detalhe. Isso é proposital: se uma página pudesse saber por que a chamada a outro domínio falhou, ela poderia sondar redes internas.
Então a biblioteca não tem como dizer mais do que Network Error. Quem sabe o motivo é o navegador, e ele mostra isso nas ferramentas de desenvolvedor, não no objeto de erro. Atualizar a biblioteca não muda essa regra, e é por isso que raramente resolve.
Como ler o painel de rede, passo a passo
O cenário de exemplo: SPA Angular em http://localhost:4200, provedor IdentityServer em https://localhost:5001, API em https://localhost:5002.
- Abra as ferramentas de desenvolvedor, aba Network, marque Preserve log e reproduza o login.
- Filtre por Fetch/XHR e encontre a requisição em vermelho.
- Anote o domínio e o caminho dela. É o documento de descoberta, o jwks, o token ou o userinfo?
- Veja a coluna Status. Valores como
(failed),CORS errorou um códigonet::ERR_...já dizem a família do problema. - Vá ao console e leia a mensagem completa que o navegador escreveu junto da falha.
- Reproduza a mesma chamada fora do navegador com
curlpara separar problema de rede de problema de política do navegador.
As quatro mensagens que resolvem quase tudo
CORS. O console mostra algo como:
Access to XMLHttpRequest at 'https://localhost:5001/.well-known/openid-configuration'
from origin 'http://localhost:4200' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
O provedor respondeu, mas não autorizou a origem do SPA a ler a resposta. Aqui o CORS é de fato o problema, porque o oidc-client chama o provedor por XHR. No IdentityServer, a origem é liberada por cliente.
Certificado. A requisição aparece com net::ERR_CERT_AUTHORITY_INVALID. O provedor em desenvolvimento usa um certificado em que o navegador não confia. Ao abrir a URL numa aba, você veria o aviso de conexão não segura; via XHR, não há aviso, só falha.
Mixed content. O console diz que a página foi carregada por HTTPS e tentou um XMLHttpRequest para um endereço HTTP, e que a requisição foi bloqueada. Acontece quando o SPA já está em HTTPS e o authority ou algum endpoint do documento de descoberta ainda aponta para HTTP, comum quando o provedor roda atrás de proxy e não sabe que o acesso externo é HTTPS.
Endereço errado ou serviço fora. net::ERR_CONNECTION_REFUSED ou net::ERR_NAME_NOT_RESOLVED. O authority tem porta errada, o provedor não subiu ou o nome não resolve na máquina de quem testa.
Como corrigir cada causa no lugar certo
Liberar a origem no IdentityServer
new Client
{
ClientId = "angular-spa",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
RedirectUris = { "http://localhost:4200/auth-callback" },
PostLogoutRedirectUris = { "http://localhost:4200/" },
AllowedCorsOrigins = { "http://localhost:4200" },
AllowedScopes = { "openid", "profile", "api1" }
}
A origem é esquema, host e porta, sem barra no final e sem caminho. http://localhost:4200/ com barra não casa com a origem que o navegador envia. Em produção, entra a origem pública do SPA, e só ela. Liberar qualquer origem para fazer o erro sumir troca um problema visível por um invisível; o post CORS não é a trava do seu servidor explica o que o CORS protege e o que não protege.
Conferir de fora do navegador
curl -i -H "Origin: http://localhost:4200" https://localhost:5001/.well-known/openid-configuration
O curl não aplica CORS, mas mostra os cabeçalhos. Se a resposta vier 200 e sem Access-Control-Allow-Origin: http://localhost:4200, a configuração do cliente não foi aplicada ou a origem está escrita diferente. Se o curl reclamar do certificado, o problema é o certificado, não o CORS. Não use a opção que desliga a verificação do curl como solução; ela só esconde a causa.
Leia também o JSON da descoberta. Todos os endpoints listados ali (token_endpoint, jwks_uri, userinfo_endpoint) precisam estar no mesmo esquema e host que o navegador alcança. Se aparecer http:// ou um nome interno de contêiner, o provedor está montando URLs a partir do que ele enxerga atrás do proxy, e é preciso configurar o encaminhamento dos cabeçalhos do proxy.
Certificado de desenvolvimento confiável
Com ASP.NET Core em Windows ou macOS, dotnet dev-certs https --trust instala e confia no certificado de desenvolvimento. Feche e reabra o navegador depois. Em produção, use um certificado de autoridade pública, como os do Let's Encrypt. Nunca desligue a verificação de certificado no cliente para avançar; o post certificado autoassinado serve no laboratório mostra onde ele cabe.
Quando atualizar a biblioteca faz sentido
O pacote oidc-client original não é mais mantido, e a comunidade continuou o trabalho no oidc-client-ts. Migrar é uma boa ideia, mas como tarefa separada, com testes, depois de entender o erro atual. Trocar a biblioteca para tentar sumir com um bloqueio de CORS só adiciona uma segunda variável ao problema.
Erros comuns
- Atualizar a biblioteca como primeiro passo. O bloqueio é do navegador e continua igual na versão nova, agora com mudanças de API para depurar junto.
- Liberar CORS na API quando o bloqueio é no provedor. A configuração vai para o projeto errado e o erro não muda, porque a requisição que falha é para outro domínio.
- Escrever a origem com barra ou caminho. O valor nunca casa com o cabeçalho
Origine a liberação parece não funcionar. - Desligar a verificação de certificado. O erro some em desenvolvimento e a mesma configuração vaza para produção, aceitando qualquer servidor.
- Ignorar o documento de descoberta. Endpoints com HTTP ou nome interno causam mixed content ou falha de resolução que parecem aleatórios.
- Colar o token no ticket enquanto depura. A credencial passa a circular em ferramentas que não deveriam tê-la.
Checklist
- Você sabe qual requisição falhou: descoberta, jwks, token ou userinfo.
- Você leu a mensagem completa do console ao lado da falha.
- A origem exata do SPA está liberada no cliente do provedor, sem barra final.
- O
curlcom cabeçalhoOriginmostra oAccess-Control-Allow-Originesperado. - O certificado do provedor é confiável no navegador de quem testa.
- Todos os endpoints do documento de descoberta usam HTTPS e o host público.
- Nenhum token aparece em log, print ou ticket do diagnóstico.
Abra agora a aba Network e reproduza o erro: para qual domínio vai a requisição vermelha, e qual motivo o navegador escreveu no console?