SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-21

Coloque qualquer app atrás de SSO com oauth2-proxy

Veja como usar oauth2-proxy com Nginx ou Traefik para proteger apps sem login via OIDC, com forward auth e correção de cookies e redirecionamentos.

Autenticação delegada: como uma aplicação sem login obtém SSO

O oauth2-proxy fornece início de sessão único a uma aplicação que não tem um sistema de login próprio. Isto funciona porque o reverse proxy à frente da aplicação interrompe todos os pedidos, pergunta ao oauth2-proxy se o pedido contém uma sessão válida e só encaminha o pedido para o upstream quando a resposta é afirmativa. O código da aplicação não é alterado, porque a aplicação nunca vê esta verificação.

A verificação consiste num pedido HTTP adicional. O proxy envia uma cópia dos cabeçalhos do pedido recebido para /oauth2/auth e lê o código de estado. O código 202 significa que o utilizador tem uma sessão, pelo que o proxy encaminha o pedido original para a aplicação. O código 401 significa que não existe uma sessão, pelo que o proxy envia o browser para /oauth2/sign_in, que inicia um login OpenID Connect (OIDC) no seu fornecedor de identidade. O OIDC é a camada de identidade criada sobre o OAuth 2.0, e o fornecedor é o sistema que já utiliza para os logins.

Este padrão tem um nome em cada reverse proxy. O Nginx chama-lhe a diretiva auth_request. O Traefik chama-lhe o middleware forwardAuth. No Caddy, escreve-se forward_auth. O serviço que responde ao subpedido também pode ser substituído. O oauth2-proxy é a escolha habitual porque fala OIDC diretamente e não precisa da sua própria base de dados.

Defina o limite de confiança antes de escrever qualquer configuração

Depois de uma verificação bem-sucedida, o oauth2-proxy devolve a identidade nos cabeçalhos da resposta, e o reverse proxy copia-os para o pedido enviado ao upstream. Com set_xauthrequest ativado, obtém X-Auth-Request-User e X-Auth-Request-Email. A aplicação lê esses cabeçalhos e confia neles.

Esse é todo o modelo de segurança, portanto explicite a consequência. Qualquer processo que consiga abrir uma ligação TCP à porta da aplicação pode definir esses cabeçalhos e assumir a identidade de qualquer utilizador. Um único curl -H "X-Auth-Request-Email: admin@example.com" http://app-host:3000/ constitui um bypass completo se conseguir chegar diretamente à aplicação.

Por isso, a aplicação não pode estar acessível de outra forma que não seja através do proxy. No Docker Compose, remova o mapeamento ports: do serviço da aplicação e mantenha-o na rede interna, para que apenas o contentor do proxy possa estabelecer ligações com ele. Num host sem contentores, faça a aplicação escutar em 127.0.0.1:3000 em vez de 0.0.0.0:3000. Em seguida, confirme o que ficou realmente exposto:

sudo ss -tlnp | grep 3000

Uma linha com 0.0.0.0:3000 significa que a aplicação responde no IP público e que a sua barreira de autenticação é apenas decorativa. 127.0.0.1:3000 é o resultado pretendido. Uma regra de firewall é uma segunda camada útil, mas o endereço de bind é o que continua a funcionar mesmo que outra ferramenta limpe o seu conjunto de regras.

Instale o oauth2-proxy

Em agosto de 2026, a versão atual é a v7.15.3, publicada em junho de 2026. Instale o binário e verifique o download:

cd /tmp
curl -fsSLO https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.15.3/oauth2-proxy-v7.15.3.linux-amd64.tar.gz
curl -fsSLO https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.15.3/oauth2-proxy-v7.15.3.linux-amd64.tar.gz-sha256sum.txt
sha256sum -c oauth2-proxy-v7.15.3.linux-amd64.tar.gz-sha256sum.txt
tar -xzf oauth2-proxy-v7.15.3.linux-amd64.tar.gz
sudo install -m 755 oauth2-proxy-v7.15.3.linux-amd64/oauth2-proxy /usr/local/bin/oauth2-proxy
oauth2-proxy --version

sha256sum -c deve imprimir uma linha terminada em OK. Se imprimir FAILED, pare e faça o download novamente, em vez de executar o binário.

No Docker, a imagem é quay.io/oauth2-proxy/oauth2-proxy e deve fixar a tag: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3. Deixá-la em latest transforma uma docker compose pull de rotina numa atualização não planeada do processo que protege todas as aplicações no servidor.

O cookie de sessão é encriptado, e cookie_secret é a chave. Ela deve ter exatamente 16, 24 ou 32 bytes, porque se torna uma chave AES (advanced encryption standard). Com qualquer outro comprimento, o oauth2-proxy recusa-se a iniciar e apresenta um erro de arranque que identifica o segredo do cookie.

openssl rand -base64 32 | tr -- '+/' '-_'

O tr não é apenas uma questão estética. Ele converte a codificação base64 padrão no alfabeto seguro para URLs. Assim, o valor pode ser usado numa shell, num ficheiro de ambiente e num cabeçalho HTTP sem problemas de quoting.

Aplique duas regras a este valor. Use um segredo diferente em cada deployment. Se executar mais de uma instância do oauth2-proxy por trás do mesmo domínio, atribua-lhes o mesmo segredo. Um cookie encriptado por uma instância tem de poder ser lido pelas restantes.

Escreva a configuração do oauth2-proxy

Mantenha as definições num ficheiro, em vez de usar uma linha de comandos longa, para que o segredo do cliente nunca apareça na saída de ps.

# /etc/oauth2-proxy/oauth2-proxy.cfg
http_address = "127.0.0.1:4180"
reverse_proxy = true

provider = "oidc"
oidc_issuer_url = "https://id.example.com/application/o/myapp/"
client_id = "REPLACE_ME"
client_secret = "REPLACE_ME"

redirect_url = "https://app.example.com/oauth2/callback"
cookie_secret = "REPLACE_ME"
cookie_secure = true
cookie_domains = [".example.com"]
whitelist_domains = [".example.com"]

email_domains = ["*"]
set_xauthrequest = true
upstreams = ["static://202"]

reverse_proxy = true indica ao oauth2-proxy que deve confiar nos cabeçalhos X-Forwarded-* enviados pelo proxy à sua frente. Sem esta opção, o oauth2-proxy considera o endereço do próprio proxy como o endereço do cliente e pode avaliar incorretamente se o pedido chegou através de HTTPS.

upstreams = ["static://202"] faz com que o oauth2-proxy responda com 202 a um pedido autenticado sem fazer proxy de nada. É exatamente isso que a autenticação de encaminhamento precisa, porque o reverse proxy faz o encaminhamento. A outra arquitetura coloca o oauth2-proxy diretamente no caminho dos pedidos, com upstreams = ["http://127.0.0.1:3000"] e sem qualquer auth_request. É mais simples para uma aplicação, mas não escala para dez.

email_domains = ["*"] permite qualquer endereço que o seu fornecedor autentique. Restrinja-o ao seu próprio domínio ou, melhor ainda, limite o acesso com uma associação a um grupo no fornecedor, porque é aí que já gere os utilizadores.

Execute-o com systemd usando um utilizador próprio:

# /etc/systemd/system/oauth2-proxy.service
[Unit]
Description=oauth2-proxy
After=network-online.target
Wants=network-online.target

[Service]
User=oauth2-proxy
Group=oauth2-proxy
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg
Restart=on-failure
ProtectSystem=strict
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
sudo useradd --system --no-create-home --shell /usr/sbin/nologin oauth2-proxy
sudo install -d -m 750 /etc/oauth2-proxy
sudo chown -R oauth2-proxy:oauth2-proxy /etc/oauth2-proxy
sudo chmod 600 /etc/oauth2-proxy/oauth2-proxy.cfg
sudo systemctl daemon-reload
sudo systemctl enable --now oauth2-proxy
curl -s http://127.0.0.1:4180/ping

/ping a mostrar OK significa que o processo arrancou e carregou a configuração. Este é o próprio endpoint de estado do oauth2-proxy e nunca solicita uma sessão. Se não houver resposta, consulte journalctl -u oauth2-proxy -n 50, porque um URL do issuer inválido e um segredo de cookie com comprimento incorreto fazem o processo falhar no arranque e ambos registam essa causa.

Registe o URI de redirecionamento no seu provedor

Crie uma aplicação OIDC no seu provedor e defina o URI de redirecionamento exatamente como o redirect_url da configuração: https://app.example.com/oauth2/callback. Exatamente significa que o esquema, o host, a porta e o caminho têm de corresponder carácter por carácter. Uma barra final torna-o num URI diferente.

Esta é a falha mais comum em toda a configuração e ocorre antes de o oauth2-proxy estar sequer envolvido. O provedor rejeita o pedido de autorização e apresenta a sua própria página de erro, por isso nada aparece no log do oauth2-proxy. O indicador está na barra de endereço: o browser continua no domínio do seu provedor e a string de consulta contém error=invalid_request ou a página apresenta diretamente redirect_uri. Quando isso acontecer, corrija o registo da aplicação no provedor, não a configuração do proxy.

Copie o URL do issuer a partir do provedor em vez de o escrever manualmente. O oauth2-proxy acrescenta /.well-known/openid-configuration a oidc_issuer_url e obtém esse documento de descoberta no arranque. Verifique-o primeiro:

curl -s https://id.example.com/application/o/myapp/.well-known/openid-configuration | head -c 400

Um JSON que contenha uma chave authorization_endpoint significa que o URL do issuer está correto. Um 404 ou uma página de erro HTML significa que está incorreto, e o oauth2-proxy falhará ao arrancar com esse mesmo 404. Se ainda não escolheu um provedor, a comparação entre Keycloak, Authentik e Zitadel explica as diferenças, e executar o Authentik como o seu próprio servidor SSO descreve a parte do provedor nesta mesma configuração.

Nginx: auth_request

O Nginx faz autenticação delegada com auth_request. A diretiva inicia uma sub-requisição interna e toma uma decisão com base no respetivo código de estado.

# in the http context, next to your other maps
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 443 ssl;
  server_name app.example.com;

  location /oauth2/ {
    proxy_pass       http://127.0.0.1:4180;
    proxy_set_header Host                    $host;
    proxy_set_header X-Real-IP               $remote_addr;
    proxy_set_header X-Auth-Request-Redirect $request_uri;
  }

  location = /oauth2/auth {
    proxy_pass       http://127.0.0.1:4180;
    proxy_set_header Host             $host;
    proxy_set_header X-Real-IP        $remote_addr;
    proxy_set_header X-Forwarded-Uri  $request_uri;
    proxy_set_header Content-Length   "";
    proxy_pass_request_body           off;
  }

  location / {
    auth_request /oauth2/auth;
    error_page 401 = @oauth2_signin;

    auth_request_set $user  $upstream_http_x_auth_request_user;
    auth_request_set $email $upstream_http_x_auth_request_email;
    proxy_set_header X-User  $user;
    proxy_set_header X-Email $email;

    auth_request_set $auth_cookie $upstream_http_set_cookie;
    add_header Set-Cookie $auth_cookie;

    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host       $host;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }

  location @oauth2_signin {
    return 302 /oauth2/sign_in?rd=$scheme://$host$request_uri;
  }
}

Há três detalhes importantes. proxy_pass_request_body off com um Content-Length vazio impede o nginx de copiar o corpo de todos os pedidos POST para a sub-requisição. Isto é importante porque o oauth2-proxy não lê esse conteúdo. Num upload de ficheiro, a configuração predefinida envia o ficheiro duas vezes.

O par auth_request_set $auth_cookie e add_header Set-Cookie devolve um cookie de sessão atualizado ao browser. Se o omitir, cookie_refresh não faz nada silenciosamente, porque o nginx descarta o Set-Cookie da sub-requisição e o browser mantém o valor antigo até a sessão expirar.

error_page 401 = @oauth2_signin transforma uma verificação falhada num redirecionamento para o login. Sem essa diretiva, um visitante não autenticado recebe uma página 401 Authorization Required simples e não tem como continuar.

Teste sempre antes de recarregar a configuração:

sudo nginx -t && sudo systemctl reload nginx

Se as diretivas envolventes forem novas para si, a anatomia de uma configuração de reverse proxy do nginx explica a camada subjacente.

Traefik: middleware forwardAuth

O Traefik precisa de dois middlewares para a mesma tarefa. Um executa a verificação. O outro transforma o 401 num redirecionamento do navegador.

# dynamic configuration
http:
  middlewares:
    oauth-auth:
      forwardAuth:
        address: https://oauth.example.com/oauth2/auth
        trustForwardHeader: true
    oauth-errors:
      errors:
        status:
          - "401-403"
        service: oauth-backend
        query: "/oauth2/sign_in?rd={url}"
        statusRewrites:
          "401": 302

Associe ambos ao router que expõe a sua aplicação e publique o oauth2-proxy no seu próprio router em oauth.example.com, porque o navegador precisa de aceder a /oauth2/sign_in e /oauth2/callback sem passar pela verificação.

statusRewrites, que transforma 401 em 302, é a parte que costuma ser esquecida. Sem isso, o Traefik devolve o redirecionamento para iniciar sessão com o estado 401, o navegador não o segue e o visitante vê uma página que contém apenas a palavra Found.

trustForwardHeader: true encaminha o host e o URI originais para o oauth2-proxy, que precisa deles para construir o valor rd que devolve o utilizador à página que pediu. Defina whitelist_domains para abranger também esse host. Caso contrário, o oauth2-proxy elimina o parâmetro rd por representar um risco de redirecionamento aberto e todos vão parar a / depois de iniciar sessão. Um servidor Traefik normalmente expõe várias aplicações ao mesmo tempo, e encaminhar várias aplicações Docker Compose através de uma única instância Traefik mostra a estrutura de routers onde esta configuração é integrada.

Caddy: forward_auth

app.example.com {
  handle /oauth2/* {
    reverse_proxy oauth2-proxy.internal:4180 {
      header_up X-Real-IP {remote_host}
      header_up X-Forwarded-Uri {uri}
    }
  }
  handle {
    forward_auth oauth2-proxy.internal:4180 {
      uri /oauth2/auth
      header_up X-Real-IP {remote_host}
      copy_headers X-Auth-Request-User X-Auth-Request-Email
      @error status 401
      handle_response @error {
        redir * /oauth2/sign_in?rd={scheme}://{host}{uri}
      }
    }
    reverse_proxy upstream.internal:3000
  }
}

A ordem é importante aqui. O bloco /oauth2/* vem primeiro e não contém forward_auth, porque um visitante que não iniciou sessão tem de conseguir aceder aos caminhos de início de sessão e de callback. Coloque a verificação antes desses caminhos. Caso contrário, o início de sessão redireciona para si próprio até o navegador desistir.

copy_headers é o que transporta a identidade para o pedido enviado ao upstream. Só produz valores quando o oauth2-proxy é executado com set_xauthrequest = true. A escolha entre os proxies é uma questão independente, e a comparação entre nginx, Caddy e Traefik explica-a.

Por que o login volta para a página de login?

Você inicia sessão no provedor, ele redireciona você de volta e o oauth2-proxy envia você imediatamente para o provedor outra vez. O loop significa que a solicitação de callback chegou sem o cookie que o oauth2-proxy definiu ao iniciar o fluxo. O log identifica o caso:

No cookies were found in OAuth callback.

ou, quando algum outro cookie chegou, mas o correto não:

Cookies were found in OAuth callback, but none was a CSRF cookie.

CSRF significa falsificação de solicitação entre sites, e este cookie existe para associar um callback ao login que o iniciou. O navegador mostra a mesma falha como:

Login Failed: Unable to find a valid CSRF token. Please try again.

Verifique estas quatro causas, nesta ordem.

  1. cookie_secure = true enquanto o navegador acessava o site por HTTP simples. Um navegador não armazena um cookie marcado como Secure em uma origem http://, portanto nunca o envia de volta. Termine o TLS (transport layer security) corretamente ou defina cookie_secure = false apenas durante testes em localhost.
  2. Um valor cookie_domains que não abrange o hostname mostrado na barra de endereço. .example.com abrange app.example.com e não tem efeito algum sobre app.example.net.
  3. O navegador está descartando o cookie. Uma extensão de privacidade rigorosa ou o bloqueio de cookies de terceiros pode remover _oauth2_proxy_csrf entre o redirecionamento de saída e o callback.
  4. Desvio do relógio. Se o relógio do servidor estiver muito diferente do relógio do provedor, iat e exp do ID token ficam fora da janela aceita, e a sessão é rejeitada ao chegar. timedatectl deve informar System clock synchronized: yes.

Observe o problema no lado do servidor em vez de tentar adivinhar:

sudo journalctl -u oauth2-proxy -f

Abra a aplicação numa janela privada. Cada solicitação é registada com o respetivo status, portanto um callback seguido imediatamente por outro redirecionamento para o provedor é o loop registado nos logs.

Caminhos que devem ignorar o login: APIs, webhooks e websockets

A autenticação de encaminhamento pressupõe um browser que mantenha um cookie. Os clientes sem browser falham.

Um cliente de API que envia Authorization: Bearer <token> não tem cookie. Por isso, recebe um 302 para a página de login do seu fornecedor e depois tenta interpretar HTML como JSON. Há duas soluções simples. Definir skip_jwt_bearer_tokens = true faz com que o oauth2-proxy aceite um token bearer JWT (JSON web token) válido do mesmo emissor. Isto é adequado quando os seus clientes de API já obtêm tokens do fornecedor. Caso contrário, exclua o caminho:

skip_auth_routes = [
  "^/api/",
  "POST=^/webhook/",
  "GET=^/healthz$"
]

Cada valor é uma expressão regular comparada com o caminho normalizado. Opcionalmente, pode ser prefixado por um método HTTP e =. POST=^/webhook/ mantém o receptor de webhook aberto a POST, enquanto uma pessoa que aceda ao mesmo caminho num browser continua a passar pelo login. Cada entrada cria uma exceção na sua proteção. Por isso, ancore as expressões com ^ e mantenha-as tão restritas quanto o cliente permitir.

Os websockets são o caso que costuma ser configurado incorretamente. O pedido de upgrade é um GET HTTP normal que transporta os mesmos cookies que qualquer outro pedido. Por isso, passa normalmente pela verificação e não precisa de uma exceção. O problema está no proxying à volta desse pedido. Sem os cabeçalhos Upgrade e Connection na localização protegida, o upgrade nunca termina e o cliente da aplicação tenta novamente sem parar, apresentando uma mensagem WebSocket connection ... failed na consola do browser. Excluir o caminho não resolve esse problema, porque o pedido já tinha sido autorizado.

Existe uma limitação concreta. A verificação é executada uma vez, durante o upgrade. Um websocket que permaneça aberto durante horas nunca volta a ser verificado. Por isso, remover um utilizador do seu fornecedor não fecha o socket que esse utilizador já tem. Reinicie a aplicação para cortar as ligações ativas.

Por predefinição, a sessão inteira fica dentro do cookie, encriptada com o seu cookie_secret. Isto mantém o oauth2-proxy sem estado e não requer nenhum serviço adicional. Também existe um limite, porque os browsers limitam um cookie a cerca de 4 KB. Quando o token de ID contém uma lista longa de claims de grupos, o oauth2-proxy divide a sessão por _oauth2_proxy_0, _oauth2_proxy_1 e os seguintes. Depois de algumas partes, os cabeçalhos dos pedidos ficam suficientemente grandes para o nginx responder com 400 Request Header Or Cookie Too Large antes de a aplicação receber o pedido.

Quando isso acontece, mova a sessão para o servidor:

session_store_type = "redis"
redis_connection_url = "redis://127.0.0.1:6379"

O browser passa a guardar um ticket curto, enquanto a sessão encriptada fica no Redis. O custo é a necessidade de manter um serviço disponível: se o Redis ficar indisponível, todas as sessões se tornam inválidas e todos os utilizadores terminam sessão ao mesmo tempo. O cookie store tem o seu próprio custo: dois pedidos que atualizem a mesma sessão exatamente ao mesmo tempo podem entrar em conflito e obrigar a autenticar novamente.

O que a autenticação externa não oferece

Isto é uma barreira de entrada. Não é autorização dentro da aplicação, e essa diferença determina se a abordagem é adequada ao seu caso.

Depois de o utilizador passar, a aplicação vê exatamente o que via antes. Se a aplicação tiver as suas próprias funções, a autenticação externa não as preenche, exceto se a aplicação aceitar autenticação baseada em cabeçalhos e mapear um cabeçalho para uma conta. O Grafana faz isso através das definições auth.proxy. A maioria das aplicações autoalojadas não faz, por isso todos os utilizadores que passam pela barreira são vistos pela aplicação como uma única identidade, que muitas vezes é uma conta de administrador.

Também não protege os tokens de API da própria aplicação. Um token de acesso pessoal emitido pela aplicação autentica-se na aplicação, não no oauth2-proxy, por isso deixa de funcionar assim que a barreira fica à frente dela. Se excluir o caminho da API para o fazer voltar a funcionar, esse token passa a ser a única proteção do caminho. Nesse caso, executa dois sistemas de autenticação no mesmo serviço, e o SSO abrange apenas um deles.

A revogação é a terceira limitação. Eliminar um utilizador no seu fornecedor impede novos logins e interrompe a renovação do token que cookie_refresh executa, mas um cookie de sessão existente continua válido até expirar. Por predefinição, cookie_expire é 168 horas, o que corresponde a uma semana de acesso para alguém que acabou de remover. Defina cookie_refresh com um valor curto, como uma hora, para que a revogação ocorra dentro desse intervalo.

O registo de auditoria também termina na barreira. O oauth2-proxy regista quem passou e quando. A aplicação regista uma sessão sem nome. Se precisar de saber quem alterou uma definição, a identidade no cabeçalho da aplicação é o mínimo necessário, e contas reais por utilizador são a resposta correta.

Pagar pelo SSO é a melhor opção

A autenticação delegada é a ferramenta certa quando uma aplicação não tem qualquer login, ou usa uma palavra-passe partilhada, e pretende um único local para adicionar e remover utilizadores. Exige uma tarde de trabalho e mais um processo, e funciona com qualquer aplicação que comunique por HTTP.

É a ferramenta errada quando pessoas diferentes precisam de permissões diferentes dentro da mesma aplicação. Uma barreira não consegue expressar «Ana pode editar os dashboards e Bo só pode consultá-los». Se o fornecedor vender um nível de serviço com SSO, o mapeamento entre grupos e funções é normalmente aquilo que está realmente a comprar. Recriar esse mapeamento com cabeçalhos e regras do proxy é mais frágil do que pagar por ele. O padrão de preços por trás desses níveis de SSO merece ser lido antes de decidir.

Duas outras situações apontam na mesma direção. O trabalho de conformidade que exige registos de auditoria por utilizador dentro da aplicação não aceitará um log de acesso do proxy como prova. Além disso, qualquer aplicação com um cliente móvel ou de desktop que não transporte cookies do navegador terá problemas com a barreira em todos os pedidos.

FAQ

O que é forward auth?

Forward auth é um padrão no qual o reverse proxy consulta um serviço de autenticação separado para cada pedido recebido antes de o encaminhar para o upstream. O proxy envia os cabeçalhos do pedido para um endpoint como /oauth2/auth e lê o código de estado. 202 significa que o acesso foi permitido, por isso o pedido original é encaminhado para a aplicação. 401 significa que não existe sessão, por isso o proxy redireciona o browser para o login. O Nginx implementa esta funcionalidade com a diretiva auth_request, o Traefik com o middleware forwardAuth e o Caddy com forward_auth.

Porque é que o oauth2-proxy me redireciona para a página de login num ciclo?

O pedido de callback chegou ao oauth2-proxy sem o cookie CSRF, por isso o oauth2-proxy reinicia o fluxo. O log do servidor mostra No cookies were found in OAuth callback. e o browser mostra Login Failed: Unable to find a valid CSRF token. Please try again. A causa habitual é cookie_secure = true num site a que o browser acedeu através de HTTP simples, porque um browser não guarda um cookie Secure numa origem http://. A causa seguinte mais comum é um valor cookie_domains que não abrange o hostname apresentado na barra de endereço.

Como permito a passagem de um cliente de API ou de um webhook pelo oauth2-proxy?

Use skip_auth_routes com uma expressão regular ancorada, opcionalmente limitada a um método HTTP, por exemplo POST=^/webhook/. Se os seus clientes de API já tiverem JWTs emitidos pelo mesmo fornecedor, skip_jwt_bearer_tokens = true aceita esses tokens em vez de um cookie e mantém o caminho protegido. Tudo o que estiver listado em skip_auth_routes fica sem autenticação para todos, por isso mantenha cada expressão tão restrita quanto o chamador permitir.

O oauth2-proxy atribui permissões por utilizador à aplicação?

Não. É uma barreira de acesso, não um sistema de autorização. Decide quem chega à aplicação, e todas as pessoas que chegam parecem iguais para a aplicação, a menos que essa aplicação leia os cabeçalhos de identidade e os associe a contas. O Grafana pode fazer isto através das definições auth.proxy. A maioria das aplicações self-hosted não consegue, por isso todas as pessoas que passam a barreira partilham a identidade única com que a aplicação está a funcionar.

É possível contornar o oauth2-proxy definindo manualmente o cabeçalho de identidade?

Sim, se for possível chegar diretamente à aplicação. A identidade é transmitida num cabeçalho simples, como X-Auth-Request-Email, e a aplicação confia no valor que recebe. Qualquer pessoa que consiga abrir uma ligação à porta da aplicação pode enviar esse cabeçalho e assumir a identidade de qualquer utilizador. Faça a aplicação escutar em 127.0.0.1 ou mantenha-a numa rede Docker interna sem uma porta publicada e confirme a configuração com sudo ss -tlnp.

#oauth2-proxy#sso#oidc#reverse-proxy#forward-auth