Open piantino opened 1 year ago
Alguem testou isso? Deu certo?
@rdurelli é basicamente isso, eu sugeriria apenas algumas alterações:
n
) em https://sso.acesso.gov.br/jwk e colocando no campo validar assinatura (precisa estar atento porque quando a chave for rotacionada o login vai parar de funcionar)openid email profile govbr_confiabilidades
(embora o govbr_confiabilidades
parece não ser mais utilizado, é o que tá na documentação).prompt=none
pode estar ON
, e é importante que esteja, para permitir checagem do SSO ao abrir o app (mas a técnica de checagem via iframe padrão da lib keycloak (chamada nos docs de silent sso check) é bloqueada (só em produção) [e provavelmente deveria ser mesmo] no gov.br via Content-Security-PolicyObtenção de informações do usuário: Para obter foto do usuário, níveis da conta (bronze, prata, ouro) e selos de confiabilidade (diversos), é preciso fazer chamadas na API e utilizar o token gov.br original, pra isso, é preciso ativar as opções Store token
e Stored tokens readable
para que o app possa obter o token gov.br original e chamar o endpoint apropriado. A obtenção do token é feita como descrito no doc https://www.keycloak.org/docs/latest/server_admin/#retrieving-external-idp-tokens:
# para o nome de provider (gov.br) e realm (dev) usados na imagem :
curl https://meu.servidorkeycloak.com.br/realms/dev/broker/gov.br/token \
-H "Authorization: Bearer <KEYCLOAK ACCESS TOKEN DO USUÁRIO>"
A resposta é o token original gov.br que a aplicação pode usar pra chamar os endpoints.
Confiabilidade e nível de conta: Talvez antigamente as confiabilidades e nível da conta viessem dentro do token (daí a existência do escopo), mas atualmente não vem mais e é preciso essa chamada subsequente. É importante que o dev compreenda que é necessário fazer essa checagem do nível da conta e confiabilidade no backend e não confie numa checagem feita no frontend. Isso é importante porque embora o frontend possa ser mexido pelo usuário, quando ele submete o token no backend para chamar uma api do app, como o token tem uma garantia criptográfica de que está correto (e pode ser checado), o backend pode confiar que se o token é válido, as informações ali dentro também são e que o usuário muito provavelmente se logou corretamente (cuidado com token hijacking). Mas como a informação de nível de conta e confiabilidade não está DENTRO do token, precisaria ser passada por fora do token e por isso está sujeita a ser forjada. Dessa forma, a aplicação precisa ou checar o nível de confiabilidade toda vez (com um cache adequado) que os seus endpoints próprios sejam chamados, ou precisa considerar o usuário logado e autorizado apenas depois da checagem ocorrer no backend e aí emitir seu mecanismo próprio de controle de sessão (cookies ou um token JWT próprio).
Set-up de Autenticação: É uma pena também que a solução atual não consiga fazer o set-up authentication (ver https://github.com/servicosgovbr/manual-roteiro-integracao-login-unico/issues/23) porque o Keycloak atualmente suporta e seria transparente passar a exigir segundo fator (ou múltiplos fatores) de autenticação para algumas operações.
Integração transparente: se o gov.br é a única base de usuários e se a sua aplicação já tem o botão (obrigatório) "Entrar com gov.br", é possível esconder a tela de login do keycloak tornando o gov.br o authentication provider padrão:
No exemplo da imagem, o id a ser inserido na configuração é gov.br
(pode ser qualquer string que estiver como "Alias" do OIDC provider).
Eu venho usando o keycloak como intermediário entre o órgão e o gov.br há anos com essas configurações e funciona muito bem, mas com essas ressalvas.
Basicamente vc deu uma aula :) irei teste. Qualquer coisa lhe peço ajuda. Muito obrigado
Perfeito as explicações @weltonrodrigo,
Sobre o scope "govbr_confiabilidades" é necessário para permitir que o token retornado pelo Gov.br tenha permissão de chamar os serviços de confiabilidade, do contrário retornará um erro assim:
{
"errors": [
{
"status":401,
"code":"ACCESSTOKEN_SCOPE_MUSTCONTAINSEXPECTEDSCOPE",
"title":"Escopo requerido não encontrado. Valor esperado: '{0}', valor recebido: '{1}'."
}
]
}
Pelo menos foi o que aconteceu nos meus testes.
@rdurelli é basicamente isso, eu sugeriria apenas algumas alterações:
- validação de assinaturas: a solução do gov.br atual interpreta errado o RFC e o formato do jwks não é reconhecido pelo keycloak. Dessa forma, precisa fazer validação manual, extraindo a chave (campo
n
) em https://sso.acesso.gov.br/jwk e colocando no campo validar assinatura (precisa estar atento porque quando a chave for rotacionada o login vai parar de funcionar)
Em que ponto o Login gov.br interpreta a RFC 7517 errada ? O exemplo no https://www.rfc-editor.org/rfc/rfc7517#appendix-A tem os mesmos campos retornados pelo https://sso.acesso.gov.br/jwk. Há algum problema com os valores?
Em que ponto o Login gov.br interpreta a RFC 7517 errada ? O exemplo no https://www.rfc-editor.org/rfc/rfc7517#appendix-A tem os mesmos campos retornados pelo https://sso.acesso.gov.br/jwk. Há algum problema com os valores?
Você tem razão que o RFC coloca o campo 'use' como OPTIONAL. É mais correto eu dizer que o gov.br e o keycloak discordam sobre o formato do jwk.
No teste que realizei pra fazer esse comentário o pyjwt conseguiu validar corretamente a partir da url do keyset e quando eu criei esse repo, isso não acontecia, tinha que fornecer a chave manualmente. Suponho então que o pyjwt tenha se alinhado.
Quando adiciono um novo identity provider no keycloak, ele coloca um valor default no campo redirect uri. Eu não consigo edita-lo e para colocar o valor https://sso.tjsc..... conforme mostra no print. Alguma dica? Valeu
Quando adiciono um novo identity provider no keycloak, ele coloca um valor default no campo redirect uri. Eu não consigo edita-lo e para colocar o valor https://sso.tjsc..... conforme mostra no print. Alguma dica? Valeu
O Redirect URI é a URL do seu Keycloak (no print é o Keycloak do TJSC) e não pode ser alterado, já o Authorization URL é a do Gov.br. Pensa que o seu keycloak é o cliente do SSO do Gov.br, e outras aplicações serão os clientes do seu keycloak. Ficou mais claro agora?
estou tantando fazer a implementação do gov.br em um servidor de homologação da minha instituição, no entanto após informar o CPF e senha na tela de login do Gov.br, o usuário é retornado pra aplicação e o keycloak exibe a mensagem:
Unexpected error when authenticating with identity provider
« Back to Application
no log do keycloak aparece esse erro:
2024-08-05 17:10:16,864 WARN [org.keycloak.events] (executor-thread-173) type="IDENTITY_PROVIDER_LOGIN_ERROR", realmId="35-secret-e072bb", realmName="virtualif-homologacao", clientId="virtualif-modulo-copese", userId="null", ipAddress="192.168.1.254", error="identity_provider_login_failure", code_id="8158e5-secret-29abf3bc"
2024-08-05 18:19:51,484 WARN [org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProvider] (executor-thread-187) PublicKey wasn't found in the storage. Requested kid: 'rsa1' . Available kids: '[]'
2024-08-05 18:19:51,486 ERROR [org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (executor-thread-187) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: token signature validation failed
at org.keycloak.broker.oidc.OIDCIdentityProvider.parseTokenInput(OIDCIdentityProvider.java:679)
at org.keycloak.broker.oidc.OIDCIdentityProvider.validateToken(OIDCIdentityProvider.java:696)
at org.keycloak.broker.oidc.OIDCIdentityProvider.validateToken(OIDCIdentityProvider.java:690)
at org.keycloak.broker.oidc.OIDCIdentityProvider.getFederatedIdentity(OIDCIdentityProvider.java:379)
at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint.authResponse(AbstractOAuth2IdentityProvider.java:557)
at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint$quarkusrestinvoker$authResponse_ab908fbdd086ee82e140d8a818c077362a2d04b4.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
alguém tem alguma ideia do que pode ser? desde já agradeço
estou tantando fazer a implementação do gov.br em um servidor de homologação da minha instituição, no entanto após informar o CPF e senha na tela de login do Gov.br, o usuário é retornado pra aplicação e o keycloak exibe a mensagem:
Unexpected error when authenticating with identity provider « Back to Application
no log do keycloak aparece esse erro:
2024-08-05 17:10:16,864 WARN [org.keycloak.events] (executor-thread-173) type="IDENTITY_PROVIDER_LOGIN_ERROR", realmId="35-secret-e072bb", realmName="virtualif-homologacao", clientId="virtualif-modulo-copese", userId="null", ipAddress="192.168.1.254", error="identity_provider_login_failure", code_id="8158e5-secret-29abf3bc" 2024-08-05 18:19:51,484 WARN [org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProvider] (executor-thread-187) PublicKey wasn't found in the storage. Requested kid: 'rsa1' . Available kids: '[]' 2024-08-05 18:19:51,486 ERROR [org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (executor-thread-187) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: token signature validation failed at org.keycloak.broker.oidc.OIDCIdentityProvider.parseTokenInput(OIDCIdentityProvider.java:679) at org.keycloak.broker.oidc.OIDCIdentityProvider.validateToken(OIDCIdentityProvider.java:696) at org.keycloak.broker.oidc.OIDCIdentityProvider.validateToken(OIDCIdentityProvider.java:690) at org.keycloak.broker.oidc.OIDCIdentityProvider.getFederatedIdentity(OIDCIdentityProvider.java:379) at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint.authResponse(AbstractOAuth2IdentityProvider.java:557) at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint$quarkusrestinvoker$authResponse_ab908fbdd086ee82e140d8a818c077362a2d04b4.invoke(Unknown Source) at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29) at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147) at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583)
alguém tem alguma ideia do que pode ser? desde já agradeço
Experimenta desativar a opção “validate signature”.
Experimenta desativar a opção “validate signature”.
Muito obrigado @weltonrodrigo . Desativando a checagem pelo JWKS o login funciona perfeitamente. No entanto ao fazer isso eu não estaria diminuindo a segurança do processo de login? Existe alguma maneira de realizar a integração mantendo a checagem da assinatura e tmb usando PKCE? Mais uma vez obrigado pela ajuda
Isso está relacionado a issue #29.
Experimenta desativar a opção “validate signature”.
Muito obrigado @weltonrodrigo . Desativando a checagem pelo JWKS o login funciona perfeitamente. No entanto ao fazer isso eu não estaria diminuindo a segurança do processo de login?
Sim. Estaria.
Sim. Estaria.
Entendi. Obrigado.
Caso eu crie um endopoint próprio, que leia o objeto retornado por https://sso.acesso.gov.br/jwk, adicione a ele o campo use: sig
, e responda a requisição com objeto "completo", conforme o keycloak espera. E então configurar esse endpoint como 'JWKS URL' no keycloak.
Em teoria, com essa estratégia eu poderia realizar a assinatura, correto?
E imagino que nesse cenário não seria possível ativar a opção 'Use PKCE', certo?
Experimenta desativar a opção “validate signature”.
Muito obrigado @weltonrodrigo . Desativando a checagem pelo JWKS o login funciona perfeitamente. No entanto ao fazer isso eu não estaria diminuindo a segurança do processo de login? Existe alguma maneira de realizar a integração mantendo a checagem da assinatura e tmb usando PKCE? Mais uma vez obrigado pela ajuda
Sim, @carlosrodovalho, diminui. Mas não é teu grave pq tá num ambiente controlado e você recebe o token gov.br a partir de um request que você mesmo faz ao endpoint de token do Gov.br. Nao é algo que você recebe dos clientes e precisa validar.
Você pode manter a validação copiando e colando a chave no campo apropriado (e substituindo manualmente quando o gov.br trocar a chave).
O PKCE é independente e não relacionado. Você pode ativar ele mesmo sem validar assinaturas do token.
Sobre o seu endpoint ajustando o keyset, vai funcionar de boas.
Olá,
Gostaria de compartilhar a configuração que fiz para o Keycloak usar o Gov.br como Identity provider, ou seja, como adicionar um botão no Keycloak para o usuário se logar com o Gov.br.
A configuração utilizada:
Acredito que seria interessante colocar no exemplo de implementação, mas apenas ficando registrado aqui já pode ajudar alguém.