Authentication in Spring Cloud Gateway through Cookies(Session ID) - spring

I am trying convert one zuul gateway to spring cloud gateway, Previously we sent cookie with session id (keycloak) from UI , zuul automatically authenticates the request and sent to microservice, but in case of Spring Cloud Gateway we are getting the Authentication Object as null
Can some gave an idea ? Getting context from
ReactiveSecurityContextHolder.getContext()
ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.doOnNext(auth -> log.debug("Authentication:{}",String.valueOf(auth)))
.subscribe();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
log.debug("authentication is {}", authentication);
The first case it is not getting any Authentication object and 2nd case it is null

If you are using Keycloak, you might have OAuth2 access, ID and refresh tokens in addition to session ID.
I'd build client (UI) and resource (REST API) servers security with Spring-security OAuth2 implementation: using access-token for resources access control (and maybe ID token to access user info from UI, but not sure this is applicable to ZK). If your projects support boot, those are spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resource-server libs.
You can find samples mapping Keycloak roles to Spring authorities for a resource-server in this tutorials. For reactive apps, you'll have to dig into samples and my own spring-boot starters for JWT decoder or introspection source code (or just use the starters as done in samples, that would save you quite some efforts).

Related

Spring boot - Token online verification

I'm developing an app.
Front/bff/api.
I'm using an open id provider that allows to check token remotely.
The bff intercepts the front requests and sends them to the API with the jwt token in the header.
The api should ask the open ip provider if the token is correct (but remotely, not using the offline mode with the public key ).
The api is a spring boot 3.0.1 project.
How to configure security in spring boot 3.0.1 to do that check?
Thank you in advance.
You do that with access-token introspection. In spring-security conf, that means using opaqueToken() instead of jwt() (the first configures a resource-server with introspection and the second with a JWT decoder).
Be aware that token introspection is far less efficient than using a JWT decoder as a request is sent to the authorization-server for each and every request to a resource-server. Tutorial there.

Spring Cloud - Micoservice Authentication propagation

I am building an application using microservice architecture. I am using Eureka for service discovery with Spring Cloud Gateway for request routing. For authentication mechanism I am issuing JWT tokens (in auth service). What is the best practice when it comes to propagating Authentication so I can get logged user information in each service which is after the gateway?
So far I've came up/found couple of possible solutions:
In gateway add headers for relevant user information, and in each service create filter which would take said headers and create Authentication object and store it into SecurityContextHolder. The downside of this approach is I can't just plug and play services outside my application.
Pass the token coming from the client through the gateway to the each service, where I would have JWTFilter which would validate token and extract the user information. Downside I see with this approach is I have to have jwt secret shared between each service or stored on each service, and I would have to implement JWT logic, producing duplicate code.
Final solution is having something like oAuth token introspection endpoint in auth service which would be called from each service (filter) once the request reaches it.
I implemented the filter logic for validating the user token in the gateway service, but I would like to use role based authorization on each endpoint (service) differently (ie. user service has endpoint for creating users (ADMIN), and for fetching user information (ANY ROLE)).
I opted for something like your option 2 and use spring-boot to configure JWT decoder from an OIDC authorization-server (Keycloak).
Configuring OpenID resource-servers is super easy (more options in parent folder), and authorization-server JWT public signing key is retrieved automatically by spring JWT decoder.
All that is required is ensuring that Authorization header with JWT bearer is correctly propagated between services.

custom oidc in keycloak

I have a spring based application which does authentication and authorization(oauth2 based) for a client app.I want to now use keycloak to manage my authorizations, but i want to keep my spring code. Basically i want to use my existing auth code as an external identity provider in keycloak.
I am thinking of adding changes in client app such that it receives token from my existing oauth code(which does the authentication) and then exchange this token with keycloak(for session and authorization management). How can i do this? What configurations need to be done in keycloak?
I read about token exchange in keycloak here, but i am not clear about the kind of token i need to send from my existing auth code.
https://www.keycloak.org/docs/latest/securing_apps/
Here is how OAuth2 roles are usually spread:
Keycloak is authorization-server
Spring service is resource-server
front-end is client
user is resource-owner
I have a doubt of you wanting your Spring service to be "authorization-server" as well (serve user identity). If so, I think you should not.
Keycloak (or any other OpenID provider) should be the only authorization-server. Both Spring and client(s) should be configured to use it as so.
To write it differently, Keycloak is responsible for users login and emitting tokens with user ID (subject) and rights (roles or whatever). Other tiers in the architecture (clients & resource servers) get user info from the token and apply relevant security checks (spring security annotations, Angular guards, etc.).
I published a mono-repo for a meetup with minimal sample involving a Spring resource-server and Angular (with Ionic) client talking to a Keycloak OpenID authorization-server. You might find some inspiration browsing it.

Spring Keycloak - How to set principal from JWT access token

I've been at this for about a week now.
I have a use case where I recieve an auth token through the body instead of the header, and because of that Keycloak and Spring don't automatically set the user. (The reason being, with websockets, I can only send the auth token through the body with the initial connection)
I've tried intercepting the call before keycloak and copying the token from the body to the header, but that did not work.
So now I would like to manually authenticate through keycloak (or just manually set the principal user). I have access to the JWT Access Token, but from here I'm not sure how to authenticate with keycloak.
Anyone have any input?
Since there are two Keycloak pieces that could be in play here, I'll start with a clarification:
Keycloak - This is the authorization server that a client will use to obtain a JWT
Keycloak Adapter - This is the thing that configures a Resource Server to integrate Keycloak with Spring Security
I have a use case where I recieve an auth token through the body instead of the header, and because of that Keycloak and Spring don't automatically set the user.
Spring Security 5.1 ships with built-in support for JWT-based access tokens, so you may not need to use the Keycloak Adapter for what you are wanting to do.
When using Spring Security's built-in support, you can configure the DefaultBearerTokenResolver to look in the body:
#Bean
public BearerTokenResolver bearerTokenResolver() {
DefaultBearerTokenResolver resolver =
new DefaultBearerTokenResolver();
resolver.setAllowFormEncodedBodyParameter(true);
return resolver;
}

Get current authenticated user from all other microservices

I am creating a project with microservices architecture using spring. I have zuul for centralized security management, and some other microservices.
To access current authenticated user, in zuul i use this line of code :
SecurityContextHolder.getContext().authentication
But to get the user from other microservices, i extract the token (jwt) from the header in each request, and then i extract user info from the claims, but i find this method is a little annoying.
So, is there another more pretty method?
I tried to add the dependencies of spring security in the other microservices to use :
SecurityContextHolder.getContext().authentication
but every time i execute a request through zuul, even if the authentication is done from there, i get an unauthorized error message, despite having disabled the security autoconfiguration from these microservices.
Any suggestion?
Using SecurityContextHolder.getContext().authentication won't work in other microservices until you set the principal object in the Spring SecurityContext.
I don't know why you are setting the principal in security context at Zuul and extracting the same there, But yes Authentication and token validation should be done at Zuul and same jwt should be sent to backend microservices in header.
Now in backend microservices, By using spring security, extract the required claims from jwt and put in the SecurityContextHolder once, So that you can utilize it further for request authorizations or method level authorizations too.

Resources