3939use Symfony \Component \HttpFoundation \RequestStack ;
4040use Symfony \Component \HttpFoundation \Session \SessionInterface ;
4141use Exception ;
42+ use Surfnet \AzureMfa \Domain \Institution \ValueObject \Institution ;
43+ use Surfnet \AzureMfa \Domain \Institution \ValueObject \IdentityProviderInterface ;
4244
4345/**
4446 * @SuppressWarnings("PHPMD.CouplingBetweenObjects")
@@ -182,36 +184,61 @@ public function handleResponse(Request $request): User
182184 $ user = $ this ->session ->get ('user ' );
183185 assert ($ user instanceof User);
184186
185- // Retrieve its institution and identity provider
186- $ this ->logger ->info ('Match the user email address ' .$ user ->getEmailAddress ().'to one of the registered institutions ' );
187+ $ this ->logger ->info ('Match the user email address ' .$ user ->getEmailAddress ().' to one of the registered institutions ' );
187188 $ institution = $ this ->matchingService ->findInstitutionByEmail ($ user ->getEmailAddress ());
188189 if (is_null ($ institution )) {
189190 throw new AzureADException ('The Institution could not be found using the users mail address ' );
190191 }
192+
191193 $ azureMfaIdentityProvider = $ this ->identityProviderCache ->build ($ institution ->getName ());
194+ $ assertion = $ this ->processResponseWithOptionalMetadataRefresh (
195+ $ request ,
196+ $ azureMfaIdentityProvider ,
197+ $ institution
198+ );
199+
200+ $ this ->validateResponseAttributes ($ assertion ->getAttributes (), $ user , $ institution , $ azureMfaIdentityProvider );
201+
202+ $ this ->logger ->info (sprintf (
203+ 'The mail attribute in the response %s matched the email address of the registering/authenticating user: %s ' ,
204+ $ assertion ->getAttributes ()[self ::SAML_EMAIL_ATTRIBUTE ],
205+ $ user ->getEmailAddress ()->getEmailAddress ()
206+ ));
192207
208+ return $ user ;
209+ }
210+
211+ private function processResponseWithOptionalMetadataRefresh (
212+ Request $ request ,
213+ IdentityProviderInterface $ azureMfaIdentityProvider ,
214+ Institution $ institution
215+ ): mixed {
193216 try {
194217 $ this ->logger ->info ('Process the SAML Response ' );
195- $ assertion = $ this ->postBinding ->processResponse (
218+
219+ return $ this ->postBinding ->processResponse (
196220 $ request ,
197221 $ azureMfaIdentityProvider ,
198222 $ this ->serviceProvider
199223 );
200224 } catch (Exception $ e ) {
201- // If the signature validation fails, we try to rebuild the identity provider
202- // This will effectively refresh the metadata for the institution
203- // Todo: refactor this to be more robust, e.g. by checking the exception type
204225 if (!$ institution ->supportsMetadataUpdate () || $ e ->getMessage () !== 'Unable to validate Signature ' ) {
205226 throw $ e ;
206227 }
207228
208- $ this ->logger ->info (sprintf ('Unable to validate signature refreshing metadata for %s ' , $ institution ->getName ()->getInstitutionName ()));
229+ $ this ->logger ->info (sprintf (
230+ 'Unable to validate signature refreshing metadata for %s ' ,
231+ $ institution ->getName ()->getInstitutionName ()
232+ ));
209233
210234 $ azureMfaIdentityProvider = $ this ->identityProviderCache ->rebuild ($ institution ->getName ());
211235
212- $ this ->logger ->info ('Reprocess the SAML Response from ' .$ institution ->getName ()->getInstitutionName ().' after updating the metadata ' );
236+ $ this ->logger ->info (
237+ 'Reprocess the SAML Response from ' .$ institution ->getName ()->getInstitutionName ().' after updating the metadata '
238+ );
239+
213240 try {
214- $ assertion = $ this ->postBinding ->processResponse (
241+ return $ this ->postBinding ->processResponse (
215242 $ request ,
216243 $ azureMfaIdentityProvider ,
217244 $ this ->serviceProvider
@@ -224,12 +251,14 @@ public function handleResponse(Request $request): User
224251 throw $ retryException ;
225252 }
226253 }
254+ }
227255
228- $ attributes = $ assertion ->getAttributes ();
229-
230- // If the IDP was an AzureAD endpoint (the entityID or Issuer starts with https://login.microsoftonline.com/, or preferably an config parameter in institutions.yaml)
231- // the SAML response attribute 'http://schemas.microsoft.com/claims/authnmethodsreferences'
232- // should contain 'http://schemas.microsoft.com/claims/multipleauthn'
256+ private function validateResponseAttributes (
257+ array $ attributes ,
258+ User $ user ,
259+ Institution $ institution ,
260+ IdentityProviderInterface $ azureMfaIdentityProvider
261+ ): void {
233262 if ($ azureMfaIdentityProvider ->isAzureAD ()) {
234263 $ this ->logger ->info ($ institution ->getName ()->getInstitutionName ().' is an AzureAD IdP. Validating authnmethodsreferences in the response. ' );
235264 if (!isset ($ attributes ['http://schemas.microsoft.com/claims/authnmethodsreferences ' ]) || !in_array ('http://schemas.microsoft.com/claims/multipleauthn ' , $ attributes ['http://schemas.microsoft.com/claims/authnmethodsreferences ' ])) {
@@ -241,7 +270,7 @@ public function handleResponse(Request $request): User
241270
242271 if (!isset ($ attributes [self ::SAML_EMAIL_ATTRIBUTE ])) {
243272 throw new MissingMailAttributeException (
244- 'The mail attribute in the Azure MFA assertion from ' .$ institution ->getName ()->getInstitutionName (). ' was missing '
273+ 'The mail attribute in the Azure MFA assertion from ' .$ institution ->getName ()->getInstitutionName ().' was missing '
245274 );
246275 }
247276
@@ -253,12 +282,5 @@ public function handleResponse(Request $request): User
253282 strtolower ($ user ->getEmailAddress ()->getEmailAddress ())
254283 ));
255284 }
256-
257- $ this ->logger ->info (sprintf (
258- 'The mail attribute in the response %s matched the email address of the registering/authenticating user: %s ' ,
259- $ attributes [self ::SAML_EMAIL_ATTRIBUTE ],
260- $ user ->getEmailAddress ()->getEmailAddress ()
261- ));
262- return $ user ;
263285 }
264286}
0 commit comments