Completed
Push — feature/168109427-remove-u2f-s... ( ac207a )
by
unknown
01:42
created

SecondFactorController   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 504
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 26

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 26
dl 0
loc 504
rs 9.36
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
B selectSecondFactorForVerificationAction() 0 72 5
C chooseSecondFactorAction() 0 102 8
A verifyGssfAction() 0 35 2
A gssfVerifiedAction() 0 32 2
A verifyYubiKeySecondFactorAction() 0 49 4
B verifySmsSecondFactorAction() 0 55 3
B verifySmsSecondFactorChallengeAction() 0 49 5
A cancelAuthenticationAction() 0 4 1
A getStepupService() 0 4 1
A getResponseContext() 0 4 1
A getAuthenticationLogger() 0 4 1
A getSelectedSecondFactor() 0 12 2
A selectAndRedirectTo() 0 18 2
A buildCancelAuthenticationForm() 0 10 1
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupGateway\GatewayBundle\Controller;
20
21
use Psr\Log\LoggerInterface;
22
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
23
use Surfnet\StepupBundle\Command\VerifyPossessionOfPhoneCommand;
24
use Surfnet\StepupBundle\Value\PhoneNumber\InternationalPhoneNumber;
25
use Surfnet\StepupBundle\Value\SecondFactorType;
26
use Surfnet\StepupGateway\GatewayBundle\Command\ChooseSecondFactorCommand;
27
use Surfnet\StepupGateway\GatewayBundle\Command\SendSmsChallengeCommand;
28
use Surfnet\StepupGateway\GatewayBundle\Command\VerifyYubikeyOtpCommand;
29
use Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor;
30
use Surfnet\StepupGateway\GatewayBundle\Exception\InvalidArgumentException;
31
use Surfnet\StepupGateway\GatewayBundle\Exception\LoaCannotBeGivenException;
32
use Surfnet\StepupGateway\GatewayBundle\Exception\RuntimeException;
33
use Surfnet\StepupGateway\GatewayBundle\Form\Type\CancelAuthenticationType;
34
use Surfnet\StepupGateway\GatewayBundle\Form\Type\ChooseSecondFactorType;
35
use Surfnet\StepupGateway\GatewayBundle\Form\Type\SendSmsChallengeType;
36
use Surfnet\StepupGateway\GatewayBundle\Form\Type\VerifySmsChallengeType;
37
use Surfnet\StepupGateway\GatewayBundle\Form\Type\VerifyYubikeyOtpType;
38
use Surfnet\StepupGateway\GatewayBundle\Saml\ResponseContext;
39
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
40
use Symfony\Component\Form\Form;
41
use Symfony\Component\Form\FormError;
42
use Symfony\Component\HttpFoundation\RedirectResponse;
43
use Symfony\Component\HttpFoundation\Request;
44
use Symfony\Component\HttpFoundation\Response;
45
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
46
47
/**
48
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
49
 */
50
class SecondFactorController extends Controller
51
{
52
    public function selectSecondFactorForVerificationAction()
53
    {
54
        $context = $this->getResponseContext();
55
        $originalRequestId = $context->getInResponseTo();
56
57
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
58
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
59
        $logger->notice('Determining which second factor to use...');
60
61
        try {
62
            // Retrieve all requirements to determine the required LoA
63
            $requestedLoa = $context->getRequiredLoa();
64
            $spConfiguredLoas = $context->getServiceProvider()->get('configuredLoas');
65
66
            $normalizedIdpSho = $context->getNormalizedSchacHomeOrganization();
67
            $normalizedUserSho = $this->getStepupService()->getNormalizedUserShoByIdentityNameId($context->getIdentityNameId());
68
69
            $requiredLoa = $this
70
                ->getStepupService()
71
                ->resolveHighestRequiredLoa(
72
                    $requestedLoa,
73
                    $spConfiguredLoas,
74
                    $normalizedIdpSho,
75
                    $normalizedUserSho
76
                );
77
        } catch (LoaCannotBeGivenException $e) {
78
            // Log the message of the domain exception, this contains a meaningful message.
79
            $logger->notice($e->getMessage());
80
            return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven');
81
        }
82
83
        $logger->notice(sprintf('Determined that the required Loa is "%s"', $requiredLoa));
84
85
        if ($this->getStepupService()->isIntrinsicLoa($requiredLoa)) {
86
            $this->get('gateway.authentication_logger')->logIntrinsicLoaAuthentication($originalRequestId);
87
88
            return $this->forward($context->getResponseAction());
89
        }
90
91
        $secondFactorCollection = $this
92
            ->getStepupService()
93
            ->determineViableSecondFactors(
94
                $context->getIdentityNameId(),
95
                $requiredLoa,
96
                $this->get('gateway.service.whitelist')
97
            );
98
99
        switch (count($secondFactorCollection)) {
100
            case 0:
101
                $logger->notice('No second factors can give the determined Loa');
102
                return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven');
103
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
104
105
            case 1:
106
                $secondFactor = $secondFactorCollection->first();
107
                $logger->notice(sprintf(
108
                    'Found "%d" second factors, using second factor of type "%s"',
109
                    count($secondFactorCollection),
110
                    $secondFactor->secondFactorType
111
                ));
112
113
                return $this->selectAndRedirectTo($secondFactor, $context);
114
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
115
116
            default:
117
                return $this->forward(
118
                    'SurfnetStepupGatewayGatewayBundle:SecondFactor:chooseSecondFactor',
119
                    ['secondFactors' => $secondFactorCollection]
120
                );
121
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
122
        }
123
    }
124
125
    /**
126
     * @Template
127
     * @param Request $request
128
     * @return array|RedirectResponse|Response
129
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
130
     */
131
    public function chooseSecondFactorAction(Request $request)
132
    {
133
        $context = $this->getResponseContext();
134
        $originalRequestId = $context->getInResponseTo();
135
136
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
137
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
138
        $logger->notice('Ask the user which one of his suitable second factor tokens to use...');
139
140
        try {
141
            // Retrieve all requirements to determine the required LoA
142
            $requestedLoa = $context->getRequiredLoa();
143
            $spConfiguredLoas = $context->getServiceProvider()->get('configuredLoas');
144
145
            $normalizedIdpSho = $context->getNormalizedSchacHomeOrganization();
146
            $normalizedUserSho = $this->getStepupService()->getNormalizedUserShoByIdentityNameId($context->getIdentityNameId());
147
148
            $requiredLoa = $this
149
                ->getStepupService()
150
                ->resolveHighestRequiredLoa(
151
                    $requestedLoa,
152
                    $spConfiguredLoas,
153
                    $normalizedIdpSho,
154
                    $normalizedUserSho
155
                );
156
        } catch (LoaCannotBeGivenException $e) {
157
            // Log the message of the domain exception, this contains a meaningful message.
158
            $logger->notice($e->getMessage());
159
            return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven');
160
        }
161
162
        $logger->notice(sprintf('Determined that the required Loa is "%s"', $requiredLoa));
163
164
        $secondFactors = $this
165
            ->getStepupService()
166
            ->determineViableSecondFactors(
167
                $context->getIdentityNameId(),
168
                $requiredLoa,
169
                $this->get('gateway.service.whitelist')
170
            );
171
172
        $command = new ChooseSecondFactorCommand();
173
        $command->secondFactors = $secondFactors;
0 ignored issues
show
Documentation Bug introduced by
It seems like $secondFactors of type object<Doctrine\Common\Collections\Collection> is incompatible with the declared type array<integer,object<Sur...e\Entity\SecondFactor>> of property $secondFactors.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
174
175
        $form = $this
176
            ->createForm(
177
                ChooseSecondFactorType::class,
178
                $command,
179
                ['action' => $this->generateUrl('gateway_verify_second_factor_choose_second_factor')]
180
            )
181
            ->handleRequest($request);
182
        $cancelForm = $this->buildCancelAuthenticationForm()->handleRequest($request);
183
184
        if ($form->isSubmitted() && $form->isValid()) {
185
            $buttonName = $form->getClickedButton()->getName();
186
            $formResults = $request->request->get('gateway_choose_second_factor', false);
187
188
            if (!isset($formResults[$buttonName])) {
189
                throw new InvalidArgumentException(
190
                    sprintf(
191
                        'Second factor type "%s" could not be found in the posted form results.',
192
                        $buttonName
193
                    )
194
                );
195
            }
196
197
            $secondFactorType = $formResults[$buttonName];
198
199
            // Filter the selected second factor from the array collection
200
            $secondFactorFiltered = $secondFactors->filter(
201
                function ($secondFactor) use ($secondFactorType) {
202
                    return $secondFactorType === $secondFactor->secondFactorType;
203
                }
204
            );
205
206
            if ($secondFactorFiltered->isEmpty()) {
207
                throw new InvalidArgumentException(
208
                    sprintf(
209
                        'Second factor type "%s" could not be found in the collection of available second factors.',
210
                        $secondFactorType
211
                    )
212
                );
213
            }
214
215
            $secondFactor = $secondFactorFiltered->first();
216
217
            $logger->notice(sprintf('User chose "%s" to use as second factor', $secondFactorType));
218
219
            // Forward to action to verify possession of second factor
220
            return $this->selectAndRedirectTo($secondFactor, $context);
221
        } else {
222
            if ($form->isSubmitted() && !$form->isValid()) {
223
                $form->addError(new FormError('gateway.form.gateway_choose_second_factor.unknown_second_factor_type'));
224
            }
225
        }
226
227
        return [
228
            'form' => $form->createView(),
229
            'cancelForm' => $cancelForm->createView(),
230
            'secondFactors' => $secondFactors,
231
        ];
232
    }
233
234
    public function verifyGssfAction()
235
    {
236
        $context = $this->getResponseContext();
237
        $originalRequestId = $context->getInResponseTo();
238
239
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
240
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
241
        $logger->info('Received request to verify GSSF');
242
243
        $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);
244
245
        $logger->info(sprintf(
246
            'Selected GSSF "%s" for verfication, forwarding to Saml handling',
247
            $selectedSecondFactor
248
        ));
249
250
        /** @var \Surfnet\StepupGateway\GatewayBundle\Service\SecondFactorService $secondFactorService */
251
        $secondFactorService = $this->get('gateway.service.second_factor_service');
252
        /** @var \Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor $secondFactor */
253
        $secondFactor = $secondFactorService->findByUuid($selectedSecondFactor);
254
        if (!$secondFactor) {
255
            throw new RuntimeException(sprintf(
256
                'Requested verification of GSSF "%s", however that Second Factor no longer exists',
257
                $selectedSecondFactor
258
            ));
259
        }
260
261
        return $this->forward(
262
            'SurfnetStepupGatewaySamlStepupProviderBundle:SamlProxy:sendSecondFactorVerificationAuthnRequest',
263
            [
264
                'provider' => $secondFactor->secondFactorType,
265
                'subjectNameId' => $secondFactor->secondFactorIdentifier
266
            ]
267
        );
268
    }
269
270
    public function gssfVerifiedAction()
271
    {
272
        $context = $this->getResponseContext();
273
        $originalRequestId = $context->getInResponseTo();
274
275
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
276
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
277
        $logger->info('Attempting to mark GSSF as verified');
278
279
        $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);
280
281
        /** @var \Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor $secondFactor */
282
        $secondFactor = $this->get('gateway.service.second_factor_service')->findByUuid($selectedSecondFactor);
283
        if (!$secondFactor) {
284
            throw new RuntimeException(
285
                sprintf(
286
                    'Verification of GSSF "%s" succeeded, however that Second Factor no longer exists',
287
                    $selectedSecondFactor
288
                )
289
            );
290
        }
291
292
        $context->markSecondFactorVerified();
293
        $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId);
294
295
        $logger->info(sprintf(
296
            'Marked GSSF "%s" as verified, forwarding to Gateway controller to respond',
297
            $selectedSecondFactor
298
        ));
299
300
        return $this->forward($context->getResponseAction());
301
    }
302
303
    /**
304
     * @Template
305
     * @param Request $request
306
     * @return array|Response
307
     */
308
    public function verifyYubiKeySecondFactorAction(Request $request)
309
    {
310
        $context = $this->getResponseContext();
311
        $originalRequestId = $context->getInResponseTo();
312
313
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
314
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
315
316
        $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);
317
318
        $logger->notice('Verifying possession of Yubikey second factor');
319
320
        $command = new VerifyYubikeyOtpCommand();
321
        $command->secondFactorId = $selectedSecondFactor;
322
323
        $form = $this->createForm(VerifyYubikeyOtpType::class, $command)->handleRequest($request);
324
        $cancelForm = $this->buildCancelAuthenticationForm()->handleRequest($request);
325
326
        if (!$form->isValid()) {
327
            // OTP field is rendered empty in the template.
328
            return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];
329
        }
330
331
        $result = $this->getStepupService()->verifyYubikeyOtp($command);
332
333
        if ($result->didOtpVerificationFail()) {
334
            $form->addError(new FormError('gateway.form.verify_yubikey.otp_verification_failed'));
335
336
            // OTP field is rendered empty in the template.
337
            return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];
338
        } elseif (!$result->didPublicIdMatch()) {
339
            $form->addError(new FormError('gateway.form.verify_yubikey.public_id_mismatch'));
340
341
            // OTP field is rendered empty in the template.
342
            return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];
343
        }
344
345
        $this->getResponseContext()->markSecondFactorVerified();
346
        $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId);
347
348
        $logger->info(
349
            sprintf(
350
                'Marked Yubikey Second Factor "%s" as verified, forwarding to Saml Proxy to respond',
351
                $selectedSecondFactor
352
            )
353
        );
354
355
        return $this->forward($context->getResponseAction());
356
    }
357
358
    /**
359
     * @Template
360
     * @param Request $request
361
     * @return array|Response
362
     */
363
    public function verifySmsSecondFactorAction(Request $request)
364
    {
365
        $context = $this->getResponseContext();
366
        $originalRequestId = $context->getInResponseTo();
367
368
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
369
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
370
371
        $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);
372
373
        $logger->notice('Verifying possession of SMS second factor, preparing to send');
374
375
        $command = new SendSmsChallengeCommand();
376
        $command->secondFactorId = $selectedSecondFactor;
377
378
        $form = $this->createForm(SendSmsChallengeType::class, $command)->handleRequest($request);
379
        $cancelForm = $this->buildCancelAuthenticationForm()->handleRequest($request);
380
381
        $stepupService = $this->getStepupService();
382
        $phoneNumber = InternationalPhoneNumber::fromStringFormat(
383
            $stepupService->getSecondFactorIdentifier($selectedSecondFactor)
384
        );
385
386
        $otpRequestsRemaining = $stepupService->getSmsOtpRequestsRemainingCount();
387
        $maximumOtpRequests = $stepupService->getSmsMaximumOtpRequestsCount();
388
        $viewVariables = ['otpRequestsRemaining' => $otpRequestsRemaining, 'maximumOtpRequests' => $maximumOtpRequests];
389
390
        if (!$form->isValid()) {
391
            return array_merge(
392
                $viewVariables,
393
                [
394
                    'phoneNumber' => $phoneNumber,
395
                    'form' => $form->createView(),
396
                    'cancelForm' => $cancelForm->createView()
397
                ]
398
            );
399
        }
400
401
        $logger->notice('Verifying possession of SMS second factor, sending challenge per SMS');
402
403
        if (!$stepupService->sendSmsChallenge($command)) {
404
            $form->addError(new FormError('gateway.form.send_sms_challenge.sms_sending_failed'));
405
406
            return array_merge(
407
                $viewVariables,
408
                [
409
                    'phoneNumber' => $phoneNumber,
410
                    'form' => $form->createView(),
411
                    'cancelForm' => $cancelForm->createView()
412
                ]
413
            );
414
        }
415
416
        return $this->redirect($this->generateUrl('gateway_verify_second_factor_sms_verify_challenge'));
417
    }
418
419
    /**
420
     * @Template
421
     * @param Request $request
422
     * @return array|Response
423
     */
424
    public function verifySmsSecondFactorChallengeAction(Request $request)
425
    {
426
        $context = $this->getResponseContext();
427
        $originalRequestId = $context->getInResponseTo();
428
429
        /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */
430
        $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId);
431
432
        $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);
433
434
        $command = new VerifyPossessionOfPhoneCommand();
435
        $form = $this->createForm(VerifySmsChallengeType::class, $command)->handleRequest($request);
436
        $cancelForm = $this->buildCancelAuthenticationForm()->handleRequest($request);
437
438
        if (!$form->isValid()) {
439
            return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];
440
        }
441
442
        $logger->notice('Verifying input SMS challenge matches');
443
444
        $verification = $this->getStepupService()->verifySmsChallenge($command);
445
446
        if ($verification->wasSuccessful()) {
447
            $this->getStepupService()->clearSmsVerificationState();
448
449
            $this->getResponseContext()->markSecondFactorVerified();
450
            $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId);
451
452
            $logger->info(
453
                sprintf(
454
                    'Marked Sms Second Factor "%s" as verified, forwarding to Saml Proxy to respond',
455
                    $selectedSecondFactor
456
                )
457
            );
458
459
            return $this->forward($context->getResponseAction());
460
        } elseif ($verification->didOtpExpire()) {
461
            $logger->notice('SMS challenge expired');
462
            $form->addError(new FormError('gateway.form.send_sms_challenge.challenge_expired'));
463
        } elseif ($verification->wasAttemptedTooManyTimes()) {
464
            $logger->notice('SMS challenge verification was attempted too many times');
465
            $form->addError(new FormError('gateway.form.send_sms_challenge.too_many_attempts'));
466
        } else {
467
            $logger->notice('SMS challenge did not match');
468
            $form->addError(new FormError('gateway.form.send_sms_challenge.sms_challenge_incorrect'));
469
        }
470
471
        return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];
472
    }
473
474
    public function cancelAuthenticationAction()
475
    {
476
        return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendAuthenticationCancelledByUser');
477
    }
478
479
    /**
480
     * @return \Surfnet\StepupGateway\GatewayBundle\Service\StepupAuthenticationService
481
     */
482
    private function getStepupService()
483
    {
484
        return $this->get('gateway.service.stepup_authentication');
485
    }
486
487
    /**
488
     * @return ResponseContext
489
     */
490
    private function getResponseContext()
491
    {
492
        return $this->get($this->get('gateway.proxy.state_handler')->getResponseContextServiceId());
493
    }
494
495
    /**
496
     * @return \Surfnet\StepupGateway\GatewayBundle\Monolog\Logger\AuthenticationLogger
497
     */
498
    private function getAuthenticationLogger()
499
    {
500
        return $this->get('gateway.authentication_logger');
501
    }
502
503
    /**
504
     * @param ResponseContext $context
505
     * @param LoggerInterface $logger
506
     * @return string
507
     */
508
    private function getSelectedSecondFactor(ResponseContext $context, LoggerInterface $logger)
509
    {
510
        $selectedSecondFactor = $context->getSelectedSecondFactor();
511
512
        if (!$selectedSecondFactor) {
513
            $logger->error('Cannot verify possession of an unknown second factor');
514
515
            throw new BadRequestHttpException('Cannot verify possession of an unknown second factor.');
516
        }
517
518
        return $selectedSecondFactor;
519
    }
520
521
    private function selectAndRedirectTo(SecondFactor $secondFactor, ResponseContext $context)
522
    {
523
        $context->saveSelectedSecondFactor($secondFactor);
524
525
        $this->getStepupService()->clearSmsVerificationState();
526
527
        $secondFactorTypeService = $this->get('surfnet_stepup.service.second_factor_type');
528
        $secondFactorType = new SecondFactorType($secondFactor->secondFactorType);
529
530
        $route = 'gateway_verify_second_factor_';
531
        if ($secondFactorTypeService->isGssf($secondFactorType)) {
532
            $route .= 'gssf';
533
        } else {
534
            $route .= strtolower($secondFactor->secondFactorType);
535
        }
536
537
        return $this->redirect($this->generateUrl($route));
538
    }
539
540
    /**
541
     * @return Form
542
     */
543
    private function buildCancelAuthenticationForm()
544
    {
545
        $cancelFormAction = $this->generateUrl('gateway_cancel_authentication');
546
        $cancelForm = $this->createForm(
547
            CancelAuthenticationType::class,
548
            null,
549
            ['action' => $cancelFormAction]
550
        );
551
        return $cancelForm;
552
    }
553
}
554