Keycloak always returns 401 - Zuul api gateway for microservices - spring-boot

I got a micro-service running behind an API - gateway(Zuul) which has secured endpoints with the use of Keycloak. Keycloak always returns 401 for the secured endpoints when accessed from a public client(eg: postman) even with a valid token.
I have done the same scenario without the use of the API gateway and works perfectly fine which isolates the issue to newly added api-gateway.(have configured clients, roles, and users in keycloak).
The gateway service client is confidential and
The API service client is public, in keycloak.
All the services, including keycloak is running as docker containers.
Below is the keycloak-spring boot configuration in the properties file.
keycloak :
realm : admin-realm
auth-server-url : http://keycloak:8080/auth
ssl-required : external
resource : gateway-service
bearer-only : true
credentials.secret : *********************
#
security-constraints[0] :
authRoles[0] : user
securityCollections[0] :
patterns[0] : /api/*
Solved
The problem was with the mismatch between the issuer URL of the token (http://localhost:8080/auth/) and the keycloak configuration URL in the property file of the micro-service (http://keycloak:8080/auth).
This happens when I tested the microservice from outside the docker network. (From postman). It could be resolved after I added the hostname 'keycloak; to the host file of Windows,
Open c:\Windows\System32\Drivers\etc\hosts file as Administrator
Add 127.0.0.1 keycloak
Then from the postman, we could access the keycloak endpoint as,
http://keycloak:8080/auth......

Related

Spring cloud gateway does not forward HTTPS correctly

I've recently migrated a microservices backend to Spring boot v2.6.1 + spring cloud v2021.0.0 (old version was v2.2.1.RELEASE).
Previously, the setup included a discovery server (Eureka), a gateway (Zuul) and various microservices accessible externally via the gateway. All of these were TLS/secure port enabled so all requests (discovery, registration, gateway forwarding etc) required SSL trust store configuration.
Since Zuul is no longer supported in this version, I am switching to Spring cloud gateway, with the following application.properties setup:
server.port = 8080
eureka.instance.securePortEnabled=false
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
spring.application.name= gateway-service
spring.cloud.gateway.routes[0].id=first-service
spring.cloud.gateway.routes[0].uri=lb://FIRST-SERVICE
spring.cloud.gateway.routes[0].predicates[0].name=Path
spring.cloud.gateway.routes[0].predicates[0].args.pattern=/firstService/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
security.require-ssl=true
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=localhost
spring.cloud.gateway.httpclient.ssl.trustedX509Certificates[0]=cert.pem
Without ssl universally disabled, I can access a first service URL via that gateway without issue. With SSL enabled, I can access the first service directly and again no issue:
https://localhost:8100/test
{ "message": "Hello World!" }
However, the gateway is not correctly forwarding the requests either via the correct protocol or with the correct client certificate. I am getting the below error response instead of "Hello World" message:
https://localhost:8080/firstService/test
Bad Request This combination of host and port requires TLS.
(It's worth noting that the Eureka secure port is disabled, and registration/registry fetch is done via HTTP due to issues logged at Error setting custom trust store for Eureka Discovery client by overriding DiscoveryClient.DiscoveryClientOptionalArgs
Ok, this comes down to how the microservices register with Eureka.
Although FIRST-SERVICE was also setup with security.require-ssl=true for direct access it was also necessary to register with Eureka with SSL enabled.
Adding the settings below to application.properties fixed the issue without any changes needed for the gateway configuration:
eureka.client.tls.enabled=true
eureka.client.tls.key-store=file:<path-to-key-store>
eureka.client.tls.key-store-password=<password>
eureka.client.tls.keyStoreType=PKCS12
eureka.client.tls.keyPassword=<password>
eureka.client.tls.trust-store=file:<path-to-trust-store>
eureka.client.tls.trust-store-password=<password>

Keycloak Kubernetes 401 Unauthorized

I have a keycloak server and a spring boot app running on Kubernetes cluster and I'm facing 2 scenarios the first one is working and the other one I'm getting 401 Unauthorized.
First scenario:
Enter a test container with kubectl exec command inside my k8s cluster and ask for a token with a curl POST request :
http://keycloak-service-name:8080/auth/realms/lisa/protocol/openid-connect/token
I get a token
I send a request from postman using that token to the secured endpoint of my app and
It's working, access is granted
Second scenario:
Ask for a token by sending a POST request to: http://keycloak-external-ip:port/auth/realms/lisa/protocol/openid-connect/token with the necessary parameters from postman
I receive an access token
I send a request from postman to the secured endpoint using that token of my app I get 401 unauthorized
The Keycloak is exposed using NodePort service.
The app is exposed using istio ingress gateway.
Keycloak server version:9.0.0.
keycloak version in spring boot app: 10.0.2
Fixed this by changing the keycloak.auth-server-url value in the application.properties file.
The keycloak deployment has 2 services, one of type clusterIP, the second is of type nodePort
before
keycloak.auth-server-url=http://keycloak-clusterIP-service-name:8080/auth
after
keycloak.auth-server-url=http://node-ip-adress:nodePort/auth

How to use service name instead of External IP/domain name for "security.oauth2.resource.tokenInfoUri"

I am using Spring cloud with OAuth2. I have 4 applications as mentioned below:
1) Eureka server : Service Registery.
2) zuul as Api Gateway
3) Auth server - which is returning the access_token to client after successful authentication.
4) Resource server - wants to validate token before giving access to resource.
I used the below configuration in application.properties:
security.oauth2.resource.tokenInfoUri=http://localhost:9600/oauth/check_token
security.oauth2.client.client-id=resourceServer-client
security.oauth2.client.client-secret=secret
I dont want to give hard coded ipaddress / hostname for "tokenInfoUri" in application.properties.
I want to access the auth server by its service name as there can be multiple instances of Auth server.
How can i do that ?
I checked the code how tokenInfoUri works in spring boot using oauth2 and found that
ResourceServerTokenServicesConfiguration -> RemoteTokenServicesConfiguration -> remoteTokenServices()
method where tokenInfoUri us retrieved from ResourceServerProperties.java as #ConfigurationProperties.
I dont want to use JWT token for token verification. I want to verify using remote call from Resource Server to AuthServer.

Keycloak two Frontend Clients and one Backend Client

i've got following setup:
Frontends are Angular1 Frontends. Backend is Spring Boot in use with the Keycloak Sring Boot Adapter in Combination of Spring Security.
The process is the standard OAUTH2:
Frontend sends credentials to Keycloak
If valid Keycloak sends Token to Frontend
Frontend sends requests with this token to backend
Backend sends token to keycloak for validation
keycloak accepts token after validation
backend is responding to frontend
This is working fine with one Frontend. You have to config the Keycloak Adapter of the Spring Backend via "keycloak.auth-server-url" in application.properties. My keycloak.auth-server-url of the Keycloak Adapter is pointing to an WebServer which is delivering the Frontend1 and is configured as reverse proxy. So it is forwarding in step 3 to the keycloak server with this config:
keycloak.auth-server-url=www.UrlOfFrontend1.com/auth
'/auth' is the forwarding path to keycloak. It is working fine for one frontend.
My problem is step 4 with two frontends. Every Frontend is delivered by a own WebServer which is configured as reverse proxy. If i deliver a second frontend with a second reverse proxy which delivers Frontend2 keycloak is throwing the error:
[org.keycloak.adapters.BearerTokenRequestAuthenticator] (default
task-39) Failed to verify token:
org.keycloak.common.VerificationException: Invalid token issuer.
Expected 'http://www.UrlOfFrontend1.com/auth/realms/myrealm', but was 'http://www.UrlOfFrontend2.com/auth/realms/myrealm'
at org.keycloak.TokenVerifier.verify(TokenVerifier.java:156)
at org.keycloak.RSATokenVerifier.verify(RSATokenVerifier.java:89)
I need the Spring Boot Keycloak Adapter configuration for both frontends. I have got two public Frontend Clients in Keycloak configured. What am I missing?

Getting HTTP 401 with Spring Boot custom authorization server when accessing spring client

Hi everyone i am not able to proceed with following settings. your small pointers are appreciated.
problem statement
i am trying to use custom authorization server provided by spring cloud security and OAuth2 with my web application so that it can propagate access token to micro services in back end.
i can able to see my authorization server can able to provide access token and when try to ingest access token for invoking endpoints for for back end micro service it work as per expectation
problem faced
when i provide following configuration in spring boot web client(which will call my back end micro service)
in application.properties
security.oauth2.client.clientId=myclient
security.oauth2.client.clientSecret=abcsecret
security.oauth2.client.access-token-uri=http://localhost:9000/services/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:9000/services/oauth/authorize
security.oauth2.client.clientAuthenticationScheme=form
security.oauth2.resource.user-info-uri=http://localhost:9000/services/user
security.oauth2.resource.prefer-token-info=true
and i provide
http://localhost:8080
in my browser. it asks for credentials. i provide credentials as present with authorization server.
once valid credentials provided authorization server asks for valid scopes.
but one important thing i observe when my web client routed to authorization server it has redirect_uri
http://localhost:8080/login
(not ok since initially i entered http://localhost:8080)
i am also getting HTTP 401 error

Resources