78
78
import java .util .Map ;
79
79
import java .util .Optional ;
80
80
import java .util .Set ;
81
+ import java .util .UUID ;
81
82
import java .util .regex .Matcher ;
82
83
import java .util .regex .Pattern ;
83
84
import java .util .stream .Collectors ;
84
85
85
86
import javax .servlet .http .HttpServletRequest ;
86
87
import javax .servlet .http .HttpServletResponse ;
87
88
89
+ import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .AUTHENTICATOR_APPLE ;
90
+ import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .COMMA_SEPARATOR ;
88
91
import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .LogConstants .ActionIDs .PROCESS_AUTHENTICATION_RESPONSE ;
89
92
import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .LogConstants .ActionIDs .VALIDATE_OUTBOUND_AUTH_REQUEST ;
93
+ import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .PLUS_SEPARATOR ;
94
+ import static org .wso2 .carbon .identity .application .authenticator .apple .AppleAuthenticatorConstants .SPACE_SEPARATOR ;
95
+ import static org .wso2 .carbon .identity .application .authenticator .oidc .OIDCAuthenticatorConstants .Claim .NONCE ;
96
+ import static org .wso2 .carbon .identity .application .authenticator .oidc .OIDCAuthenticatorConstants .OIDC_FEDERATION_NONCE ;
97
+ import static org .wso2 .carbon .identity .application .authenticator .oidc .OIDCAuthenticatorConstants .REDIRECT_URL_SUFFIX ;
98
+ import static org .wso2 .carbon .identity .application .authenticator .oidc .OIDCAuthenticatorConstants .SCOPE_PARAM_SUFFIX ;
99
+ import static org .wso2 .carbon .identity .application .authenticator .oidc .OIDCAuthenticatorConstants .STATE_PARAM_SUFFIX ;
90
100
import static org .wso2 .carbon .identity .base .IdentityConstants .FEDERATED_IDP_SESSION_ID ;
91
101
92
102
/**
@@ -144,9 +154,13 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
144
154
145
155
String clientId = authenticatorProperties .get (OIDCAuthenticatorConstants .CLIENT_ID );
146
156
String authorizationEP = getAuthorizationServerEndpoint (authenticatorProperties );
147
- String callbackUrl = getCallbackUrl (authenticatorProperties );
148
- String state = getStateParameter (context , authenticatorProperties );
157
+ String callbackUrl = getCallbackUrl (authenticatorProperties , context );
158
+ String state = getStateParameter (request , context , authenticatorProperties );
159
+ context .setProperty (getName () + STATE_PARAM_SUFFIX , state );
160
+ String nonce = UUID .randomUUID ().toString ();
161
+ context .setProperty (getName () + OIDC_FEDERATION_NONCE , nonce );
149
162
String scopes = getScope (authenticatorProperties );
163
+ context .setProperty (getName () + SCOPE_PARAM_SUFFIX , scopes );
150
164
String queryString = getQueryString (authenticatorProperties );
151
165
152
166
// If scopes are present, Apple requires sending response_mode as form_post.
@@ -174,7 +188,8 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
174
188
OAuthClientRequest .AuthenticationRequestBuilder requestBuilder =
175
189
OAuthClientRequest .authorizationLocation (authorizationEP ).setClientId (clientId )
176
190
.setResponseType (OIDCAuthenticatorConstants .OAUTH2_GRANT_TYPE_CODE )
177
- .setState (state );
191
+ .setState (state )
192
+ .setParameter (NONCE , nonce );
178
193
if (LoggerUtils .isDiagnosticLogsEnabled () && diagnosticLogBuilder != null ) {
179
194
diagnosticLogBuilder .inputParam ("scopes" , scope );
180
195
}
@@ -200,6 +215,7 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
200
215
loginPage = loginPage + queryString ;
201
216
}
202
217
}
218
+ context .setProperty (getName () + REDIRECT_URL_SUFFIX , loginPage );
203
219
response .sendRedirect (response .encodeRedirectURL (loginPage .replace ("\r \n " , "" )));
204
220
if (LoggerUtils .isDiagnosticLogsEnabled () && diagnosticLogBuilder != null ) {
205
221
diagnosticLogBuilder .resultMessage ("Redirected to the Apple Id login page." );
@@ -341,6 +357,21 @@ protected void processAuthenticationResponse(HttpServletRequest request, HttpSer
341
357
}
342
358
}
343
359
360
+ // Nonce validation.
361
+ String nonceKey = getName () + OIDC_FEDERATION_NONCE ;
362
+ if (StringUtils .isNotBlank ((String ) context .getProperty (nonceKey ))) {
363
+ String nonce = (String ) jwtAttributeMap .get (NONCE );
364
+ if (nonce == null ) {
365
+ log .debug ("OIDC provider does not support nonce claim in id_token." );
366
+ }
367
+ if (nonce != null && !nonce .equals (context .getProperty (nonceKey ))) {
368
+ setAuthenticatorMessageToContext (OIDCErrorConstants .ErrorMessages .NONCE_MISMATCH , context );
369
+
370
+ throw new AuthenticationFailedException (OIDCErrorConstants .ErrorMessages .NONCE_MISMATCH .getCode (),
371
+ OIDCErrorConstants .ErrorMessages .NONCE_MISMATCH .getMessage ());
372
+ }
373
+ }
374
+
344
375
String authenticatedUserId = getAuthenticatedUserId (context , oAuthResponse , jwtAttributeMap );
345
376
String attributeSeparator = getMultiAttributeSeparator (context , authenticatedUserId );
346
377
@@ -636,6 +667,44 @@ protected String getComponentId() {
636
667
return AppleAuthenticatorConstants .LogConstants .OUTBOUND_AUTH_APPLE_SERVICE ;
637
668
}
638
669
670
+ /**
671
+ * Get the i18n key defined to represent the authenticator name.
672
+ *
673
+ * @return the 118n key.
674
+ */
675
+ @ Override
676
+ public String getI18nKey () {
677
+
678
+ return AUTHENTICATOR_APPLE ;
679
+ }
680
+
681
+ /**
682
+ * This method is responsible for validating whether the authenticator is supported for API Based Authentication.
683
+ *
684
+ * @return true if the authenticator is supported for API Based Authentication.
685
+ */
686
+ @ Override
687
+ public boolean isAPIBasedAuthenticationSupported () {
688
+
689
+ return true ;
690
+ }
691
+
692
+ /**
693
+ * This method is responsible to return the scopes as space separated string.
694
+ *
695
+ * @param authenticatorProperties Authenticator properties.
696
+ * @return Scopes string
697
+ */
698
+ @ Override
699
+ protected String getScope (Map <String , String > authenticatorProperties ) {
700
+
701
+ String scopes = authenticatorProperties .get (IdentityApplicationConstants .Authenticator .OIDC .SCOPES );
702
+ if (StringUtils .isNotBlank (scopes )) {
703
+ return scopes .replace (COMMA_SEPARATOR , SPACE_SEPARATOR ).replace (PLUS_SEPARATOR , SPACE_SEPARATOR );
704
+ }
705
+ return scopes ;
706
+ }
707
+
639
708
/**
640
709
* Evaluates if the client secret should be generated and if required, generate and store the secret.
641
710
*
@@ -811,13 +880,20 @@ private void initTokenEndpoint() {
811
880
/**
812
881
* Get state parameter.
813
882
*
883
+ * @param request Authentication request.
814
884
* @param context Authentication context.
815
885
* @param authenticatorProperties Authenticator properties.
816
886
* @return State.
817
887
*/
818
- private String getStateParameter (AuthenticationContext context , Map <String , String > authenticatorProperties ) {
888
+ private String getStateParameter (HttpServletRequest request , AuthenticationContext context ,
889
+ Map <String , String > authenticatorProperties ) {
819
890
820
- String state = context .getContextIdentifier () + "," + OIDCAuthenticatorConstants .LOGIN_TYPE ;
891
+ String state ;
892
+ if (FrameworkUtils .isAPIBasedAuthenticationFlow (request )) {
893
+ state = UUID .randomUUID () + "," + OIDCAuthenticatorConstants .LOGIN_TYPE ;
894
+ } else {
895
+ state = context .getContextIdentifier () + "," + OIDCAuthenticatorConstants .LOGIN_TYPE ;
896
+ }
821
897
return getState (state , authenticatorProperties );
822
898
}
823
899
@@ -1014,22 +1090,6 @@ private String getMultiAttributeSeparator(AuthenticationContext context, String
1014
1090
return attributeSeparator ;
1015
1091
}
1016
1092
1017
- /**
1018
- * Get application details from the authentication context.
1019
- * @param context Authentication context.
1020
- * @return Map of application details.
1021
- */
1022
- private Map <String , String > getApplicationDetails (AuthenticationContext context ) {
1023
-
1024
- Map <String , String > applicationDetailsMap = new HashMap <>();
1025
- FrameworkUtils .getApplicationResourceId (context ).ifPresent (applicationId ->
1026
- applicationDetailsMap .put (LogConstants .InputKeys .APPLICATION_ID , applicationId ));
1027
- FrameworkUtils .getApplicationName (context ).ifPresent (applicationName ->
1028
- applicationDetailsMap .put (LogConstants .InputKeys .APPLICATION_NAME ,
1029
- applicationName ));
1030
- return applicationDetailsMap ;
1031
- }
1032
-
1033
1093
private static List <String > getUserAttributeClaimMappingList (AuthenticatedUser authenticatedUser ) {
1034
1094
1035
1095
return authenticatedUser .getUserAttributes ().keySet ().stream ()
0 commit comments