Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
WebAuthnSecondaryAuthenticationProvider
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 6
210
0.00% covered (danger)
0.00%
0 / 1
 getAuthenticationRequests
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOATHUser
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 beginSecondaryAuthentication
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 continueSecondaryAuthentication
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
42
 addModules
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 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\Context\RequestContext;
12use MediaWiki\Extension\OATHAuth\OATHAuthServices;
13use MediaWiki\Extension\OATHAuth\OATHUser;
14use MediaWiki\User\User;
15
16class WebAuthnSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
17
18    /** @inheritDoc */
19    public function getAuthenticationRequests( $action, array $options ) {
20        return [];
21    }
22
23    private function getOATHUser( User $user ): OATHUser {
24        return OATHAuthServices::getInstance()
25            ->getUserRepository()->findByUser( $user );
26    }
27
28    /** @inheritDoc */
29    public function beginSecondaryAuthentication( $user, array $reqs ) {
30        $authenticator = OATHAuthServices::getInstance()->getWebAuthnAuthenticator();
31        $oathUser = $this->getOATHUser( $user );
32        if ( !$authenticator->isEnabled( $oathUser ) ) {
33            return AuthenticationResponse::newAbstain();
34        }
35        $canAuthenticate = $authenticator->canAuthenticate( $oathUser );
36        if ( !$canAuthenticate->isGood() ) {
37            return AuthenticationResponse::newFail( $canAuthenticate->getMessage() );
38        }
39        $startAuthResult = $authenticator->startAuthentication( $oathUser );
40        if ( $startAuthResult->isGood() ) {
41            $request = new WebAuthnAuthenticationRequest( $startAuthResult->getValue()['json'] );
42            $this->addModules();
43            return AuthenticationResponse::newUI( [ $request ] );
44        }
45        return AuthenticationResponse::newFail( $startAuthResult->getMessage() );
46    }
47
48    /** @inheritDoc */
49    public function continueSecondaryAuthentication( $user, array $reqs ) {
50        $authenticator = OATHAuthServices::getInstance()->getWebAuthnAuthenticator();
51        $oathUser = $this->getOATHUser( $user );
52        $canAuthenticate = $authenticator->canAuthenticate( $oathUser );
53        if ( !$canAuthenticate->isGood() ) {
54            return AuthenticationResponse::newFail( $canAuthenticate->getMessage() );
55        }
56
57        /** @var WebAuthnAuthenticationRequest $request */
58        $request = AuthenticationRequest::getRequestByClass(
59            $reqs,
60            WebAuthnAuthenticationRequest::class
61        );
62        if ( !$request ) {
63            // Re-ask user for credentials
64            $request = new WebAuthnAuthenticationRequest(
65                $authenticator->startAuthentication( $oathUser )->getValue()['json']
66            );
67            $this->addModules();
68            return AuthenticationResponse::newUI( [ $request ],
69                wfMessage( 'oathauth-webauthn-error-credentials-missing' ), 'error' );
70        }
71
72        // Get credential retrieved from the client
73        $verificationData = $request->getSubmittedData();
74        if ( $verificationData['credential'] === '' ) {
75            return AuthenticationResponse::newUI( [ $request ],
76                wfMessage( 'oathauth-webauthn-error-credentials-missing' ), 'error' );
77        }
78
79        $authResult = $authenticator->continueAuthentication( $oathUser, $verificationData['credential'] );
80        if ( $authResult->isGood() ) {
81            return AuthenticationResponse::newPass( $authResult->getValue()->getUser()->getName() );
82        }
83
84        $messages = $authResult->getMessages();
85
86        if ( $messages === [] ) {
87            return AuthenticationResponse::newFail( wfMessage( 'oathauth-webauthn-error-verification-failed' ) );
88        }
89
90        // Return the first error from the authenticator, if there is any
91        return AuthenticationResponse::newFail( wfMessage( $messages[0] ) );
92    }
93
94    private function addModules() {
95        // It would be better to add modules in HTMLFormField class,
96        // but that does not seem to work for the login form
97        $out = RequestContext::getMain()->getOutput();
98        $out->addModules( "ext.webauthn.login" );
99    }
100
101    /** @inheritDoc */
102    public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
103        return AuthenticationResponse::newAbstain();
104    }
105}