Keycloak multi-tenancy and bearer-only microservice - spring-boot

I have some Spring Boot microservices with Spring Cloud API gateway (Zuul) in front of them.
API gateway authenticates users and forward the Authorization bearer token header.
Working fine with a single realm.
Now, I'd like to use multiple realms.
Using KeycloakConfigResolver, I'm able to authenticate user with the API gateway (keycloak deployment based on path).
But how should I configure KeycloakConfigResolver for microservices (bearer-only) so the use the right KeycloakDeployment? As every service can be accessed by both realms I don't know how to detect against which realm user was authenticated?
I cannot use path for KeycloakConfigResolver as for example for the order-service, users can do a GET on /orders being authenticated by realm1 or realm2...
Using header does not seem to be a good solution either....
Any idea?
I hope I'm clear enough...

You can find the realm from the KeycloakPrincipal (logged-in user) and then build KeycloakDeployment accordingly, you find the example here
InputStream is = getClass().getResourceAsStream("/realm1-keycloak.json");
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);

Related

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.

How to secure Spring Cloud microservices using Spring Security?

Here is the authorization service. It has endpoints to login and receive a JWT using either a custom username/password or social OAuth (Facebook, GitHub etc.).
I have a Eureka server setup and Zuul gateway service. From what I understand, there are two ways to go about implementing secure microservices. You either proxy requests through the auth service, or you send requests to the requested service (Ex. Service A) and service A authorizes using the auth service.
I would like to use the second way, however I'm having trouble implementing it. Is my understanding correct? Is there a way to setup service A somehow so that certain paths (configured using Ant matchers) will have to authorize using the auth service, which will set the SecurityContext appropriately and inject a UserPrincipal into the request. If anyone can point me to a good guide for this that would be much appreciated.

How to implement JWT with Keycloak in Spring boot microservice acrhitecture?

I have read some articles for Keycloak spring implementation (eg: easily-secure-your-spring-boot-applications-with-keycloak) but no one mention how to use with JWT.
I have created zuul api gateway and add Keycloak adapter as described in the previously linked article. That's ok, but I want to use JWT with keycloak.
Mentioned elsewhere set the client access type to bearer-only and the session strategy to NullAuthenticatedSessionStrategy. That's enough or need something else for JWT?
So my questions:
How do I configure client on Keycloak admin for JWT?
How do I configure Keycloak in backend config file for JWT?
How do I configure Keycloak adapter for JWT?
How do I pass user info to microservice? Create filter in gateway? But how I get user info from request?
Keycloak access token is a JWT. It is a JSON and each field in that JSON is called a claim. By default, logged in username is returned in a claim named “preferred_username” in access token. Spring Security OAuth2 Resource Server expects username in a claim named “user_name”. So, you need to create mapper to map logged in username to a new claim named user_name.
In order to provide access to client (micro-service), respective role needs to be assigned/mapped to user.
In your spring boot application, then you need to configure connection to keycloak server, providing, auth url, token url, scope, grant-type, client-id and client-secret.
Afterthat, your app be able to parse JWT token, you need to create some JwtAccessTokenCustomizer. This class should extend DefaultAccessTokenConverter and implement JwtAccessTokenConverterConfigurer classes. The main logic lays in public OAuth2Authentication extractAuthentication(Map<String, ?> tokenMap) method.
Then you need to configure OAuth2 Resource Server to provide access for other micro services. For that you define here - Oauth2RestTemplate Bean.
And in the end, secure your REST API, via the standard configuration Component.
So, you can see that, it is a large work, and couldn't be described with code, show some of your work, divide it to the chunk, and ask interesting your questions.

Implementing authentication and authorization using Zuul Proxy, Oauth2 on REST Microservices

I am trying to implement the above architecture in the workflow with Spring Boot.
Web client makes a request to Resource Server (Microservices Endpoints) through Zuul Proxy.
Zuul Proxy redirects to oauth2 server for authentication.
Oauth2 redirects to Zuul Proxy if the request is authenticated or not.
If not authenticated, Zuul redirects Web client with an unauthenticated response.
If Authenticated, Zull proxy redirects to the requested microservice endpoint.
Microservice endpoint checks if the user is authorized (user level access) to access the resource or not.
Microservice also could make internal rest call to other microservice.
Finally, the requested resource is sent back to the client.
I want to make sure I am following the correct workflow.
I would like to know if there is any solution which has implemented a similar kind for securing microservices APIs.
I have confusion on:
How can we pass the user details to the microservices so that the microservices can do their own level of user authorization?
Should the OAuth2 Access Token header be passed to each microservices such that microservices can validate the token separately?
Should each Microservice use secret credentials to validate the access token so that the token cannot be forged along the request chain?
I know its a bit of lengthy question. But I have not found a proper solution to above architecture.
Unfortunately, I don't have complete answer, only some parts:
Once JWT token is available to the zuul proxy then every microservice can authorize requests by configuring its resource server, e.g.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().access("#oauth2.hasScope('microserviceA.read')").and()
.csrf().disable()
.httpBasic().disable();
}
Scopes could be managed by the oauth microservice with a database - basing on the client credentials it will take the scopes info and encode into JWT token.
What I don't know at the moment - how to make the zuul proxy to use "web client" credentials to authorize itself by the oauth - I don't want to hard-code zuul proxy credentials because then the web-client creds won't be used.
I've just posted similar question on this topic:
Authorizing requests through spring gateway with zool via oauth server
update:
I've found article describing almost this configuration (without eureka, but it doesn't that add much complexity from my experience): https://www.baeldung.com/spring-security-zuul-oauth-jwt, there is github project with source code. The source code is unfortunately not polished as it's being used by the author for his commercial courses.
But I've managed to build from his examples working set.
Summary: in the described architecture every resource server (microservice A, B, ..) receive JWT token forwarded by the zuul proxy/gateway from the requesting client. The token is forwarded in a request header. If there is no valid token provided then the gateway will redirect the request to authorization page.
Also every resource server can check the token with the oauth service and if required do scope checking as I wrote above.
I've been struggling with same security design issue for microservice architecture based on spring cloud solution. I only find this article shedding some light on it: https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth
But it's pertaining to Okta sso service provider, not a generic solution to other oauth2 server like keycloak.
I also saw some solutions on how to protect gateway and microservice with oauth2 server like this one:
https://github.com/jgrandja/oauth2login-gateway
But it doesn't take into consideration the web client.
I am not sure whether you were able to resolve this, I can see this is not answered yet, but there is a way you can pass all information from JWT to all downstream microservices.
Write your own ZuulAuthenticationFilter, and then create below method
private void addClaimHeaders(RequestContext context, String token) {
try {
Map<String, Claim> claims = jwtTokenVerifier.getAllClaims(token);
claims.forEach((key, claim) -> {
context.addZuulRequestHeader("x-user-info-"+key, String.valueOf(claim.as(Object.class)));
});
}catch(Exception ex) {
log.error("Error in setting zuul header : "+ex.getMessage(), ex);
}
}
this way, you will get information from JWT in headers in each microservice, headers that starts with "x-user-info-" will have your JWT details
There is an implementation of the above architecture in following link:
https://www.baeldung.com/spring-security-zuul-oauth-jwt

Resources