Expose public and private endpoint through Spring Cloud Gateway - spring-boot

I'm using Spring Cloud Gateway like entrypoint for my infrastructure. The gateway is configured with keycloak to validate Authentication header with following configuration
spring:
security:
oauth2:
resource-server:
jwt:
jwk-set-uri: https://httpd.keycloak.local:443/keycloak/realms/myRealm/protocol/openid-connect/certs
An example Route is the following
spring:
cloud:
gateway:
routes:
- id: my-route
uri: http://service.local:8020
predicates:
- Path=/myPath/api/myRoute/test
filters:
- name: StripPrefix
args:
parts: 2
How can I define, into yml file, this route public and another one authenticated through jwk-uri directed to keycloak?

I see nothing about security rules in spring-cloud-gateway configuration doc.
I believe you'll have to either:
let all traffic go through gateway and handle security on each service
write a web-security configuration class and define a SecurityWebFilterChain bean.
use spring-addons-webflux-jwt-resource-server to define permitAll() routes from properties (other routes requirering user to be authenticated)
Only last solution would work with yaml file configuration only:
in pom, replace spring-boot-starter-oauth2-resource-server with
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
<version>${com.c4-soft.springaddons.version}</version>
</dependency>
in yaml, replace spring.security.oauth2.resource-server with
com.c4-soft.springaddons.security:
jwt-issuers:
- location: https://httpd.keycloak.local:443/keycloak/realms/myRealm
permit-all:
- /myPath/api/myRoute/test
- /public/**
Note that trying to access "non-public" routes without valid authorization would result in 401 (unauthorized) and not in 302 (redirect to login). In my opinion, client should unsure requests to protected routes are issued with Authorization header or handle unauthorized with a redirection to authorization-server and a retry.
Also note that spring-security-oauth2-webflux-addons will auto-configure more than just permit-all routes (CORS, CSRF and authorities mapping for instance).
Last, I haven't tried it yet with spring-cloud-gateway. please let me know how it goes ;-)

Related

How to configure spring boot to validate JWT token with call instropection endpoint of authorization server

I have simple resource server application with spring boot, this is yaml file:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8083/auth/realms/rasool
Now, i want to make change in configuration or code to force spring security to validate JWT token with calling introspection endpoint of authorization server instead of local validation with keys, but i didn't find any way as spring security docs says.
Spring-boot spring.security.oauth2.resourceserver.jwt.* configuration properties are for JWT decoder.
For token introspection, use spring.security.oauth2.resourceserver.opaque-token.* properties instead (token being in whatever format, including JWT). "opaque" means that tokens are considered a "black-box" by resource-server which delegates validataion and attributes retrieval to authorization-server on introspection endpoint:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: http://localhost:8083/auth/realms/rasool/protocol/openid-connect/token/introspect
client-id: change-me
client-secret: change-me
Introspection uri from .well-known/openid-configuration
If you are using Java configurationn the switch is about the same: replace http.oauth2ResourceServer().jwt()... with http.oauth2ResourceServer().opaqueToken()...
A few notes about declared clients on authorisation-server
Resource-servers introspect token on authorisation-server introspection endpoint using client-credentials flow: for each and every request it process, resource-servers will send a request to authorization-server to get token details. This can have serious performance impact. Are you sure you want to switch to token introspection?
As a consequence, in the properties above, you must configure a client with:
"Access Type" set to confidential
"Service Accounts Enabled" activated
Create one if you don't have yet. You'll get client-secret from "credentials tab" once configuration saved.
Note you should have other (public) clients to identify users (from web / mobile apps or REST client) and query your resource-server on behalf of those users.
From the authorization-server point of view, this means that access-tokens will be issued to a (public) client and introspected by antoher (confidential) client.
Complete working sample here
It does a few things useful for resource-servers:
authorities mapping (choose attributes to parse user authorities from, prefix & case processing)
CORS configuration
stateless-session management
CSRF with Cookie repo
anonymous enabled for a list of configured public routes
401 (unauthorized) instead of 302 (redirect to login) when trying to access protected resources with missing or invalid Authorization

Spring security Oauth2 Redirect URI setting is ignored when using application.properties

I am configuring multiple authentication clients for a spring-boot application, and am attempting to override the default redirect URI using:
spring.security.oauth2.client.registration.google.redirectUri={baseUrl}/oauth2/callback/{registrationId}
and then setting the following in SecurityConfig:
http.oauth2Login()
.authorizationEndpoint().baseUri("/oauth2/authorize")
.and()
.redirectionEndpoint().baseUri("/oauth2/callback/*")
However, this is not working - when accessing {baseUrl}/oauth2/authorize/google, the client is redirected to
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount
?response_type=code
&client_id<clientId>
&scope=email%20profile
&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Foauth2%2Fcode%2Fgoogle
&flowName=GeneralOAuthFlow
with redirect uri parameter "{baseUrl}/login/oauth2/code/google" which is the default set by spring security when redirectUri is not set. If I switch to using application.yml with the below configuration:
spring:
security:
oauth2:
client:
registration:
google:
redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
it works fine. However, for various reasons I want to stick with the application.properties format. Any idea why the setting is ignored?

Spring Boot SAML and AWS Cognito

I have a working Cognito app client that utilizes user pool that is wired to use SAML. It accesses Azure AD as IdP. When I click "Launch Hosted UI" it properly redirects me to the login screen and upon authentication attempts to load my callback URL.
Now I want to wire this with a Spring Boot app.
I found this example developed by Joe Grandja that is using spring-security-saml2-service-provider to connect to a simple IdP.
The example is very compelling because all I really need to do is to provide correct configuration that in example provided like this:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
signing.credentials:
- private-key-location: "classpath:credentials/rp-private.key"
certificate-location: "classpath:credentials/rp-certificate.crt"
identityprovider:
entity-id: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php
verification.credentials:
- certificate-location: "classpath:credentials/idp-certificate.crt"
sso-url: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php
However I'm lost at how to map information available to me from Cognito to these settings? For example values in signing.credentials?
Here's the list of settings I get from Cognito (all keys/names are bogus):
Pool Id: us-west-2_1B1AHf00
Pool ARN: arn:aws:cognito-idp:us-west-2:1234567898:userpool/us-west-2_1B1AHf00
Domain: https://blah-foo.auth.us-west-2.amazoncognito.com
App client: blahfoo-client
App client ID: 1b1l1a2f8oo83456c
Callback URL: http://localhost:8080
Login URL: https://blah-foo.auth.us-west-2.amazoncognito.com/login?client_id=1b1l1a2f8oo83456c&response_type=code&scope=email+openid&redirect_uri=http://localhost:8080
I also have a SAML-formatted file I got back from IdP but that is already plugged into Cognito so why would I put anything from it into the app configuration?
I wonder if part of spring-security-saml2-service-provider is to assemble that login URL and if I can get away with less settings that are given in the example?
Any pointers will be greatly appreciated
The signing.credentials section is if your app needs to sign things like an AuthnRequest. They are credentials that you own.
The items under identityprovider are things that Cognito would provide.
For Spring Boot 2.4+, if Cognito supports a SAML metadata endpoint, then you can provide that and Spring Security will discover the rest:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
identityprovider:
metadata-uri: classpath:cognito/metadata/file/location
Or, for earlier versions, you can use RelyingPartyRegistrations:
#Bean
RelyingPartyRegistrationRepository registrations() {
String location = "classpath:cognito/metadata/file/location";
RelyingPartyRegistration registration =
RelyingPartyRegistrations.fromMetadataLocation(location)
.build();
return new InMemoryRelyingPartyRegistration(registration);
}
That said, the information that you've posted about Cognito's authentication endpoint appears OAuth-based, especially the Login URL. You may instead consider configuring your app for OAuth 2.0 and pointing at Cognito's OAuth endpoint.
I am posting the solution as a separate answer however credit and accepted answer go to #jzheaux
Basically the comment section provides the much needed hint: Even if you are wiring SAML-based Identity provider you will wire up Cognito using OAuth information given to you in the AWS console for User Pool
In my specific case the application.yaml then looks like this:
spring:
security:
oauth2:
client:
registration:
cognito:
client-id: 1ab2cd34efghi5jk6klmno7p8
client-secret: *********
scope: openid
redirect-uri: http://localhost:8080/login/oauth2/code/cognito
clientName: foobar-sandbox
provider:
cognito:
issuerUri: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_abCDeFGHI
user-name-attribute: cognito:username
client-secret is found in General settings -> App clients -> Show Details

spring clould oauth2 redirect to "/" after login with spring cloud gateway

I'm a new comer for the spring oauth2. With the document check, I do a simple function for the oauth2 in a spring cloud alibaba nacos environment.
In the design, I want to add redirect for oauth request from gateway to auth micro service. Then auth micro service authorize with form login, and authorize the scope, then redirect to the redirect url set in oauth url.
When raising request to auth micro service directly, it works well. But if raising request to gateway, after log in, it will redirect to "/" direction and raise 404 error.
With investigation for SavedRequestAwareAuthenticationSuccessHandler, I found the session contained in request in null. Further more, I check the HttpSessionRequestCache, it also gets null session when saving the request. Since the request is not saved, it can't redirect to origin url after login and redirect to default url "/".
But I don't know why the request related session is empty. I also try to do a demo project like following:
#RequestMapping(value = "hello",method = RequestMethod.GET)
public RedirectView hello(HttpServletRequest request){
System.out.println("Hello:"+request.getSession().getId());
return new RedirectView("thanks");
}
#RequestMapping(value = "thanks",method = RequestMethod.GET)
public ResponseEntity<String> thanks(HttpServletRequest request){
System.out.println("Thanks:"+request.getSession().getId());
return new ResponseEntity("aaa",HttpStatus.OK);
}
The printed session id are different when using gateway. But without gateway, they are same. The following is my gateway settings:
server:
port: 8080
spring:
application:
name: flower-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: order-routes
uri: lb://flower-order
predicates:
- Path=/order/**
- id: core-routes
uri: lb://flower-core
predicates:
- Path=/**
- id: auth-routes
uri: lb://flower-auth
predicates:
- Path=/oauth/**
So I guess that this is caused by spring cloud gateway. But I don't know how to fix it. Please give me some information. Thanks.
New updates:
As I change the auth gateway to
- id: auth-routes
uri: https://127.0.0.1:8082
predicates:
- Path=/oauth/**
it passes normal behavior. So does it means that it is caused by load balance. But as I just have one running entity, not quite sure. Does this mean it should use the distribute session management such as spring session? As in actual production environment, the auth service won't only have one entity.

404 error in spring cloud gateway for every alternate request

I am encountering a very peculiar problem in spring cloud gateway. Every alternate request returns a 404. This happens across all services I've configured in the api-gateway without exception. I don't even know where to start to debug this problem.
Here's my application.yml file for common config.
server:
port: 8080
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: password
key-store-type: pkcs12
key-alias: tomcat
security:
require-ssl=true:
logging:
level:
org:
springframework:
cloud.gateway: DEBUG
http.server.reactive: DEBUG
web.reactive: DEBUG
spring:
application:
name: api-gateway
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
Here's my java config file
#EnableWebFluxSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
ReactiveClientRegistrationRepository clientRegistrationRepository) {
// Authenticate through configured OpenID Provider
http.oauth2Login();
// Also logout at the OpenID Connect provider
http.logout(logout -> logout.logoutSuccessHandler(new OidcClientInitiatedServerLogoutSuccessHandler(
clientRegistrationRepository)));
// Require authentication for all requests
http.authorizeExchange().anyExchange().authenticated();
// Allow showing /home within a frame
http.headers().frameOptions().mode(Mode.SAMEORIGIN);
// Disable CSRF in the gateway to prevent conflicts with proxied service CSRF
http.csrf().disable();
return http.build();
}
}
Here's the spring profile specific config file that gets loaded on top of the common application.yml file.
spring:
security:
oauth2:
client:
provider:
keycloak:
issuerUri: http://localhost:9080/auth/realms/mylocal
userNameAttribute: preferred_username
registration:
keycloak:
clientId: api-gateway-client
clientSecret: abcdefgh-ijkl-mnop-qrst-uvwxyz5d6a9
cloud:
gateway:
default-filters:
- TokenRelay
routes:
- id: news
uri: http://localhost:8082/news
predicates:
- Path= /news/**
- id: customers
uri: http://localhost:8083/customers
predicates:
- Path= /boards/**
- id: search
uri: http://localhost:8085/search
predicates:
- Path= /search/**
Hey i got the issue recently too, I figure out that it was the load balancing the issue. Make sure that your spring.application.name of the microservice that you try to contact are all in capital letter (EXAMPLE) especially if you use Eureka. (hope it helps)
I had a similar issue. The reason was in registration of several services under the same application name in my Eureka server. Just like this:
Eureka instances screenshot
Those are completely different services, but they are registered under one application name. So when a request is made through load balancer, the latter tries to use both of the service urls. One service is able to serve request correctly, but another one may know nothing about the requested path, that is why 404 is returned.
Check if there are two or more microservices using the same spring.application.name and registered with eureka.
I know it's an old question... But might help future readers.
hey, here is the simplest solution I found for myself, in the browser
url, instead of 'localhost', give your system's name.
ex:
http://hp:8083/SERVICE-NAME/customers/**
also make sure to name all your spring application in uppercase.

Resources