Reputation: 3036
I have tried securing my Spring Cloud Gateway using Keycloak as IdP but when I send the request via gateway the response is always redirecting me to Login Page.
Keycloak realm has three clients:
The request generated with Angular via browser is this one:
OPTIONS /trainings/exercises HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: http://localhost:4201
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-Fetch-Dest: empty
Referer: http://localhost:4201/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:4201
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Credentials: true
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
content-length: 0
GET /trainings/exercises HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: application/json, text/plain, */*
Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4bGpIN2tlaGJWeWZNcVBPWUgyZTZJbmRQQjZfQm15amtzOEk5dnJtWU8wIn0.eyJleHAiOjE1OTAzNDI1MzMsImlhdCI6MTU5MDM0MjIzMywiYXV0aF90aW1lIjoxNTkwMzQyMTc0LCJqdGkiOiJlZmVhZDI1ZS1jNTI3LTQxMzEtODQyMy1kMWExNGM4NmZiYTgiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODIvYXV0aC9yZWFsbXMvYmFza2VpdG9yIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVkMDRhYWU4LWQ5MTEtNGQ5MC1iMDIyLWU3ZDRkZWExNTRiYyIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlYW1zLXBvcnRhbCIsIm5vbmNlIjoiZjYwOTE0MjUtYzYzMy00MWI3LTkwNGUtZGQ1YWY0MDY2Y2RhIiwic2Vzc2lvbl9zdGF0ZSI6IjJkZTQwNzg3LTZkZGItNGI1ZC05OTVhLTc5MTU0ZGQwMWNmOSIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo0MjAxIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiY29hY2giXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJ0ZXN0MSB0ZXN0MSIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QxIiwiZ2l2ZW5fbmFtZSI6InRlc3QxIiwiZmFtaWx5X25hbWUiOiJ0ZXN0MSIsImVtYWlsIjoidGVzdDFAaW52ZW50LmNvbSJ9.jll1KSMZiubWrewXZ_DgtmxeRsHZ38VtDMAXfzMqo87M0C9OQ9ieG6VgveE5M2zJJDuUYv2ixiiOEgZeQ2MOYcZ93YX38viT8KahqE1RggSS2Iiq_O2xLx_BA-GvYtM4D5dkQvfXJ5NQ6id53QRPPKA7T_4lyAafxlmMrtslXOeC6_iGnbWlRxYAeGURoAkAoy8fZhHDbz8MRJ3ayyNpWbgkymQE8ZRw-d8N41n0eapId3UYlEUGRxPtHcbDGZIdPoVgewrIJSFr-Q7oLSlqaTvqbSjFn_zxiVIYjV-c7SVBxeOV5aMebKzCzmiRGYBm-4sFZpa6YJrlbRLnGIFquQ
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Origin: http://localhost:4201
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4201/
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 302 Found
Location: /oauth2/authorization/keycloak
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
content-length: 0
The token was generated using Keycloaks Login Page with clientId=teams-portal and later the output token is injected as Bearer Token in the next request sent to gateway.
gateway properties
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: training-service
uri: http://localhost:8200
predicates:
- Path=/trainings/**
filters:
- StripPrefix=1
- TokenRelay=
- RemoveRequestHeader=Cookie
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: http://localhost:8082/auth/realms/baskeitor
user-name-attribute: preferred_username
registration:
keycloak:
client-id: gateway
client-secret: 87444049-832b-41d0-995f-1ae84db61684
training-service properties
spring.application.name=training-service
keycloak.auth-server-url=http://localhost:8082/auth/
keycloak.realm=baskeitor
keycloak.resource=training-service
keycloak.public-client=true
keycloak.bearer-only=true
keycloak.principal-attribute=preferred_username
angular environment
keycloakConfig: {
url: 'http://localhost:8082/auth',
realm: 'baskeitor',
clientId: 'teams-portal'
}
¿Why token is not valid and it is redirecting me to /oauth2/authorization/keycloak? ¿How I should change the config in order to this works propertly?
EDIT 1 I have update gateway with this CORS config:
@EnableWebFlux
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
class CorsConfig : WebFluxConfigurer {
override fun addCorsMappings(corsRegistry: CorsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3600)
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
fun corsFilter(): CorsWebFilter {
val config = CorsConfiguration()
config.allowCredentials = true
config.addAllowedOrigin("*")
config.addAllowedHeader("*")
config.addAllowedMethod("*")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", config)
return CorsWebFilter(source)
}
}
But the browser displays this error:
Access to XMLHttpRequest at 'http://localhost:8082/auth/realms/baskeitor/protocol/openid-connect/auth?response_type=code&client_id=gateway&scope=openid%20address%20email%20microprofile-jwt%20offline_access%20phone%20profile%20roles%20web-origins&state=wDesvgfbtxm29q1MfeFoBYtxyXf-12Nnm47kXR7uzjU%3D&redirect_uri=http://localhost:8762/login/oauth2/code/keycloak&nonce=GvepIeC1HXq-xdq6ZBP333zrumwhXTB_KVei74gdndY' (redirected from 'http://localhost:8762/trainings/exercises') from origin 'http://localhost:4201' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
With this configuration the request is still redirected to Login Page:
OPTIONS /trainings/exercises HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: http://localhost:4201
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-Fetch-Dest: empty
Referer: http://localhost:4201/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:4201
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Credentials: true
content-length: 0
GET /trainings/exercises HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: application/json, text/plain, */*
Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4bGpIN2tlaGJWeWZNcVBPWUgyZTZJbmRQQjZfQm15amtzOEk5dnJtWU8wIn0.eyJleHAiOjE1OTA0MjMxNDYsImlhdCI6MTU5MDQyMjg0NiwiYXV0aF90aW1lIjoxNTkwNDIwNTMzLCJqdGkiOiIwMjk0NDA0MC00NTJjLTRkNWUtOGQxYS1kOWYwZTMxNDAyMzQiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODIvYXV0aC9yZWFsbXMvYmFza2VpdG9yIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVkMDRhYWU4LWQ5MTEtNGQ5MC1iMDIyLWU3ZDRkZWExNTRiYyIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlYW1zLXBvcnRhbCIsIm5vbmNlIjoiYTI4NjJkYTgtY2NiOS00ZDQzLTkxOGEtMGExMmIzYjQ2ZDY0Iiwic2Vzc2lvbl9zdGF0ZSI6IjczMTAxMzNjLTc3MGMtNDdiZS1iZmU5LWZjZDZkODRlZDVlYiIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo0MjAxIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiY29hY2giXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJ0ZXN0MSB0ZXN0MSIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QxIiwiZ2l2ZW5fbmFtZSI6InRlc3QxIiwiZmFtaWx5X25hbWUiOiJ0ZXN0MSIsImVtYWlsIjoidGVzdDFAaW52ZW50LmNvbSJ9.LwTs7-FM0Dh2ATx1KwRz5lGfAJjhluvHoTAuiUPd4WXjd1YNgfSQ5R0B_aY05MSLQupiVQNBwrEDmoJT2qup8jyoIUBn4aHRmtS1b3b0ppni60vprHTuEZKpBERfsoGE7mnfyxFv-xL_yUVNOrx4TzM3rnFTIPVchXAQuNkVrnp9UudrdWw4Qar1XhMPZ1J8Df8uPqDr5yBdHXu8u1IPptD3PnfcTfhesx1ugn09JmSFWtQzat7ucmdYjN1buhW8_NCK_kGSblM-k0GheAVO2Fjc6nvigsogW1gmmTRZNB1xX1DSZPNjQ4lNzNAR-JEtP0XEMvGHhuQDJbZ3yvFT6A
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Origin: http://localhost:4201
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4201/
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 302 Found
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:4201
Access-Control-Allow-Credentials: true
Location: /oauth2/authorization/keycloak
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
content-length: 0
OPTIONS /oauth2/authorization/keycloak HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: http://localhost:4201
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-Fetch-Dest: empty
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:4201
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Credentials: true
content-length: 0
GET /oauth2/authorization/keycloak HTTP/1.1
Host: localhost:8762
Connection: keep-alive
Accept: application/json, text/plain, */*
Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4bGpIN2tlaGJWeWZNcVBPWUgyZTZJbmRQQjZfQm15amtzOEk5dnJtWU8wIn0.eyJleHAiOjE1OTA0MjMxNDYsImlhdCI6MTU5MDQyMjg0NiwiYXV0aF90aW1lIjoxNTkwNDIwNTMzLCJqdGkiOiIwMjk0NDA0MC00NTJjLTRkNWUtOGQxYS1kOWYwZTMxNDAyMzQiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODIvYXV0aC9yZWFsbXMvYmFza2VpdG9yIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVkMDRhYWU4LWQ5MTEtNGQ5MC1iMDIyLWU3ZDRkZWExNTRiYyIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlYW1zLXBvcnRhbCIsIm5vbmNlIjoiYTI4NjJkYTgtY2NiOS00ZDQzLTkxOGEtMGExMmIzYjQ2ZDY0Iiwic2Vzc2lvbl9zdGF0ZSI6IjczMTAxMzNjLTc3MGMtNDdiZS1iZmU5LWZjZDZkODRlZDVlYiIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo0MjAxIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiY29hY2giXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJ0ZXN0MSB0ZXN0MSIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QxIiwiZ2l2ZW5fbmFtZSI6InRlc3QxIiwiZmFtaWx5X25hbWUiOiJ0ZXN0MSIsImVtYWlsIjoidGVzdDFAaW52ZW50LmNvbSJ9.LwTs7-FM0Dh2ATx1KwRz5lGfAJjhluvHoTAuiUPd4WXjd1YNgfSQ5R0B_aY05MSLQupiVQNBwrEDmoJT2qup8jyoIUBn4aHRmtS1b3b0ppni60vprHTuEZKpBERfsoGE7mnfyxFv-xL_yUVNOrx4TzM3rnFTIPVchXAQuNkVrnp9UudrdWw4Qar1XhMPZ1J8Df8uPqDr5yBdHXu8u1IPptD3PnfcTfhesx1ugn09JmSFWtQzat7ucmdYjN1buhW8_NCK_kGSblM-k0GheAVO2Fjc6nvigsogW1gmmTRZNB1xX1DSZPNjQ4lNzNAR-JEtP0XEMvGHhuQDJbZ3yvFT6A
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37
Origin: http://localhost:4201
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Accept-Language: es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 302 Found
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:4201
Access-Control-Allow-Credentials: true
Location: http://localhost:8082/auth/realms/baskeitor/protocol/openid-connect/auth?response_type=code&client_id=gateway&scope=openid%20address%20email%20microprofile-jwt%20offline_access%20phone%20profile%20roles%20web-origins&state=wDesvgfbtxm29q1MfeFoBYtxyXf-12Nnm47kXR7uzjU%3D&redirect_uri=http://localhost:8762/login/oauth2/code/keycloak&nonce=GvepIeC1HXq-xdq6ZBP333zrumwhXTB_KVei74gdndY
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
Set-Cookie: SESSION=d6617ced-7cda-414e-8f7b-2c1f2adb5694; Path=/; HttpOnly; SameSite=Lax
content-length: 0
Upvotes: 3
Views: 3828
Reputation: 240
I think you are mixing things up. With your current config, you need to serve or access the Angular app behind the gateway .You need to add another route in your gateway which forwards the request to your Angular app and access the Angular app via gateway. In your case its http://localhost:8762/angularapp. You will be redirected to your IdP to login and redirects back to your Angular app and you will be able to access your api. You also need to remove the OIDC authentication flow / implicit in your angular app because you wouldn't need it anymore since you are already authenticated. There will be a token exchange between your gateway and IdP when you access your API and you will get a JWT response which you can use in your API.
NOTE: Your keycloak client (gateway) should be set to confidential.
If you don't want to serve your angular app behind your gateway. you need to remove the oauth2 client in your gateway and just forward the request along with your access token into your api and let spring handle the jwt for authorization. Here's a tutorial
Upvotes: 0