Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How-to: Use Spring Cloud Gateway with Spring Authorization Server #1687

Open
sjohnr opened this issue Aug 6, 2024 · 10 comments
Open

How-to: Use Spring Cloud Gateway with Spring Authorization Server #1687

sjohnr opened this issue Aug 6, 2024 · 10 comments
Labels
type: enhancement A general enhancement

Comments

@sjohnr
Copy link
Member

sjohnr commented Aug 6, 2024

Publish a guide on how to set up Spring Cloud Gateway as an OAuth2 Client of Spring Authorization Server in order to use the gateway as a BFF (backend-for-frontend). This guide would demonstrate using the TokenRelay filter to adapt from a browser-based session (i.e. JSESSIONID cookie) to an Authorization header containing an access token (i.e. Bearer tokens) when making protected resources requests.

The guide should mention the main benefits of this architecture choice, which include:

  • Securely storing access tokens (and refresh tokens) on the server (backend) instead of in the browser
  • Centralizing OAuth2 Client configuration in the gateway
  • Simplifying authentication requirements for browser-based (frontend) applications
@sjohnr sjohnr added the type: enhancement A general enhancement label Aug 6, 2024
@sjohnr
Copy link
Member Author

sjohnr commented Aug 6, 2024

Related gh-564, gh-297

@lojc
Copy link

lojc commented Aug 24, 2024

This will be extremely helpful for the community. I've been struggling trying to find a decent working tutorial that would help me to implemente a bff. Only thing i can find is Ch4mp redirecting to the baeldung tutorial he wrote. i tried to follow it, but it is pretty complex for a person who is starting in this.

@sjohnr
Copy link
Member Author

sjohnr commented Aug 26, 2024

Thanks for the upvote @lojc. One thing I'm wondering for the benefit of this guide would be: What are the main challenges you have faced when implementing a BFF? Can you share any details about your use case?

@HungUnicorn
Copy link

@sjohnr Would it be possible to also include Token-Mediating Backend?

@sjohnr
Copy link
Member Author

sjohnr commented Sep 13, 2024

I don't think so, @HungUnicorn. That would be a separate guide, but not one that we would likely demonstrate as it is less secure than a BFF.

@loren-coding
Copy link

loren-coding commented Sep 24, 2024

@sjohnr Maybe something like this. Can support multiple frontend clients.

  • Authentication
        @GetMapping("/authorize")
	public Mono<ResponseEntity<Void>> index(ServerWebExchange webExchange, @RequestParam(value = APP_PARAM, required = false) String appName) {
		AppProperties.Frontend.App app = properties.getFrontend().getDefaultApp();
		if (StringUtils.hasText(appName)) {
			AppProperties.Frontend.App targetApp = properties.getFrontend().getApp().get(appName);
			app = targetApp != null ? targetApp : app;
		}
		
		String targetUrl = determineTargetUrl(webExchange, app);
		return Mono.just(ResponseEntity.status(HttpStatus.FOUND)
				.header(HttpHeaders.LOCATION, UriComponentsBuilder.fromUriString(targetUrl).toUriString())
				.build());
	}
  • Logout org.springframework.security.oauth2.client.oidc.web.server.logout.OidcClientInitiatedServerLogoutSuccessHandler
        //Dynamically obtain the logout address instead of the fixed value of the `postLogoutRedirectUri` property.
	@Nullable
	private String getPostLogoutRedirectUri(ServerHttpRequest request) {
		return Optional.ofNullable(request.getQueryParams().getFirst(APP_PARAM))
				.map(p -> properties.getFrontend().getApp().get(p))
				.map(AppProperties.Frontend.App::getPostLogoutRedirectUrl)
				.orElseGet(() -> this.properties.getFrontend().getDefaultApp().getPostLogoutRedirectUrl());
	}

@sjohnr
Copy link
Member Author

sjohnr commented Sep 24, 2024

@loren-coding thanks for the suggestion. However, I feel like I might be missing some context. I don't think I see how multiple frontend applications are relevant to this guide. Also, if this code belongs in the authorization server, we should let the OAuth2 framework handle secure redirects using the redirect_uri and post_logout_redirect_uri parameters instead of reinventing the wheel here, so I don't think we would include anything like this. Having said that, it's also possible I'm misunderstanding what you're trying to demonstrate. Regardless, let's stay focused on discussing the focus for this guide which is Spring Cloud Gateway.

@sebutzu
Copy link

sebutzu commented Sep 24, 2024

It would be really great if we could also have a multi-tenant implementation of the Spring Cloud Gateway and Spring Authorization Server as a sample (maybe even with a Resource Server multi-tenant).

@loren-coding
Copy link

loren-coding commented Sep 25, 2024

@loren-coding thanks for the suggestion. However, I feel like I might be missing some context. I don't think I see how multiple frontend applications are relevant to this guide. Also, if this code belongs in the authorization server, we should let the OAuth2 framework handle secure redirects using the redirect_uri and post_logout_redirect_uri parameters instead of reinventing the wheel here, so I don't think we would include anything like this. Having said that, it's also possible I'm misunderstanding what you're trying to demonstrate. Regardless, let's stay focused on discussing the focus for this guide which is Spring Cloud Gateway.

Thank you very much for your reply. We have a scenario where a gateway is used by several systems at the same time. When logging in or out of a certain system, it is redirected back to the corresponding system address, so that the application experience will be better. Because some systems are relatively small, it seems unnecessary to establish a one-to-one BFF. Or is there an implementation plan that I don’t know about?

Otherwise, we can only create a method similar to a workbench or portal page to unify the login/logout address.

@sjohnr
Copy link
Member Author

sjohnr commented Sep 26, 2024

@loren-coding it sounds like you are building something like (but perhaps not exactly) a multi-tenant gateway. Or at least a gateway with multiple frontend clients. I imagine this use case is quite common. Thank you for providing the code snippets, but I still feel this type of configuration is out of scope for this guide. I do appreciate the discussion and extra details though.

Because some systems are relatively small, it seems unnecessary to establish a one-to-one BFF. Or is there an implementation plan that I don’t know about?

There isn't an implementation plan that you are missing. If you are having challenges accomplishing this, please feel free to provide a minimal sample in a separate git repository or upload a project as an attachment, and I'd be happy to take a look.

Otherwise, we can only create a method similar to a workbench or portal page to unify the login/logout address.

You shouldn't have to do that unless desired. Instead, you can certainly implement a custom ServerLogoutSuccessHandler which computes a dynamic postLogoutRedirectUri for each client, possibly like what you have done in your code snippet above. I will caution that implementing custom redirection logic should be done with care to avoid accidentally exposing a vulnerability, such as open redirect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants