1
1
package io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .converter .auth .endpoint ;
2
2
3
3
import 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 ;
4
7
import io .github .patternhelloworld .securityhelper .oauth2 .api .config .security .response .error .exception .EasyPlusOauth2AuthenticationException ;
5
8
import 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 ;
7
11
import jakarta .servlet .http .HttpServletRequest ;
8
12
import lombok .RequiredArgsConstructor ;
9
- import org .springframework .lang . Nullable ;
13
+ import org .springframework .security . authentication . AnonymousAuthenticationToken ;
10
14
import org .springframework .security .core .Authentication ;
15
+ import org .springframework .security .core .authority .AuthorityUtils ;
11
16
import org .springframework .security .core .context .SecurityContextHolder ;
12
17
import org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
13
18
import org .springframework .security .oauth2 .core .endpoint .OAuth2ParameterNames ;
19
+ import org .springframework .security .oauth2 .core .oidc .OidcScopes ;
14
20
import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
15
21
import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
16
22
import org .springframework .security .oauth2 .server .authorization .client .RegisteredClient ;
17
23
import org .springframework .security .oauth2 .server .authorization .client .RegisteredClientRepository ;
18
24
import org .springframework .security .web .authentication .AuthenticationConverter ;
25
+ import org .springframework .security .web .util .matcher .AndRequestMatcher ;
26
+ import org .springframework .security .web .util .matcher .RequestMatcher ;
19
27
import org .springframework .util .MultiValueMap ;
20
28
import org .springframework .util .StringUtils ;
21
29
@@ -28,98 +36,116 @@ public final class AuthorizationCodeAuthorizationRequestConverter implements Aut
28
36
private final EasyPlusAuthorizationConsentRepository easyPlusAuthorizationConsentRepository ;
29
37
private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService ;
30
38
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 ;
36
40
37
- OAuth2ClientAuthenticationToken clientAuthenticationToken = new OAuth2ClientAuthenticationToken (
38
- registeredClient ,
39
- ClientAuthenticationMethod .CLIENT_SECRET_BASIC ,
40
- null
41
- );
42
41
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 ();
45
46
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
+ */
46
55
@ Override
47
- @ Nullable
48
56
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 );
53
57
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
+ }
54
61
62
+ MultiValueMap <String , String > parameters = EasyPlusOAuth2EndpointUtils .getWebParameters (request );
55
63
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
+ }
64
72
65
- // setClientAuthenticationContext from the client_id param
66
- setClientAuthenticationContext (clientId );
67
- Authentication clientPrincipal = SecurityContextHolder .getContext ().getAuthentication ();
73
+ Map <String , Object > additionalParameters = new HashMap <>();
68
74
75
+ parameters .forEach ((key , value ) -> {
76
+ additionalParameters .put (key , (value .size () == 1 ) ? value .get (0 ) : value .toArray (new String [0 ]));
77
+ });
69
78
70
- RegisteredClient registeredClient = ((OAuth2ClientAuthenticationToken ) clientPrincipal ).getRegisteredClient ();
71
79
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
+ }
80
84
85
+ Set <String > requestedScopes = new HashSet <>(parameters .getOrDefault (OAuth2ParameterNames .SCOPE , Collections .emptyList ()));
81
86
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
+ }
85
92
86
- if (!registeredScopes .containsAll (requestedScopes )) {
87
- throw new EasyPlusOauth2AuthenticationException ("Invalid scopes: " + requestedScopes + ". Allowed scopes: " + registeredScopes );
88
- }
93
+ RegisteredClient registeredClient = ((OAuth2ClientAuthenticationToken ) principal ).getRegisteredClient ();
89
94
90
- Map <String , Object > additionalParameters = new HashMap <>();
95
+ if (registeredClient == null ) {
96
+ throw new EasyPlusOauth2AuthenticationException (iSecurityUserExceptionMessageService .getUserMessage (DefaultSecurityUserExceptionMessage .AUTHENTICATION_REGISTERED_CLIENT_NOT_FOUND ));
97
+ }
91
98
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
+ }
95
102
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
100
104
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
+ }
107
109
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 ());
110
114
}
111
115
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 ,
118
119
redirectUri ,
119
- state,
120
- scopes,
121
120
additionalParameters
122
- );*/
121
+ );
123
122
}
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
+
124
150
}
125
151
0 commit comments