spring-projects / spring-authorization-server

Spring Authorization Server
https://spring.io/projects/spring-authorization-server
Apache License 2.0
4.89k stars 1.3k forks source link

How-to: Use Spring Authorization server behind Spring Cloud Gateway #564

Open rcbandit111 opened 2 years ago

rcbandit111 commented 2 years ago

Publish a guide on How-to: Implement the core services behind Spring Cloud Gateway:

sjohnr commented 2 years ago

Related #499

rcbandit111 commented 2 years ago

When I can expect this guide?

rcbandit111 commented 2 years ago

Kind remind guys. I will appreciate it if someone write me some configuration steps before official documentation is ready.

jgrandja commented 2 years ago

@rcbandit111 It will be a while before we get to this as there are a few higher priority tasks we need to work on first.

If someone in the community can help that would be great. cc @spencergibb

rcbandit111 commented 2 years ago

@jgrandja Thank you for the response. I will wait further information.

jacko9et commented 2 years ago

No additional configuration is required if it is only used behind a gateway. However, in the case of load balancing, spring session support is required, because the server uses session to associate authentication actions.

salutonmondo commented 2 years ago

Is this suit your needs #807

sjohnr commented 2 years ago

@salutonmondo I believe this issue is addressing using Spring Authorization Server behind Spring Cloud Gateway, whereas the sample you submitted treats SAS as an identity provider but does not route the requests to it. When we look more deeply at this issue, we'll have to think about what use cases make sense for putting SAS behind the gateway. One that I can think of, for example, is rate limiting to protect the authorization server or specific endpoints from malicious clients.

uniquejava commented 2 years ago

@salutonmondo Thanks for your demo, I added some comments there in your https://github.com/spring-projects/spring-authorization-server/pull/807, could you shed some light?

zhoyq-work commented 1 year ago

use database to share data between auth servers, this can reslove load balancing problem.

zhenhe commented 1 year ago

any update recently?

maradanasai commented 1 year ago

Hi, any update?

uniquejava commented 1 year ago

I once needed this, after some research, I found what I really need is how to use spring cloud gateway as resource server and verify token from this single point. Finally I managed to do this, and I don't think I need the auth server sit behind spring cloud gateway now. Here is my current demo architecture.

image

The benefit: I just verify token from spring cloud gateway and pass the resolved user id as http header to my downstream micro-services and all my microservices now don't even have a dependency on spring security.

I once used spring cloud gateway as oauth2 client, --- almost each tutorial on the internet do this, and I think that is too academic/complex and not feasible at all in a real large application.

zhenhe commented 1 year ago

I once needed this, after some research, I found what I really need is how to use spring cloud gateway as resource server and verify token from this single point. Finally I managed to do this, and I don't think I need the auth server sit behind spring cloud gateway now. Here is my current demo architecture.

image

The benefit: I just verify token from spring cloud gateway and pass the resolved user id as http header to my downstream micro-services and all my microservices now don't even have a dependency on spring security.

I once used spring cloud gateway as oauth2 client, --- almost each tutorial on the internet do this, and I think that is too academic/complex and not feasible at all in a real large application.

good advice,how scg do both verify token and then pass request ? use filter?

uniquejava commented 1 year ago

@zhenhe Yes, a single UserHeaderFilter will do the job, and here is an example: https://stackoverflow.com/a/71485398/2497876

daphdev commented 1 year ago

I once needed this, after some research, I found what I really need is how to use spring cloud gateway as resource server and verify token from this single point. Finally I managed to do this, and I don't think I need the auth server sit behind spring cloud gateway now. Here is my current demo architecture.

image

The benefit: I just verify token from spring cloud gateway and pass the resolved user id as http header to my downstream micro-services and all my microservices now don't even have a dependency on spring security.

I once used spring cloud gateway as oauth2 client, --- almost each tutorial on the internet do this, and I think that is too academic/complex and not feasible at all in a real large application.

Hi. Can you make a sample repo? Many thanks.

Crain-32 commented 4 months ago

Hey @jgrandja, I've encountered this issue myself at work, and would be willing to write up the documentation if I could get some help getting it working in a Spring Preferred way.

I know it was declined, but since I haven't contributed to Spring Authorization before, I have written documentation for Validation in Spring Boot.

The issues I'm seeing are that Spring Authorization Server currently holds the client information within a Session Context. This is fine (and the preferred security option), however for the Token to be passed through the Gateway that Session either needs to be shared between the Authorization Server and the Gateway (Spring Session likely works, just unsure the proper configuration/dependencies), or some changes to the flow so that the gateway has the Session -> Token link instead.

The flow works as expected when the Authorization Server is the Identity Provider and the Gateway is the client. But that isn't always possible.

Some thoughts I've had are that you put the Authorization Server into the Gateway Application, so that way the Gateway/Authorization are in the same place. This works for when you're using a 3rd Party Identity Provider. However isn't really a preferred "Microservice" pattern. For example in the flow I'm aiming for (which is vague enough I can share) I would like to do the following.

I'd also be fine if the flow was using Spring Session to Distribute the information between the Authorization Server and Gateway.

If you'd like, I can share a repository with an example setup. Being Eureka, a Gateway, Authorization Server, and Dummy API.

Crain-32 commented 4 months ago

Example Setup can be found here.

Crain-32 commented 4 months ago

Minor update. I've attempted Distribution of information with Spring Session and a backer like Redis. It didn't work.

I've attempted using a ProxyExchange Approach instead of the typical links, it appears without some level of Special handling in the gateway putting the Authorization Server behind it just isn't possible.

jgrandja commented 4 months ago

@Crain-32 To be honest, I'm still finding it difficult to understand a use case on why an application would need to integrate Spring Authorization Server "behind" Spring Cloud Gateway. It makes perfect sense to me to integrate an OAuth2 Client within Spring Cloud Gateway, however, not the former.

The issues I'm seeing are that Spring Authorization Server currently holds the client information within a Session Context. This is fine (and the preferred security option), however for the Token to be passed through the Gateway that Session either needs to be shared between the Authorization Server and the Gateway

Why do you need to share the Spring Authorization Server session with Spring Cloud Gateway? Please provide more details and specifically the data you need shared.

sjohnr commented 4 months ago

Like @jgrandja, I'm not completely clear on what's being discussed here. I think it's possible there are multiple distinct use cases that are blending together, and it might be helpful to untangle them. The two use-cases I'm aware of are:

  1. Proxy the auth server behind the gateway - This could be useful for adding protective features like rate limiting (see comment) to the auth server, but provides little value otherwise. In this scenario, only the gateway is public facing. This seems to be the use case this issue was originally opened for, but I don't know if it's what most people are actually wanting a how-to guide for.
  2. Proxy resource servers behind the gateway and use the gateway as an OAuth2 Client with the TokenRelay filter to adapt from sessions to access tokens - This is the BFF use case that I feel most people are thinking about. In this scenario, the auth server and the gateway are both public facing.

The flow works as expected when the Authorization Server is the Identity Provider and the Gateway is the client. But that isn't always possible.

@Crain-32 can you please clarify this point also? In what situation is this not possible? Are you trying to put both 1 and 2 above together somehow?

Crain-32 commented 4 months ago

@sjohnr phrased it amazingly, I think there are two use cases being talked about.

I'm interested in both cases, and is likely why I caused the confusion. I'll list the reasoning behind why we want both 1 and 2.

  1. There are more benefits than just rate limiting. You can combine it with Eureka for dynamic scaling, you've isolated the codebases so Authorization Server version isn't coupled to the Gateway (Very useful since only recently was the Gateway able to be both reactive and traditional), etc. Beyond that it feels very... weird.. to have your Application Cluster /w Eureka, and then an Authorization Cluster that your favorite cloud provider's Loader Balancer handles. Gateway does load balancing, but to handle the Authorization Server we have to put it behind a different load balancer? Just feels off.

  2. Proxying the Resource servers behind the gateway and using it for TokenRelay is also something we'd like. For us the Gateway provides a single point to call, and routing everything from there is very nice. However to accomplish this we can't use the Authorization Server (unless it is the identity provider), or we have to put the Authorization Server in our Gateway (See notes on 1)

@Crain-32 can you please clarify this point also? In what situation is this not possible?

From my reading of the documentation it didn't seem possible to have the JWT Claims adjusted if your placed the Authorization Server behind the gateway. As I understand the flow, you'd now have a different Identity Provider (Github for example), and at that point your Authorization Server is the client, which results in the gateway not having the Access Token to use in the TokenRelay.

This was the case @jgrandja was asking clarification for.

Why do you need to share the Spring Authorization Server session with Spring Cloud Gateway? Please provide more details and specifically the data you need shared.

To get the TokenRelay to work if the Authorization Server is the client, we'd need to share the Access Token back to the gateway, however the Session Data is in the Authorization Server. Sharing the Session Data between services failed due to serialization/deserialization issues.

sjohnr commented 4 months ago

@Crain-32 thanks for the quick response!

  1. Gateway does load balancing

Good point on load balancing. That would be another major use case.

2. However to accomplish this we can't use the Authorization Server (unless it is the identity provider), or we have to put the Authorization Server in our Gateway (See notes on 1)

I'm not sure I'm understanding this, but I currently think I disagree with this point. (See below)

From my reading of the documentation it didn't seem possible to have the JWT Claims adjusted if your placed the Authorization Server behind the gateway.

Are you referring to the iss claim or some other claim(s)? I believe if you have set up X-Forwarded headers properly, the authorization server will correctly use the gateway's URI as the Issuer URI. I'd have to double check that though, which is part of what this how-to guide would help bring to light.

To get the TokenRelay to work if the Authorization Server is the client, we'd need to share the Access Token back to the gateway

I don't want to speak for Joe here, but I personally don't think I agree with this. Possibly, you're thinking about it differently or I'm misunderstanding you. I do believe it would be challenging to set up a gateway that combines both 1 and 2 (from my earlier comment), but not impossible. I don't know that it is a common enough scenario that it should be combined in a single how-to guide, but your points about load balancing make sense to me, such that I wonder if it could be more common than I expect.

Crain-32 commented 4 months ago

@sjohnr and I thank you for the quick response!

do believe it would be challenging to set up a gateway that combines both 1 and 2

I can understand that concern, and it might be one of those situations where the community could be asked about how important it is to support. Right now I'm using a work around, described below.

Are you referring to the iss claim or some other claim(s)? I believe if you have set up X-Forwarded headers properly, the authorization server will correctly use the gateway's URI as the Issuer URI. I'd have to double check that though, which is part of what this how-to guide would help bring to light.

Originally I was referring to scopes, and having the Authorization Server append some internal-specific scopes onto the JWT. However I've instead leveraged a Converter<Jwt, Authentication> on the specific application that required it. This removed the direct need for an Authorization Server, as the Gateway could be our client. However this also means we lost out on the QoL features the Authorization Server provides.

If the community shows a clear desire for 1 and 2, it might be useful to have a bit of a collaboration between the Gateway and Authorization Server projects to figure out a way to securely pass the Access/Refresh tokens to the Gateway. This would let the Gateway proxy as a client, while leveraging the Authorization Server as the actual client.

For now an easy bypass (with security tradeoffs, but that is the nature of security) would be a guide on how to configure the Authorization Server to store the JWT in a browser cookie. Then it's just a matter of extracting the cookie value in the gateway and swapping it to a Authorization: Bearer <Token>.

sjohnr commented 4 months ago

@Crain-32,

If the community shows a clear desire for 1 and 2, it might be useful to have a bit of a collaboration between the Gateway and Authorization Server projects to figure out a way to securely pass the Access/Refresh tokens to the Gateway.

I think we're still not on the same page yet. Hopefully, I can clarify things.

I don't want to get into the details of how each deployment we're discussing might be set up (1, 2, 1+2), but it's clear that combining 1 and 2 is complicated enough that we should avoid discussing it further unless we have a clear need to (e.g. the community asks for it), because it will make the conversation quite muddy.

Regardless, there is no need (in my view) to share a session between the gateway and the auth server in any scenario I'm aware of. The client (in this case, the gateway) should always make a standard back-channel access token request to the authorization server to obtain an access token.

This would let the Gateway proxy as a client, while leveraging the Authorization Server as the actual client.

I feel like possibly you're combining this conversation with the concept of federating login, for example by adding GitHub as the IdP for the authorization server. If so, this is a separate issue and out of scope for this how-to guide. In fact, we already have a how-to guide for that case.

For now an easy bypass (with security tradeoffs, but that is the nature of security) would be a guide on how to configure the Authorization Server to store the JWT in a browser cookie. Then it's just a matter of extracting the cookie value in the gateway and swapping it to a Authorization: Bearer <Token>.

This too is out of scope for this guide. Also, I wouldn't recommend this practice, so I don't think we should produce a guide to demonstrate it.

For now, I'd say let's focus on the core use cases I identified above (1 and 2), and try to determine which one will be most useful as a how-to guide. If we need two how-to guides, we can open an additional issue. However, I don't think I'd prefer to try and combine them in any case, at least not right now. Sound good?

Crain-32 commented 4 months ago

@sjohnr

it's clear that combining 1 and 2 is complicated enough

I got a chuckle out of "complicated enough", fantastic wording. Although it might just be that if you want 1 and 2, you run the Authorization Server on a Servlet Gateway, and System Architects can hold their tongue.

I feel like possibly you're combining this conversation with the concept of federating login, for example by adding GitHub as the IdP for the authorization server

Federated Login is likely what I'm thinking about. I'm unsure if you can use federated login with JWT claim adjustments, but that's a different issue targeting that guide/documentation.

Similar note though, to be clear, is federated login with situation 2 even possible? From my understanding the Authorization Server is the client in that flow, so the Gateway never has the information required to make the TokenRelay work as expected. I bring this up because if/once there is a guide on how to do 1, then we probably want to put some Note sections in several places about limitations.

sjohnr commented 4 months ago

is federated login with situation 2 even possible?

Yes, it is possible, and actually isn't different than what's in that guide.

From my understanding the Authorization Server is the client in that flow, so the Gateway never has the information required to make the TokenRelay work as expected.

There are actually two clients in that case. The gateway is a client (of the authz server) and the authz server is a client (of GitHub). You might be thinking about it incorrectly, as I picked up on you mentioning a few things that indicate you're thinking of using GitHub access tokens (for example) in the gateway, which you would not want to do. You would be using the authz server's access tokens in your architecture.

If you want to chat more about it and clarify it, perhaps you can put together a stackoverflow question and I can hash it out with you on there? We'll keep focused on discussing this how-to guide here.

Crain-32 commented 3 months ago

@sjohnr Sorry for taking a few days to respond, I took the weekend to focus on some other projects.

In that case 1 + 2 is possible if you use Federated Login, which is perfect for me.

If you want to chat more about it and clarify it, perhaps you can put together a stackoverflow question and I can hash it out with you on there?

I'm torn on stackoverflow vs the Issue, since although the issue is for a "How To", a conversation on the current difference between what the guides state and what users aren't understanding is very useful for adjusting the guides. I'd like to attempt federated login with cloud gateway using only the current documentation. I can then add what I felt like the guides were missing and we can work from there.

Regarding the current documentation state, should the Social Login Guide mention Federated Login near the top? I didn't even realize it was covering that, I just thought of it as a setting up the Authorization Server as a client of another Provider.

Finally, since the Guides are broken into sections, it might be useful to have the following, How-to: Use the Authorization Server with Spring Cloud

sjohnr commented 3 months ago

@Crain-32,

a conversation on the current difference between what the guides state and what users aren't understanding is very useful for adjusting the guides. I'd like to attempt federated login with cloud gateway using only the current documentation. I can then add what I felt like the guides were missing and we can work from there.

Please open a new issue, targeting improving the guide you followed. You might be prepared to provide a sample of what you ended up with to clarify configuration, misunderstandings, missing information, etc.

I didn't even realize it was covering that

Sorry for bringing up the term "federated", it is just a more technical way of saying the same thing. The main concept is adding one or more external login providers using OAuth2 Login (http.oauth2Login()). Again, let's keep this issue focused on the main topic. Please open a new issue for improving that guide.

How-to: Use the Authorization Server with Spring Cloud

I don't think all of the items you've mentioned match that title. However, please also open a new issue for that and provide as many details as you can.

Crain-32 commented 3 months ago

@sjohnr,

Sorry for bringing up the term "federated", it is just a more technical way of saying the same thing

Don't be sorry, that's the point I realized I was looking at things incorrectly. Federated Login is what I was thinking of, but I couldn't remember the proper term. I've been learning too many OAuth2 Terms during all this to remember everything.

The main concept is adding one or more external login providers using OAuth2 Login (http.oauth2Login()). Again, let's keep this issue focused on the main topic. Please open a new issue for improving that guide.

I'll hold off on that issue specifically until we have a clear path on this issue. From my (biased) perspective Federated Login is popular in the Cloud, so I'd rather hold off on improving the Documentation around that until how the Authorization Server fits with Spring Cloud Gateway is more defined.

However, please also open a new issue for that and provide as many details as you can.

I hate to say this, but the title of the current issue is "How-to: Use Spring Authorization Server behind Spring Cloud Gateway", so I'm unsure what to call a new ticket. Is the idea that you'd like to close this ticket as more of the initial discussion, and then we have tickets with less noise targeting the issues? Just want to make sure I'm on the same page.

Overall it sounds like we want three issues from this.

sjohnr commented 3 months ago

Is the idea that you'd like to close this ticket as more of the initial discussion, and then we have tickets with less noise targeting the issues?

This issue will remain open. As the title suggests, it is only addressing putting the authz server behind a gateway. The use cases we discussed are 1) rate limiting and 2) load balancing. What I'm suggesting is that anything else become a new issue. I'd recommend holding off opening any new issues if you're uncertain what you would like to suggest.

sjohnr commented 3 months ago

@Crain-32 In an effort to keep this issue focused, I'm going to mark a number of comments in our recent discussion as off-topic. GitHub will maintain the comments as hidden in case you want to revisit them and use them for opening up new issues. Thank you for understanding as we work to keep the issue tracker accessible and useful for everyone.

To re-cap, the two primary use cases I'm aware of for Spring Cloud Gateway and Spring Authorization Server are as follows:

  1. Proxy the auth server behind the gateway - This could be useful for adding features like rate limiting (see comment), load balancing and high availability to the auth server. In this scenario, only the gateway is public facing.
  2. Proxy resource servers behind the gateway and use the gateway as an OAuth2 Client with the TokenRelay filter to adapt from sessions to access tokens - In this scenario, the auth server and the gateway are both public facing. This is the BFF (backend-for-frontend) use case.

This issue addresses (1) above. I will open a new issue for (2).