|
17 | 17 | package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource; |
18 | 18 |
|
19 | 19 | import java.util.Collections; |
| 20 | +import java.util.LinkedHashMap; |
20 | 21 | import java.util.List; |
| 22 | +import java.util.Map; |
21 | 23 | import java.util.regex.Matcher; |
22 | 24 | import java.util.regex.Pattern; |
23 | 25 |
|
24 | 26 | import jakarta.servlet.http.HttpServletRequest; |
| 27 | +import jakarta.servlet.http.HttpServletResponse; |
25 | 28 |
|
26 | 29 | import org.springframework.http.HttpHeaders; |
27 | 30 | import org.springframework.http.HttpStatus; |
28 | 31 | import org.springframework.security.authentication.AuthenticationManager; |
29 | 32 | import org.springframework.security.config.annotation.web.HttpSecurityBuilder; |
30 | 33 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; |
31 | 34 | import org.springframework.security.core.Authentication; |
| 35 | +import org.springframework.security.core.AuthenticationException; |
32 | 36 | import org.springframework.security.oauth2.core.OAuth2AccessToken; |
33 | 37 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
34 | 38 | import org.springframework.security.oauth2.core.OAuth2Error; |
35 | 39 | import org.springframework.security.oauth2.core.OAuth2ErrorCodes; |
| 40 | +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
| 41 | +import org.springframework.security.oauth2.jose.jws.JwsAlgorithms; |
36 | 42 | import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider; |
37 | 43 | import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken; |
| 44 | +import org.springframework.security.web.AuthenticationEntryPoint; |
38 | 45 | import org.springframework.security.web.authentication.AuthenticationConverter; |
39 | 46 | import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler; |
40 | 47 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
41 | 48 | import org.springframework.security.web.authentication.AuthenticationFilter; |
42 | 49 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
43 | | -import org.springframework.security.web.authentication.HttpStatusEntryPoint; |
44 | 50 | import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; |
45 | 51 | import org.springframework.security.web.util.matcher.RequestMatcher; |
46 | 52 | import org.springframework.util.CollectionUtils; |
@@ -102,7 +108,7 @@ private AuthenticationSuccessHandler getAuthenticationSuccessHandler() { |
102 | 108 | private AuthenticationFailureHandler getAuthenticationFailureHandler() { |
103 | 109 | if (this.authenticationFailureHandler == null) { |
104 | 110 | this.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler( |
105 | | - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); |
| 111 | + new DPoPAuthenticationEntryPoint()); |
106 | 112 | } |
107 | 113 | return this.authenticationFailureHandler; |
108 | 114 | } |
@@ -161,4 +167,47 @@ public Authentication convert(HttpServletRequest request) { |
161 | 167 |
|
162 | 168 | } |
163 | 169 |
|
| 170 | + private static final class DPoPAuthenticationEntryPoint implements AuthenticationEntryPoint { |
| 171 | + |
| 172 | + @Override |
| 173 | + public void commence(HttpServletRequest request, HttpServletResponse response, |
| 174 | + AuthenticationException authenticationException) { |
| 175 | + Map<String, String> parameters = new LinkedHashMap<>(); |
| 176 | + if (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) { |
| 177 | + OAuth2Error error = oauth2AuthenticationException.getError(); |
| 178 | + parameters.put(OAuth2ParameterNames.ERROR, error.getErrorCode()); |
| 179 | + if (StringUtils.hasText(error.getDescription())) { |
| 180 | + parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription()); |
| 181 | + } |
| 182 | + if (StringUtils.hasText(error.getUri())) { |
| 183 | + parameters.put(OAuth2ParameterNames.ERROR_URI, error.getUri()); |
| 184 | + } |
| 185 | + } |
| 186 | + parameters.put("algs", |
| 187 | + JwsAlgorithms.RS256 + " " + JwsAlgorithms.RS384 + " " + JwsAlgorithms.RS512 + " " |
| 188 | + + JwsAlgorithms.PS256 + " " + JwsAlgorithms.PS384 + " " + JwsAlgorithms.PS512 + " " |
| 189 | + + JwsAlgorithms.ES256 + " " + JwsAlgorithms.ES384 + " " + JwsAlgorithms.ES512); |
| 190 | + String wwwAuthenticate = toWWWAuthenticateHeader(parameters); |
| 191 | + response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate); |
| 192 | + response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
| 193 | + } |
| 194 | + |
| 195 | + private static String toWWWAuthenticateHeader(Map<String, String> parameters) { |
| 196 | + StringBuilder wwwAuthenticate = new StringBuilder(); |
| 197 | + wwwAuthenticate.append(OAuth2AccessToken.TokenType.DPOP.getValue()); |
| 198 | + if (!parameters.isEmpty()) { |
| 199 | + wwwAuthenticate.append(" "); |
| 200 | + int i = 0; |
| 201 | + for (Map.Entry<String, String> entry : parameters.entrySet()) { |
| 202 | + wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); |
| 203 | + if (i++ != parameters.size() - 1) { |
| 204 | + wwwAuthenticate.append(", "); |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + return wwwAuthenticate.toString(); |
| 209 | + } |
| 210 | + |
| 211 | + } |
| 212 | + |
164 | 213 | } |
0 commit comments