Skip to content

Commit

Permalink
Pick end_session_endpoint from OpenID conf first, then spring-addons …
Browse files Browse the repository at this point in the history
…logout one
  • Loading branch information
ch4mpy committed Jun 14, 2023
1 parent eda1904 commit 4a95af9
Show file tree
Hide file tree
Showing 13 changed files with 38 additions and 10 deletions.
10 changes: 6 additions & 4 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Do not hesitate to fork this repo and send pull requests, even for things as sma

# Spring-addons
The libraries hosted in this repo shine in two domains:
- provide with annotations to mock OAuth2 `Authentication` during tests (`@WithMockJwtAuth`, `@WithOAuth2Login`, `@WithOidcLogin`, `@WithMockBearerTokenAuthentication`, etc.), which allow to test method security on any `@Component`. Details below.
- provide with annotations to mock OAuth2 `Authentication` during tests (`@WithMockJwtAuth`, `@WithOAuth2Login`, `@WithOidcLogin`, `@WithMockBearerTokenAuthentication`, etc.), which allow to test method security on any `@Component`. **New in 6.1.12: `@JwtAuthenticationSource` and alike to work with JUnit 5 `@ParameterizedTest`**. Details below.
- help configuring Spring Boot 3 applications OAuth2 configuration by pushing auto-configuration to the next level. As shown in **[Tutorials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials)**, with 0 Java conf (just properties), we can configure:
* authorities mapping (source claims, prefix and case transformation), without having to provide authentication converter, user service or `GrantedAuthoritiesMapper` in each app
* fine grained CORS configuration (per path matcher), which enables to override allowed origins as environment variable when switching from `localhost` to `dev` or `prod` environments
Expand All @@ -29,6 +29,8 @@ To test method security on any type of `@Component` (`@Controller`, off course,

An [article covering the usage of OAuth2 test annotations from this lib](https://www.baeldung.com/spring-oauth-testing-access-control) was published on Baeldung. This, along with all [samples](https://github.com/ch4mpy/spring-addons/tree/master/samples) and [tutorials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials) source-code (which contain a lot of unit and integration testing), should be enough to get you started.

Starting from `6.1.12`, `@JwtAuthenticationSource`, `@BearerAuthenticationSource`, `@OpenIdAuthenticationSource`, `@OAuth2LoginAuthenticationSource` and `@OidcLoginAuthenticationSource` were added to run the same JUnit 5 `@ParameterizedTest` with different `Authentication` instance. This can prove pretty handy to assert that access is actually granted for each of the entries in a `asAnyAuthority(...)` or `asAnyRole(...)` expression. See [Release Notes](#release-notes) or Javadoc for usage.

## 2. <a name="oauth2-starters"/>Spring Boot Starters
This repo contains thin wrappers around `spring-boot-starter-oauth2-resource-server` or `spring-boot-starter-oauth2-client`:
- [spring-addons-webflux-client](https://github.com/ch4mpy/spring-addons/tree/master/webflux/spring-addons-webflux-client) to be used in reactive applications rendering templates on the server (Thymeleaf, JSF, etc.), **or in `spring-cloud-gateway` used as BFF** (server-side OAuth2 confidential client securing a browser application with sessions and replacing session cookies with OAuth2 access tokens before forwarding requests from browsers to resource servers)
Expand Down Expand Up @@ -63,7 +65,7 @@ If locked wtih a lower JDK or spring-boot version, you'll have to use a 5.4.x re
I could forget to update README before releasing, so please refer to [maven central](https://repo1.maven.org/maven2/com/c4-soft/springaddons/spring-addons/) to pick latest available release
```xml
<properties>
<springaddons.version>6.1.11</springaddons.version>
<springaddons.version>6.1.12</springaddons.version>
<app-type>webmvc</app-type><!-- alternative value is webflux !-->
<token>jwt</token><!-- alternative value is introspecting !-->
</properties>
Expand Down Expand Up @@ -118,11 +120,11 @@ Using such libs is dead simple: just declare depedency on one of those libs and
2.0 comes with a noticeable amount of breaking changes. So lets start tracking features.

### 6.1.12
- [gh-122](https://github.com/ch4mpy/spring-addons/issues/122) Support for parametrized OAuth2 Authentications in `@ParameterizedTest`. Sample usage (mind the `@JwtAuthenticationSource` and `@ParameterizedJwtAuth`):
- [gh-122](https://github.com/ch4mpy/spring-addons/issues/122) Support for parametrized OAuth2 Authentications in `@ParameterizedTest`. **Mind the `@JwtAuthenticationSource` and `@ParameterizedJwtAuth`** in this sample (the first annotation defines the different authentication instances, the second inserts the one for the current test in the security context and provides it as test method parameter):
```java
@ParameterizedTest
@JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") })
void givenUserIsGrantedWithAnyJwtAuthentication_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception {
void givenUserIsGrantedWithAnyNiceAuthority_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception {
api.perform(get("/restricted"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.body").value("You are so nice!"));
Expand Down
12 changes: 9 additions & 3 deletions samples/tutorials/reactive-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,18 @@ static class DelegatingOidcClientInitiatedServerLogoutSuccessHandler implements
String postLogoutRedirectUri) {
delegates = StreamSupport.stream(clientRegistrationRepository.spliterator(), false)
.collect(Collectors.toMap(ClientRegistration::getRegistrationId, clientRegistration -> {
final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId());
if (registrationProperties == null) {
final var endSessionEnpoint = (String) (clientRegistration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint"));
if (StringUtils.hasText(endSessionEnpoint)) {
final var handler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository);
handler.setPostLogoutRedirectUri(postLogoutRedirectUri);
return handler;
}
final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId());
if (registrationProperties == null) {
throw new MisconfigurationException(
"OAuth2 client registration \"%s\" has no end_session_endpoint in OpenID configuration nor spring-addons logout properties"
.formatted(clientRegistration.getRegistrationId()));
}
return new AlmostOidcClientInitiatedServerLogoutSuccessHandler(registrationProperties, clientRegistration, postLogoutRedirectUri);
}));
}
Expand All @@ -262,7 +268,7 @@ static class DelegatingOidcClientInitiatedServerLogoutSuccessHandler implements

}
```
This handler switches between Spring's `OidcClientInitiatedServerLogoutSuccessHandler` and our `AlmostOidcClientInitiatedServerLogoutSuccessHandler` depending on the configuration properties.
This handler switches between Spring's `OidcClientInitiatedServerLogoutSuccessHandler` (used if the `.well-known/openid-configuration` exposes an `end_session_endpont`) and our `AlmostOidcClientInitiatedServerLogoutSuccessHandler` (if the logout configuration properties are present, or throws an exception).

Last we need to update the security filter-chain to use the new `DelegatingOidcClientInitiatedServerLogoutSuccessHandler`:
```java
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.c4soft.springaddons.tutorials;

import static org.springframework.security.config.Customizer.withDefaults;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
Expand Down Expand Up @@ -45,12 +47,14 @@
import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import com.c4soft.springaddons.tutorials.WebSecurityConfig.AuthoritiesMappingProperties.MisconfigurationException;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;

Expand All @@ -69,7 +73,7 @@ SecurityWebFilterChain clientSecurityFilterChain(
InMemoryReactiveClientRegistrationRepository clientRegistrationRepository,
LogoutProperties logoutProperties) {
http.addFilterBefore(loginPageWebFilter(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
http.oauth2Login();
http.oauth2Login(withDefaults());
http.logout(logout -> {
logout.logoutSuccessHandler(
new DelegatingOidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository, logoutProperties, "{baseUrl}"));
Expand Down Expand Up @@ -189,12 +193,18 @@ public DelegatingOidcClientInitiatedServerLogoutSuccessHandler(
String postLogoutRedirectUri) {
delegates = StreamSupport.stream(clientRegistrationRepository.spliterator(), false)
.collect(Collectors.toMap(ClientRegistration::getRegistrationId, clientRegistration -> {
final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId());
if (registrationProperties == null) {
final var endSessionEnpoint = (String) (clientRegistration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint"));
if (StringUtils.hasText(endSessionEnpoint)) {
final var handler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository);
handler.setPostLogoutRedirectUri(postLogoutRedirectUri);
return handler;
}
final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId());
if (registrationProperties == null) {
throw new MisconfigurationException(
"OAuth2 client registration \"%s\" has no end_session_endpoint in OpenID configuration nor spring-addons logout properties"
.formatted(clientRegistration.getRegistrationId()));
}
return new AlmostOidcClientInitiatedServerLogoutSuccessHandler(registrationProperties, clientRegistration, postLogoutRedirectUri);
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see ParameterizedBearerAuth
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see ParameterizedJwtAuth
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see ParameterizedOAuth2Login
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see ParameterizedOidcLogin
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see ParameterizedOpenId
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see BearerAuthenticationSource
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see JwtAuthenticationSource
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see OAuth2LoginAuthenticationSource
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see OidcLoginAuthenticationSource
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
* @see OpenIdAuthenticationSource
* @since 6.1.12
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
Expand Down

0 comments on commit 4a95af9

Please sign in to comment.