Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
RecoveryCodesSecondaryAuthenticationProvider
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 5
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAuthenticationRequests
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 beginSecondaryAuthentication
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 continueSecondaryAuthentication
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
 beginSecondaryAccountCreation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 */
5
6namespace MediaWiki\Extension\OATHAuth\Auth;
7
8use MediaWiki\Auth\AbstractSecondaryAuthenticationProvider;
9use MediaWiki\Auth\AuthenticationRequest;
10use MediaWiki\Auth\AuthenticationResponse;
11use MediaWiki\Auth\AuthManager;
12use MediaWiki\Extension\OATHAuth\Module\RecoveryCodes;
13use MediaWiki\Extension\OATHAuth\OATHAuthLogger;
14use MediaWiki\Extension\OATHAuth\OATHUserRepository;
15use MediaWiki\Message\Message;
16
17/**
18 * AuthManager secondary authentication provider for Recovery Codes second-factor authentication.
19 *
20 * After a successful primary authentication, requests a recovery code from the user.
21 *
22 * @see AuthManager
23 */
24class RecoveryCodesSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
25
26    public function __construct(
27        private readonly RecoveryCodes $module,
28        private readonly OATHUserRepository $userRepository,
29        private readonly OATHAuthLogger $oathLogger,
30    ) {
31    }
32
33    /** @inheritDoc */
34    public function getAuthenticationRequests( $action, array $options ): array {
35        // don't ask for anything initially, so the second factor is on a separate screen
36        return [];
37    }
38
39    /**
40     * If the user has a recovery code module enabled, request a second factor.
41     *
42     * @inheritDoc
43     */
44    public function beginSecondaryAuthentication( $user, array $reqs ): AuthenticationResponse {
45        $authUser = $this->userRepository->findByUser( $user );
46
47        if ( !$this->module->isEnabled( $authUser ) ) {
48            return AuthenticationResponse::newAbstain();
49        }
50
51        return AuthenticationResponse::newUI( [ new RecoveryCodesAuthenticationRequest() ] );
52    }
53
54    /** @inheritDoc */
55    public function continueSecondaryAuthentication( $user, array $reqs ) {
56        /** @var RecoveryCodesAuthenticationRequest $request */
57        $request = AuthenticationRequest::getRequestByClass( $reqs, RecoveryCodesAuthenticationRequest::class );
58        if ( !$request ) {
59            return AuthenticationResponse::newUI( [ new RecoveryCodesAuthenticationRequest() ],
60                wfMessage( 'oathauth-recovery-code-login-failed' ), 'error' );
61        }
62
63        // Don't increase pingLimiter, just check for limit exceeded.
64        if ( $user->pingLimiter( 'badoath', 0 ) ) {
65            return AuthenticationResponse::newUI(
66                [ new RecoveryCodesAuthenticationRequest() ],
67                new Message( 'oathauth-throttled' ),
68                'error'
69            );
70        }
71
72        $authUser = $this->userRepository->findByUser( $user );
73        $recoveryCode = $request->RecoveryCode;
74
75        if ( $this->module->verify( $authUser, [ 'recoverycode' => $recoveryCode ] ) ) {
76            return AuthenticationResponse::newPass();
77        }
78
79        // Increase rate limit counter for failed request
80        $user->pingLimiter( 'badoath' );
81
82        $this->logger->info( 'OATHAuth user {user} failed recovery code from {clientip}', [
83            'user'     => $user->getName(),
84            'clientip' => $user->getRequest()->getIP(),
85        ] );
86
87        $this->oathLogger->logFailedVerification( $user );
88
89        return AuthenticationResponse::newUI(
90            [ new RecoveryCodesAuthenticationRequest() ],
91            wfMessage( 'oathauth-recovery-code-login-failed' ),
92            'error'
93        );
94    }
95
96    /** @inheritDoc */
97    public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
98        return AuthenticationResponse::newAbstain();
99    }
100}