11package io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .converter .auth .endpoint ;
22
33import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .dao .EasyPlusAuthorizationConsentRepository ;
4+ import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .message .DefaultSecurityUserExceptionMessage ;
5+ import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .message .ISecurityUserExceptionMessageService ;
6+ import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .response .error .dto .EasyPlusErrorMessages ;
47import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .response .error .exception .EasyPlusOauth2AuthenticationException ;
58import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .serivce .persistence .authorization .OAuth2AuthorizationServiceImpl ;
6- import io .github .patternhelloworld .securityhelper .oauth2 .api .config .util .RequestOAuth2Distiller ;
9+ import io .github .patternhelloworld .securityhelper .oauth2 .api .config .util .EasyPlusOAuth2EndpointUtils ;
10+ import io .github .patternhelloworld .securityhelper .oauth2 .api .config .util .ErrorCodeConstants ;
711import jakarta .servlet .http .HttpServletRequest ;
812import lombok .RequiredArgsConstructor ;
9- import org .springframework .lang . Nullable ;
13+ import org .springframework .security . authentication . AnonymousAuthenticationToken ;
1014import org .springframework .security .core .Authentication ;
15+ import org .springframework .security .core .authority .AuthorityUtils ;
1116import org .springframework .security .core .context .SecurityContextHolder ;
1217import org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
1318import org .springframework .security .oauth2 .core .endpoint .OAuth2ParameterNames ;
19+ import org .springframework .security .oauth2 .core .oidc .OidcScopes ;
1420import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
1521import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
1622import org .springframework .security .oauth2 .server .authorization .client .RegisteredClient ;
1723import org .springframework .security .oauth2 .server .authorization .client .RegisteredClientRepository ;
1824import org .springframework .security .web .authentication .AuthenticationConverter ;
25+ import org .springframework .security .web .util .matcher .AndRequestMatcher ;
26+ import org .springframework .security .web .util .matcher .RequestMatcher ;
1927import org .springframework .util .MultiValueMap ;
2028import org .springframework .util .StringUtils ;
2129
@@ -28,98 +36,116 @@ public final class AuthorizationCodeAuthorizationRequestConverter implements Aut
2836 private final EasyPlusAuthorizationConsentRepository easyPlusAuthorizationConsentRepository ;
2937 private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService ;
3038
31- public void setClientAuthenticationContext (String clientId ) {
32- RegisteredClient registeredClient = registeredClientRepository .findByClientId (clientId );
33- if (registeredClient == null ) {
34- throw new IllegalArgumentException ("Invalid client ID" );
35- }
39+ private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService ;
3640
37- OAuth2ClientAuthenticationToken clientAuthenticationToken = new OAuth2ClientAuthenticationToken (
38- registeredClient ,
39- ClientAuthenticationMethod .CLIENT_SECRET_BASIC ,
40- null
41- );
4241
43- SecurityContextHolder .getContext ().setAuthentication (clientAuthenticationToken );
44- }
42+ private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken ("anonymous" ,
43+ "anonymousUser" , AuthorityUtils .createAuthorityList ("ROLE_ANONYMOUS" ));
44+
45+ private static final RequestMatcher OIDC_REQUEST_MATCHER = createOidcRequestMatcher ();
4546
47+ /*
48+ * Why is the validation check done here?
49+ * - Because if an OAuth2AuthenticationException is thrown in the CustomizedProvider,
50+ * Spring retries the process by replacing the CustomizedProvider with the OAuth2AuthorizationCodeRequestAuthenticationProvider.
51+ *
52+ * Where is OAuth2AuthorizationCodeRequestAuthenticationToken implemented?
53+ * - It is handled by "EasyPlusGrantAuthenticationToken" when calling "/oauth2/token"
54+ */
4655 @ Override
47- @ Nullable
4856 public Authentication convert (HttpServletRequest request ) {
49- if ("POST" .equalsIgnoreCase (request .getMethod ())) {
50- // TODO: Authorization Consent
51- } else if ("GET" .equalsIgnoreCase (request .getMethod ())) {
52- MultiValueMap <String , String > parameters = RequestOAuth2Distiller .getAuthorizationCodeSecurityAdditionalParameters (request );
5357
58+ if (!"GET" .equals (request .getMethod ()) && !OIDC_REQUEST_MATCHER .matches (request )) {
59+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD ));
60+ }
5461
62+ MultiValueMap <String , String > parameters = EasyPlusOAuth2EndpointUtils .getWebParameters (request );
5563
56- String clientId = parameters .getFirst (OAuth2ParameterNames .CLIENT_ID );
57- if (!StringUtils .hasText (clientId )) {
58- throw new EasyPlusOauth2AuthenticationException ("client_id missing" );
59- }
60- String redirectUri = parameters .getFirst (OAuth2ParameterNames .REDIRECT_URI );
61- if (!StringUtils .hasText (redirectUri )) {
62- throw new EasyPlusOauth2AuthenticationException ("redirect_uri missing" );
63- }
64+ String clientId = parameters .getFirst (OAuth2ParameterNames .CLIENT_ID );
65+ if (!StringUtils .hasText (clientId )) {
66+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService . getUserMessage ( DefaultSecurityUserExceptionMessage . AUTHENTICATION_CLIENT_ID_MISSING ) );
67+ }
68+ String redirectUri = parameters .getFirst (OAuth2ParameterNames .REDIRECT_URI );
69+ if (!StringUtils .hasText (redirectUri )) {
70+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService . getUserMessage ( DefaultSecurityUserExceptionMessage . AUTHENTICATION_REDIRECT_URI_MISSING ) );
71+ }
6472
65- // setClientAuthenticationContext from the client_id param
66- setClientAuthenticationContext (clientId );
67- Authentication clientPrincipal = SecurityContextHolder .getContext ().getAuthentication ();
73+ Map <String , Object > additionalParameters = new HashMap <>();
6874
75+ parameters .forEach ((key , value ) -> {
76+ additionalParameters .put (key , (value .size () == 1 ) ? value .get (0 ) : value .toArray (new String [0 ]));
77+ });
6978
70- RegisteredClient registeredClient = ((OAuth2ClientAuthenticationToken ) clientPrincipal ).getRegisteredClient ();
7179
72- // Check if the registered client is null
73- if (registeredClient == null ) {
74- throw new EasyPlusOauth2AuthenticationException ("Registered client is missing or invalid" );
75- }
76- // Check if the redirectUri is not in the registered redirect URIs
77- if (!registeredClient .getRedirectUris ().contains (redirectUri )) {
78- throw new EasyPlusOauth2AuthenticationException ("Invalid redirect_uri: " + redirectUri );
79- }
80+ String state = parameters .getFirst (OAuth2ParameterNames .STATE );
81+ if (!StringUtils .hasText (state )) {
82+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_STATE_MISSING ));
83+ }
8084
85+ Set <String > requestedScopes = new HashSet <>(parameters .getOrDefault (OAuth2ParameterNames .SCOPE , Collections .emptyList ()));
8186
82- Set <String > requestedScopes = new HashSet <>(parameters .getOrDefault (OAuth2ParameterNames .SCOPE , Collections .emptyList ()));
83- // Scopes from the request
84- Set <String > registeredScopes = registeredClient .getScopes (); // Scopes from the RegisteredClient
87+ Authentication principal = SecurityContextHolder .getContext ().getAuthentication ();
88+ if (principal == null ) {
89+ setClientAuthenticationContext (clientId );
90+ principal = SecurityContextHolder .getContext ().getAuthentication ();
91+ }
8592
86- if (!registeredScopes .containsAll (requestedScopes )) {
87- throw new EasyPlusOauth2AuthenticationException ("Invalid scopes: " + requestedScopes + ". Allowed scopes: " + registeredScopes );
88- }
93+ RegisteredClient registeredClient = ((OAuth2ClientAuthenticationToken ) principal ).getRegisteredClient ();
8994
90- Map <String , Object > additionalParameters = new HashMap <>();
95+ if (registeredClient == null ) {
96+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_REGISTERED_CLIENT_NOT_FOUND ));
97+ }
9198
92- parameters . forEach (( key , value ) -> {
93- additionalParameters . put ( key , ( value . size () == 1 ) ? value . get ( 0 ) : value . toArray ( new String [ 0 ] ));
94- });
99+ if (! registeredClient . getRedirectUris (). contains ( redirectUri )) {
100+ throw new EasyPlusOauth2AuthenticationException ( iSecurityUserExceptionMessageService . getUserMessage ( DefaultSecurityUserExceptionMessage . AUTHENTICATION_INVALID_REDIRECT_URI ));
101+ }
95102
96- String code = parameters .getFirst (OAuth2ParameterNames .CODE );
97- if (!StringUtils .hasText (code )) {
98- throw new EasyPlusOauth2AuthenticationException ("Authorization code missing in GET request" );
99- }
103+ Set <String > registeredScopes = registeredClient .getScopes (); // Scopes from the RegisteredClient
100104
101- return new OAuth2AuthorizationCodeAuthenticationToken (
102- code ,
103- clientPrincipal ,
104- redirectUri ,
105- additionalParameters
106- );
105+ if (!registeredScopes .containsAll (requestedScopes )) {
106+ throw new EasyPlusOauth2AuthenticationException (EasyPlusErrorMessages .builder ().userMessage (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_INVALID_REDIRECT_URI ))
107+ .message ("Invalid scopes: " + requestedScopes + ". Allowed scopes: " + registeredScopes ).build ());
108+ }
107109
108- } else {
109- throw new IllegalStateException ("Unsupported HTTP method: " + request .getMethod ());
110+ String code = parameters .getFirst (OAuth2ParameterNames .CODE );
111+ if (!StringUtils .hasText (code )) {
112+ throw new EasyPlusOauth2AuthenticationException (EasyPlusErrorMessages .builder ().userMessage (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_AUTHORIZATION_CODE_MISSING ))
113+ .errorCode (ErrorCodeConstants .REDIRECT_TO_LOGIN ).build ());
110114 }
111115
112- return null ;
113- // TODO: Authorization Consent
114- /* return new OAuth2AuthorizationCodeRequestAuthenticationToken(
115- parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI),
116- clientId,
117- clientPrincipal,
116+ return new OAuth2AuthorizationCodeAuthenticationToken (
117+ code ,
118+ principal ,
118119 redirectUri ,
119- state,
120- scopes,
121120 additionalParameters
122- );*/
121+ );
123122 }
123+
124+ private static RequestMatcher createOidcRequestMatcher () {
125+ RequestMatcher postMethodMatcher = (request ) -> "POST" .equals (request .getMethod ());
126+ RequestMatcher responseTypeParameterMatcher = (
127+ request ) -> request .getParameter (OAuth2ParameterNames .RESPONSE_TYPE ) != null ;
128+ RequestMatcher openidScopeMatcher = (request ) -> {
129+ String scope = request .getParameter (OAuth2ParameterNames .SCOPE );
130+ return StringUtils .hasText (scope ) && scope .contains (OidcScopes .OPENID );
131+ };
132+ return new AndRequestMatcher (postMethodMatcher , responseTypeParameterMatcher , openidScopeMatcher );
133+ }
134+
135+ public void setClientAuthenticationContext (String clientId ) {
136+ RegisteredClient registeredClient = registeredClientRepository .findByClientId (clientId );
137+ if (registeredClient == null ) {
138+ throw new IllegalArgumentException ("Invalid client ID" );
139+ }
140+
141+ OAuth2ClientAuthenticationToken clientAuthenticationToken = new OAuth2ClientAuthenticationToken (
142+ registeredClient ,
143+ ClientAuthenticationMethod .CLIENT_SECRET_BASIC ,
144+ null
145+ );
146+
147+ SecurityContextHolder .getContext ().setAuthentication (clientAuthenticationToken );
148+ }
149+
124150}
125151
0 commit comments