2424import  jakarta .servlet .http .HttpServletResponse ;
2525
2626import  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 ;
2730import  org .springframework .security .authentication .AbstractAuthenticationToken ;
2831import  org .springframework .security .authentication .AuthenticationDetailsSource ;
2932import  org .springframework .security .authentication .AuthenticationManager ;
3033import  org .springframework .security .core .Authentication ;
34+ import  org .springframework .security .core .AuthenticationException ;
3135import  org .springframework .security .core .context .SecurityContext ;
3236import  org .springframework .security .core .context .SecurityContextHolder ;
37+ import  org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
3338import  org .springframework .security .oauth2 .core .OAuth2AuthenticationException ;
3439import  org .springframework .security .oauth2 .core .OAuth2Error ;
3540import  org .springframework .security .oauth2 .core .OAuth2ErrorCodes ;
41+ import  org .springframework .security .oauth2 .core .http .converter .OAuth2ErrorHttpMessageConverter ;
3642import  org .springframework .security .oauth2 .server .authorization .authentication .ClientSecretAuthenticationProvider ;
3743import  org .springframework .security .oauth2 .server .authorization .authentication .JwtClientAssertionAuthenticationProvider ;
38- import  org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationException ;
3944import  org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
4045import  org .springframework .security .oauth2 .server .authorization .authentication .PublicClientAuthenticationProvider ;
4146import  org .springframework .security .oauth2 .server .authorization .authentication .X509ClientCertificateAuthenticationProvider ;
4247import  org .springframework .security .oauth2 .server .authorization .web .authentication .ClientSecretBasicAuthenticationConverter ;
4348import  org .springframework .security .oauth2 .server .authorization .web .authentication .ClientSecretPostAuthenticationConverter ;
4449import  org .springframework .security .oauth2 .server .authorization .web .authentication .JwtClientAssertionAuthenticationConverter ;
45- import  org .springframework .security .oauth2 .server .authorization .web .authentication .OAuth2ClientAuthenticationFailureHandler ;
4650import  org .springframework .security .oauth2 .server .authorization .web .authentication .PublicClientAuthenticationConverter ;
4751import  org .springframework .security .oauth2 .server .authorization .web .authentication .X509ClientCertificateAuthenticationConverter ;
4852import  org .springframework .security .web .authentication .AuthenticationConverter ;
4953import  org .springframework .security .web .authentication .AuthenticationFailureHandler ;
5054import  org .springframework .security .web .authentication .AuthenticationSuccessHandler ;
5155import  org .springframework .security .web .authentication .DelegatingAuthenticationConverter ;
5256import  org .springframework .security .web .authentication .WebAuthenticationDetailsSource ;
57+ import  org .springframework .security .web .authentication .www .BasicAuthenticationEntryPoint ;
5358import  org .springframework .security .web .util .matcher .RequestMatcher ;
5459import  org .springframework .util .Assert ;
5560import  org .springframework .web .filter .OncePerRequestFilter ;
7075 * @see ClientSecretAuthenticationProvider 
7176 * @see PublicClientAuthenticationConverter 
7277 * @see PublicClientAuthenticationProvider 
73-  * @see OAuth2ClientAuthenticationFailureHandler 
7478 * @see <a target="_blank" href= 
7579 * "https://datatracker.ietf.org/doc/html/rfc6749#section-2.3">Section 2.3 Client 
7680 * Authentication</a> 
@@ -84,13 +88,17 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
8488
8589	private  final  RequestMatcher  requestMatcher ;
8690
91+ 	private  final  HttpMessageConverter <OAuth2Error > errorHttpResponseConverter  = new  OAuth2ErrorHttpMessageConverter ();
92+ 
8793	private  final  AuthenticationDetailsSource <HttpServletRequest , ?> authenticationDetailsSource  = new  WebAuthenticationDetailsSource ();
8894
95+ 	private  final  BasicAuthenticationEntryPoint  basicAuthenticationEntryPoint  = new  BasicAuthenticationEntryPoint ();
96+ 
8997	private  AuthenticationConverter  authenticationConverter ;
9098
9199	private  AuthenticationSuccessHandler  authenticationSuccessHandler  = this ::onAuthenticationSuccess ;
92100
93- 	private  AuthenticationFailureHandler  authenticationFailureHandler  = new   OAuth2ClientAuthenticationFailureHandler () ;
101+ 	private  AuthenticationFailureHandler  authenticationFailureHandler  = this :: onAuthenticationFailure ;
94102
95103	/** 
96104	 * Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided 
@@ -106,6 +114,7 @@ public OAuth2ClientAuthenticationFilter(AuthenticationManager authenticationMana
106114		Assert .notNull (requestMatcher , "requestMatcher cannot be null" );
107115		this .authenticationManager  = authenticationManager ;
108116		this .requestMatcher  = requestMatcher ;
117+ 		this .basicAuthenticationEntryPoint .setRealmName ("default" );
109118		// @formatter:off 
110119		this .authenticationConverter  = new  DelegatingAuthenticationConverter (
111120				Arrays .asList (
@@ -129,16 +138,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
129138		Authentication  authenticationRequest  = null ;
130139		try  {
131140			authenticationRequest  = this .authenticationConverter .convert (request );
132- 			if  (authenticationRequest  == null ) {
133- 				throw  new  OAuth2AuthenticationException (OAuth2ErrorCodes .INVALID_CLIENT );
134- 			}
135141			if  (authenticationRequest  instanceof  AbstractAuthenticationToken  authenticationToken ) {
136142				authenticationToken .setDetails (this .authenticationDetailsSource .buildDetails (request ));
137143			}
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+ 			}
141149			filterChain .doFilter (request , response );
150+ 
142151		}
143152		catch  (OAuth2AuthenticationException  ex ) {
144153			if  (this .logger .isTraceEnabled ()) {
@@ -151,8 +160,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
151160			else  {
152161				this .authenticationFailureHandler .onAuthenticationFailure (request , response , ex );
153162			}
154- 		}
155163
164+ 		}
156165	}
157166
158167	/** 
@@ -202,6 +211,35 @@ private void onAuthenticationSuccess(HttpServletRequest request, HttpServletResp
202211		}
203212	}
204213
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+ 
205243	private  static  void  validateClientIdentifier (Authentication  authentication ) {
206244		if  (!(authentication  instanceof  OAuth2ClientAuthenticationToken )) {
207245			return ;
@@ -223,4 +261,21 @@ private static void validateClientIdentifier(Authentication authentication) {
223261		}
224262	}
225263
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+ 
226281}
0 commit comments