Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.14% covered (warning)
85.14%
63 / 74
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
AccessToken
85.14% covered (warning)
85.14%
63 / 74
60.00% covered (warning)
60.00%
3 / 5
12.47
0.00% covered (danger)
0.00%
0 / 1
 getSupportedRequestTypes
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 execute
62.50% covered (warning)
62.50%
10 / 16
0.00% covered (danger)
0.00%
0 / 1
4.84
 getBodyParamSettings
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
1
 getGrantType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getGrantClass
28.57% covered (danger)
28.57%
2 / 7
0.00% covered (danger)
0.00%
0 / 1
14.11
1<?php
2
3namespace MediaWiki\Extension\OAuth\Rest\Handler;
4
5use GuzzleHttp\Psr7\ServerRequest;
6use League\OAuth2\Server\Exception\OAuthServerException;
7use MediaWiki\Extension\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAccessTokens;
8use MediaWiki\Extension\OAuth\AuthorizationProvider\Grant\ClientCredentials;
9use MediaWiki\Extension\OAuth\AuthorizationProvider\Grant\RefreshToken;
10use MediaWiki\Extension\OAuth\Response;
11use MWExceptionHandler;
12use Throwable;
13use Wikimedia\ParamValidator\ParamValidator;
14
15/**
16 * Handles the oauth2/access_token endpoint, which can be used after the user has returned from
17 * the authorization dialog to trade the off the received authorization code for an access token.
18 */
19class AccessToken extends AuthenticationHandler {
20
21    private const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
22    private const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code';
23    private const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
24
25    /**
26     * Support x-www-form-urlencoded (and nothing else), as required by RFC 6749.
27     * @return string[]
28     */
29    public function getSupportedRequestTypes(): array {
30        return [
31            'application/x-www-form-urlencoded',
32        ];
33    }
34
35    /**
36     * @inheritDoc
37     */
38    public function execute() {
39        $response = new Response();
40
41        try {
42            if ( $this->queuedError ) {
43                throw $this->queuedError;
44            }
45            $request = ServerRequest::fromGlobals()->withParsedBody(
46                $this->getValidatedBody()
47            );
48
49            $authProvider = $this->getAuthorizationProvider();
50            return $authProvider->getAccessTokens( $request, $response );
51        } catch ( OAuthServerException $exception ) {
52            return $this->errorResponse( $exception, $response );
53        } catch ( Throwable $exception ) {
54            MWExceptionHandler::logException( $exception );
55            return $this->errorResponse(
56                OAuthServerException::serverError( $exception->getMessage(), $exception ),
57                $response
58            );
59        }
60    }
61
62    /**
63     * @inheritDoc
64     */
65    public function getBodyParamSettings(): array {
66        return [
67            'grant_type' => [
68                self::PARAM_SOURCE => 'body',
69                ParamValidator::PARAM_TYPE => [
70                    self::GRANT_TYPE_CLIENT_CREDENTIALS,
71                    self::GRANT_TYPE_AUTHORIZATION_CODE,
72                    self::GRANT_TYPE_REFRESH_TOKEN,
73                ],
74                ParamValidator::PARAM_REQUIRED => true,
75            ],
76            'client_id' => [
77                self::PARAM_SOURCE => 'body',
78                ParamValidator::PARAM_TYPE => 'string',
79                ParamValidator::PARAM_REQUIRED => false,
80            ],
81            'client_secret' => [
82                self::PARAM_SOURCE => 'body',
83                ParamValidator::PARAM_TYPE => 'string',
84                ParamValidator::PARAM_REQUIRED => false,
85            ],
86            'redirect_uri' => [
87                self::PARAM_SOURCE => 'body',
88                ParamValidator::PARAM_TYPE => 'string',
89                ParamValidator::PARAM_REQUIRED => false,
90            ],
91            'scope' => [
92                self::PARAM_SOURCE => 'body',
93                ParamValidator::PARAM_TYPE => 'string',
94                ParamValidator::PARAM_REQUIRED => false,
95            ],
96            'code' => [
97                self::PARAM_SOURCE => 'body',
98                ParamValidator::PARAM_TYPE => 'string',
99                ParamValidator::PARAM_REQUIRED => false,
100            ],
101            'refresh_token' => [
102                self::PARAM_SOURCE => 'body',
103                ParamValidator::PARAM_TYPE => 'string',
104                ParamValidator::PARAM_REQUIRED => false,
105            ],
106            'code_verifier' => [
107                self::PARAM_SOURCE => 'body',
108                ParamValidator::PARAM_TYPE => 'string',
109                ParamValidator::PARAM_REQUIRED => false,
110            ]
111        ];
112    }
113
114    /**
115     * @return string
116     */
117    protected function getGrantType() {
118        $body = $this->getValidatedBody();
119        '@phan-var array $body';
120        return $body['grant_type'];
121    }
122
123    /**
124     * @param string $grantType
125     * @return string|false
126     */
127    protected function getGrantClass( $grantType ) {
128        switch ( $grantType ) {
129            case static::GRANT_TYPE_AUTHORIZATION_CODE:
130                return AuthorizationCodeAccessTokens::class;
131            case static::GRANT_TYPE_CLIENT_CREDENTIALS:
132                return ClientCredentials::class;
133            case static::GRANT_TYPE_REFRESH_TOKEN:
134                return RefreshToken::class;
135            default:
136                return false;
137        }
138    }
139}