24
24
import jakarta .servlet .http .HttpServletResponse ;
25
25
26
26
import org .springframework .core .log .LogMessage ;
27
+ import org .springframework .http .HttpStatus ;
28
+ import org .springframework .http .converter .HttpMessageConverter ;
29
+ import org .springframework .http .server .ServletServerHttpResponse ;
27
30
import org .springframework .security .authentication .AbstractAuthenticationToken ;
28
31
import org .springframework .security .authentication .AuthenticationDetailsSource ;
29
32
import org .springframework .security .authentication .AuthenticationManager ;
30
33
import org .springframework .security .core .Authentication ;
34
+ import org .springframework .security .core .AuthenticationException ;
31
35
import org .springframework .security .core .context .SecurityContext ;
32
36
import org .springframework .security .core .context .SecurityContextHolder ;
37
+ import org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
33
38
import org .springframework .security .oauth2 .core .OAuth2AuthenticationException ;
34
39
import org .springframework .security .oauth2 .core .OAuth2Error ;
35
40
import org .springframework .security .oauth2 .core .OAuth2ErrorCodes ;
41
+ import org .springframework .security .oauth2 .core .http .converter .OAuth2ErrorHttpMessageConverter ;
36
42
import org .springframework .security .oauth2 .server .authorization .authentication .ClientSecretAuthenticationProvider ;
37
43
import org .springframework .security .oauth2 .server .authorization .authentication .JwtClientAssertionAuthenticationProvider ;
38
- import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationException ;
39
44
import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
40
45
import org .springframework .security .oauth2 .server .authorization .authentication .PublicClientAuthenticationProvider ;
41
46
import org .springframework .security .oauth2 .server .authorization .authentication .X509ClientCertificateAuthenticationProvider ;
42
47
import org .springframework .security .oauth2 .server .authorization .web .authentication .ClientSecretBasicAuthenticationConverter ;
43
48
import org .springframework .security .oauth2 .server .authorization .web .authentication .ClientSecretPostAuthenticationConverter ;
44
49
import org .springframework .security .oauth2 .server .authorization .web .authentication .JwtClientAssertionAuthenticationConverter ;
45
- import org .springframework .security .oauth2 .server .authorization .web .authentication .OAuth2ClientAuthenticationFailureHandler ;
46
50
import org .springframework .security .oauth2 .server .authorization .web .authentication .PublicClientAuthenticationConverter ;
47
51
import org .springframework .security .oauth2 .server .authorization .web .authentication .X509ClientCertificateAuthenticationConverter ;
48
52
import org .springframework .security .web .authentication .AuthenticationConverter ;
49
53
import org .springframework .security .web .authentication .AuthenticationFailureHandler ;
50
54
import org .springframework .security .web .authentication .AuthenticationSuccessHandler ;
51
55
import org .springframework .security .web .authentication .DelegatingAuthenticationConverter ;
52
56
import org .springframework .security .web .authentication .WebAuthenticationDetailsSource ;
57
+ import org .springframework .security .web .authentication .www .BasicAuthenticationEntryPoint ;
53
58
import org .springframework .security .web .util .matcher .RequestMatcher ;
54
59
import org .springframework .util .Assert ;
55
60
import org .springframework .web .filter .OncePerRequestFilter ;
70
75
* @see ClientSecretAuthenticationProvider
71
76
* @see PublicClientAuthenticationConverter
72
77
* @see PublicClientAuthenticationProvider
73
- * @see OAuth2ClientAuthenticationFailureHandler
74
78
* @see <a target="_blank" href=
75
79
* "https://datatracker.ietf.org/doc/html/rfc6749#section-2.3">Section 2.3 Client
76
80
* Authentication</a>
@@ -84,13 +88,17 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
84
88
85
89
private final RequestMatcher requestMatcher ;
86
90
91
+ private final HttpMessageConverter <OAuth2Error > errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter ();
92
+
87
93
private final AuthenticationDetailsSource <HttpServletRequest , ?> authenticationDetailsSource = new WebAuthenticationDetailsSource ();
88
94
95
+ private final BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint ();
96
+
89
97
private AuthenticationConverter authenticationConverter ;
90
98
91
99
private AuthenticationSuccessHandler authenticationSuccessHandler = this ::onAuthenticationSuccess ;
92
100
93
- private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ClientAuthenticationFailureHandler () ;
101
+ private AuthenticationFailureHandler authenticationFailureHandler = this :: onAuthenticationFailure ;
94
102
95
103
/**
96
104
* Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided
@@ -106,6 +114,7 @@ public OAuth2ClientAuthenticationFilter(AuthenticationManager authenticationMana
106
114
Assert .notNull (requestMatcher , "requestMatcher cannot be null" );
107
115
this .authenticationManager = authenticationManager ;
108
116
this .requestMatcher = requestMatcher ;
117
+ this .basicAuthenticationEntryPoint .setRealmName ("default" );
109
118
// @formatter:off
110
119
this .authenticationConverter = new DelegatingAuthenticationConverter (
111
120
Arrays .asList (
@@ -129,16 +138,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
129
138
Authentication authenticationRequest = null ;
130
139
try {
131
140
authenticationRequest = this .authenticationConverter .convert (request );
132
- if (authenticationRequest == null ) {
133
- throw new OAuth2AuthenticationException (OAuth2ErrorCodes .INVALID_CLIENT );
134
- }
135
141
if (authenticationRequest instanceof AbstractAuthenticationToken authenticationToken ) {
136
142
authenticationToken .setDetails (this .authenticationDetailsSource .buildDetails (request ));
137
143
}
138
- validateClientIdentifier (authenticationRequest );
139
- Authentication authenticationResult = this .authenticationManager .authenticate (authenticationRequest );
140
- this .authenticationSuccessHandler .onAuthenticationSuccess (request , response , authenticationResult );
144
+ if (authenticationRequest != null ) {
145
+ validateClientIdentifier (authenticationRequest );
146
+ Authentication authenticationResult = this .authenticationManager .authenticate (authenticationRequest );
147
+ this .authenticationSuccessHandler .onAuthenticationSuccess (request , response , authenticationResult );
148
+ }
141
149
filterChain .doFilter (request , response );
150
+
142
151
}
143
152
catch (OAuth2AuthenticationException ex ) {
144
153
if (this .logger .isTraceEnabled ()) {
@@ -151,8 +160,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
151
160
else {
152
161
this .authenticationFailureHandler .onAuthenticationFailure (request , response , ex );
153
162
}
154
- }
155
163
164
+ }
156
165
}
157
166
158
167
/**
@@ -202,6 +211,35 @@ private void onAuthenticationSuccess(HttpServletRequest request, HttpServletResp
202
211
}
203
212
}
204
213
214
+ private void onAuthenticationFailure (HttpServletRequest request , HttpServletResponse response ,
215
+ AuthenticationException authenticationException ) throws IOException {
216
+
217
+ SecurityContextHolder .clearContext ();
218
+
219
+ if (authenticationException instanceof OAuth2ClientAuthenticationException clientAuthenticationException ) {
220
+ OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationException
221
+ .getClientAuthentication ();
222
+ if (ClientAuthenticationMethod .CLIENT_SECRET_BASIC
223
+ .equals (clientAuthentication .getClientAuthenticationMethod ())) {
224
+ this .basicAuthenticationEntryPoint .commence (request , response , authenticationException );
225
+ return ;
226
+ }
227
+ }
228
+
229
+ OAuth2Error error = ((OAuth2AuthenticationException ) authenticationException ).getError ();
230
+ ServletServerHttpResponse httpResponse = new ServletServerHttpResponse (response );
231
+ if (OAuth2ErrorCodes .INVALID_CLIENT .equals (error .getErrorCode ())) {
232
+ httpResponse .setStatusCode (HttpStatus .UNAUTHORIZED );
233
+ }
234
+ else {
235
+ httpResponse .setStatusCode (HttpStatus .BAD_REQUEST );
236
+ }
237
+ // We don't want to reveal too much information to the caller so just return the
238
+ // error code
239
+ OAuth2Error errorResponse = new OAuth2Error (error .getErrorCode ());
240
+ this .errorHttpResponseConverter .write (errorResponse , null , httpResponse );
241
+ }
242
+
205
243
private static void validateClientIdentifier (Authentication authentication ) {
206
244
if (!(authentication instanceof OAuth2ClientAuthenticationToken )) {
207
245
return ;
@@ -223,4 +261,21 @@ private static void validateClientIdentifier(Authentication authentication) {
223
261
}
224
262
}
225
263
264
+ private static final class OAuth2ClientAuthenticationException extends OAuth2AuthenticationException {
265
+
266
+ private final OAuth2ClientAuthenticationToken clientAuthentication ;
267
+
268
+ private OAuth2ClientAuthenticationException (OAuth2Error error , Throwable cause ,
269
+ OAuth2ClientAuthenticationToken clientAuthentication ) {
270
+ super (error , cause );
271
+ Assert .notNull (clientAuthentication , "clientAuthentication cannot be null" );
272
+ this .clientAuthentication = clientAuthentication ;
273
+ }
274
+
275
+ private OAuth2ClientAuthenticationToken getClientAuthentication () {
276
+ return this .clientAuthentication ;
277
+ }
278
+
279
+ }
280
+
226
281
}
0 commit comments