Authorizing requests through spring gateway with zool via oauth server - spring

My application has microservices behind (spring) gateway with zuul proxy. There is also internal (spring) oauth2 authorization server. I want to implement client_credentials grant type for my microservices calls from outside - for M2M communication.
When I configure for the gateway client_id and client_secret in its application.yml requests come through the gateway but there is no requester check - oauth authorizes the gateway itself, as a result there is no authorization at all. I could use authorization code grant type, but then it would require web-client authorization which (web client) user might not have.
If I request authentication token from the oauth microservice, I get correct token for this app.
How can I force the gateway use the requester's client_id and client_secret to get token from oauth? - e.g. I can provide them as basic authorization via header.
Or can I provide to the gateway the token obtained by the requester from oauth?
The question is very similar to another one: Implementing authentication and authorization using Zuul Proxy, Oauth2 on REST Microservices
except the thing that there might be no web client, but an external microservice.

I have answered the question Implementing authentication and authorization using Zuul Proxy, Oauth2 on REST Microservices.
In my case the most important thing was to configure zuul proxy to forward authorization header to downstream services. Initially I thought about using zuul filters, but solution was much simpler - just configure sensitive headers for zuul:
server:
port: 8080
zuul:
sensitiveHeaders: Cookie,Set-Cookie # <--- this line
routes:
spring-security-oauth-resource:
path: /spring-security-oauth-resource/**
url: http://localhost:8081/spring-security-oauth-resource
oauth:
path: /oauth/**
url: http://localhost:8083/spring-security-oauth-server/oauth
After successful authentication of a client/user with oauth JWT token will be forwarded to downstream by the gateway.
Certainly, for this gateway must allow unathenticated access to oauth resource and require authentication for all others.
More details on the topics can be found in the article
https://www.baeldung.com/spring-security-zuul-oauth-jwt

Related

Use OAuth2 for authorization of session-authenticated users

I'm developing a microservices application that has to authenticate users against an external Identity Provider using SAML2 protocol.
The architecture
The idea is to use a SPA running in the browser which only talks to the API Gateway and uses Cookies for authentication.
The gateway delegates the Authorization Server to check if each request is authenticated and initialize the SAML2 login if not.
Once the user authenticates, the Authorization server initializes a session and sends back the cookie straight to the browser.
The Authorization Server is actually an OAuth2 Auth Server as well as a SAML2 Service Provider.
For every request coming after the user authenticated, I want internal communications to use OAuth2.
Frameworks used
For the authorization server I'm using the Spring Authorization Server package as well as SAML2 Service Provider libraries of Spring Security.
Resource services would use Spring Boot OAuth2 Server library.
What's working
I managed to set up the SAML2 client so that the Authorization Server is already generating a Session for the user after IdP authentication and I'm capable of reading the authenticated principal.
The problem
For the upcoming requests I want the API Gateway to perform a token replacement by exchanging the Cookie for an OAuth2 access token before forwarding these requests to resource services. Each resource service will then validate these tokens against the authorization server.
What I'm trying to achieve here is to make the API Gateway as a Backend-for-Frontend but the I'm struggling to figure out which authorization flow to use given that:
the client is the API Gateway, so it can be considered confidential
user credentials are missing as they are provided to an external IdP and the principal comes from a SAML Response
the authorization server has already estabilished a session for the user
Basically I can't figure out how to exchange the JSessionID for an authorization code.
Any ideas?
You should not bother about the authentication-code, the BFF (gateway configured as OAuth2 client) should receive it and exchange it for tokens (access, ID and refresh) during login process and store those in session (which should be activated along with CSRF protection).
When requests land on the gateway, session is replaced with Bearer access-token (kept in session) before being forwarded to resource-server. This behavior is activated with the tokenRelay filter in route properties (if I remember well...)

Keycloak authentication flow in a microservices based environment

I want to use Keycloak in a microservices based environment, where authentication is based on OpenID endpoints REST calls ("/token", no redirection to keycloak login page), a flow that I thought of would be something like this:
1. Front-end SPA retrieves the tokens from the "/token" endpoint and stores in browser's localStorage, then sends it with every request.
2. Gateway-level authentication: Acess Token is passed from the front end to the gateway, gateway consults Keycloak server to check if the token is still valid (not invalidated by a logout end-point call).
3. Micro-service based authorization: Acess Token is passed from the Gateway to the microservices, using Spring Boot adapter the microservices check the signature of the token offline (bearer-only client?) then based on the role in the token do the authorization.
My questions are: Does this flow make sense or can you suggest another flow? What type of Keycloak clients to use? What's an ideal way to pass Tokens using Spring Boot Adapter, and should it be done like that in the first place? Please keep in mind that I am not a Keycloak expert, I've done my research but I still have doubts.
Your Front-end SPA should be public-client and springboot micro service should be Bearer only Client and Gateway could be Confidential Client.
You can check the Keycloak provided oidc adapters. For springboot you use the keycloak provided adapter
Similar solution using api gateway is discussed here

Spring cloud oauth 2 with ingress kubernetes

Is it possible to use spring cloud oauth 2 server with kubernetes api gateway ingress.
I have used it with zuul to authenticate user before making a call. Can I do similar with ingress?
Edit 1:
To explain it more clearly, what I am trying to achieve
I am using token based oAuth2 implementation given by the spring cloud.
oauth is running as one of the service behind the zuul.
zuul has routes mapped for the oauth server and resource server
client call the auth server via zuul and gets the token.
client call resource server via zuul with token passed
zuul is configured to validate the token before making a call to resource server.
In this way we can stop any downstream traffic to go without a valid token.
can we do token validation in ingress with auth server running with in a cluster?
I have not used Spring Cloud OAuth 2 but as OAuth is a standard I believe you can set it up if you are using Nginx Ingress as the ingress controller, you can specify and external Oauth Provider (As OAuth generally has the same flow) like this on your ingress:
...
metadata:
name: application
annotations:
nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri"
...
You can find more information here with an example of using GitHub as an OAuth provider
There are currently three different nginx-ingress-controllers (see here), which differ in functionality. I believe that none of these ingress controllers themselves can perform an oauth token introspection. However, requests can be routed to the authorization server's introspection interface using the auth_request module.
Specifically for your case, you can use the auth-url annotation (see) in the ingress controller to direct the requests to the introspection interface of the spring cloud oauth2 server (see). The introspection interface is available under /oaut/check_token by default when #EnableAuthorizationServer is used. If the introspection interface returns a 2XX, the ingress will forward the request. This functionality is based on the auth_request module, which expects a 2xx response code from the external service if the access is allowed and 401 or 403 if denied.
If you use JWTs and want to validate the request by only checking the signature, this can in some cases actually be done by the ingress itself. To my knowledge, only the nginx plus ingress controller (paid) can validate JWTs. But there is also the nginx-based kong-ingress controller, which you can equip with pulgins (see here). There is e.g. promoted with oauth2 integration and JWT validation.
Did you find out more than me?

Relaying the incoming token downstream to other services

I'm trying to understand 100% how a Resource Server works, relaying the incoming token downstream to other services.
I have a microservice architecture with spring boot eureka, with Bearer authentication against an #EnableAuthorizationServer.
I use an Edge Service in zuul with #EnableZuulProxy and #EnableOAuth2Sso for request entry, and I wanted security to be centralized at that point, but of course, I can't leave the microservices without security and each of them is an #EnableResourceServer.
Everything works ok.
The question is:
Either with a security.oauth2.resource.user-info-uri pointing to the oauth server or to the edge service, that bearer token is always validated against the oauth server, i.e. if it passes through 10 microservices a request, will it validate the token 10 times against the oauth server?
Isn't there any way that I don't have to request 10 times to the oauth server if the token is valid?
All right,
It seems that for a Bearer token type, it is always necessary to authenticate on each resource server against the authentication server.
The solution is to use JWT tokens.
As explained in:
https://developer.okta.com/blog/2018/04/02/client-creds-with-spring-boot#extra-credit-reduce-the-number-of-calls-to-the-authorization-server
We use signed JWTs which means you can validate them locally instead of making an additional request from the API service to the authorization server on each request.
That's it.

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