1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.sra.security.oauth2;
20
21 import java.net.URI;
22 import java.nio.charset.StandardCharsets;
23 import org.apache.syncope.sra.security.AbstractServerLogoutSuccessHandler;
24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.beans.factory.annotation.Qualifier;
26 import org.springframework.security.core.Authentication;
27 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
28 import org.springframework.security.oauth2.client.registration.ClientRegistration;
29 import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
30 import org.springframework.security.oauth2.core.oidc.user.OidcUser;
31 import org.springframework.security.web.server.WebFilterExchange;
32 import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;
33 import org.springframework.util.Assert;
34 import org.springframework.web.util.UriComponentsBuilder;
35 import reactor.core.publisher.Mono;
36
37
38
39
40
41
42
43 public class OidcClientInitiatedServerLogoutSuccessHandler extends AbstractServerLogoutSuccessHandler {
44
45 @Autowired
46 @Qualifier("oidcClientRegistrationRepository")
47 private ReactiveClientRegistrationRepository clientRegistrationRepository;
48
49 protected final RedirectServerLogoutSuccessHandler serverLogoutSuccessHandler =
50 new RedirectServerLogoutSuccessHandler();
51
52
53
54
55
56
57 public void setLogoutSuccessUrl(final URI logoutSuccessUrl) {
58 Assert.notNull(logoutSuccessUrl, "logoutSuccessUrl cannot be null");
59 this.serverLogoutSuccessHandler.setLogoutSuccessUrl(logoutSuccessUrl);
60 }
61
62 @Override
63 public Mono<Void> onLogoutSuccess(final WebFilterExchange exchange, final Authentication authentication) {
64 return Mono.just(authentication).
65 filter(OAuth2AuthenticationToken.class::isInstance).
66 filter(token -> authentication.getPrincipal() instanceof OidcUser).
67 map(OAuth2AuthenticationToken.class::cast).
68 flatMap(this::endSessionEndpoint).
69 map(endSessionEndpoint -> endpointUri(exchange, endSessionEndpoint, authentication)).
70 switchIfEmpty(serverLogoutSuccessHandler.onLogoutSuccess(exchange, authentication).then(Mono.empty())).
71 flatMap(endpointUri -> redirectStrategy.sendRedirect(exchange.getExchange(), endpointUri));
72 }
73
74 private Mono<URI> endSessionEndpoint(final OAuth2AuthenticationToken token) {
75 String registrationId = token.getAuthorizedClientRegistrationId();
76 return clientRegistrationRepository.findByRegistrationId(registrationId).
77 map(ClientRegistration::getProviderDetails).
78 map(ClientRegistration.ProviderDetails::getConfigurationMetadata).
79 flatMap(configurationMetadata -> Mono.justOrEmpty(configurationMetadata.get("end_session_endpoint"))).
80 map(Object::toString).
81 map(URI::create);
82 }
83
84 private URI endpointUri(
85 final WebFilterExchange exchange,
86 final URI endSessionEndpoint,
87 final Authentication authentication) {
88
89 UriComponentsBuilder builder = UriComponentsBuilder.fromUri(endSessionEndpoint);
90 builder.queryParam("id_token_hint", idToken(authentication));
91
92 URI postLogout = getPostLogout(exchange);
93 builder.queryParam("post_logout_redirect_uri", postLogout);
94
95 return builder.encode(StandardCharsets.UTF_8).build().toUri();
96 }
97
98 private String idToken(final Authentication authentication) {
99 return ((OidcUser) authentication.getPrincipal()).getIdToken().getTokenValue();
100 }
101 }