HMACSignatureAuth::getTokenFromAccessToken()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 1
dl 0
loc 16
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace yrc\filters\auth;
4
5
use DateTime;
6
use ncryptf\Authorization;
7
use ncryptf\Token as NcryptfToken;
8
use yrc\models\redis\Token;
9
use yii\helpers\Json;
10
use yii\filters\auth\AuthMethod;
11
use yii\web\HttpException;
12
use yii\web\Request;
13
use yii\web\Response;
14
use Yii;
15
16
/**
17
 * HeaderParamAuth is an action filter that supports the authentication based on the access token passed through a query parameter.
18
 */
19
final class HMACSignatureAuth extends AuthMethod
20
{
21
    // The date header
22
    const DATE_HEADER = 'X-DATE';
23
    
24
    // The authorization header
25
    const AUTHORIZATION_HEADER = 'Authorization';
26
    
27
    // The amount of the seconds the request is permitted to differ from the server time
28
    const DRIFT_TIME_ALLOWANCE = 90;
29
30
    /**
31
     * @inheritdoc
32
     */
33
    public function beforeAction($action)
34
    {
35
        if (Yii::$app->request->method === 'OPTIONS') {
36
            return true;
37
        }
38
39
        return parent::beforeAction($action);
40
    }
41
42
    /**
43
     * @inheritdoc
44
     */
45
    public function authenticate($user, $request, $response)
46
    {
47
        $params = Authorization::extractParamsFromHeaderString($request->getHeaders()->get(self::AUTHORIZATION_HEADER));
48
49
        if ($params) {
50
            if ($token = $this->getTokenFromAccessToken($params['access_token'])) {
51
                try {
52
                    $date = new DateTime($params['date'] ?? $request->getHeaders()->get(self::DATE_HEADER));
53
                    $auth = new Authorization(
54
                        $request->method,
55
                        $request->getUrl(),
56
                        $token->getNcryptfToken(),
57
                        $date,
58
                        $this->getBodyFromRequest($request),
59
                        $params['v'],
60
                        \base64_decode($params['salt'])
61
                    );
62
63
                    if ($auth->verify(\base64_decode($params['hmac']), $auth, static::DRIFT_TIME_ALLOWANCE)) {
64
                        if ($identity = $user->loginByAccessToken($token, \get_class($this))) {
65
                            return $identity;
66
                        }
67
                    }
68
                } catch (HttpException $e) {
69
                    throw $e;
70
                } catch (\Exception $e) {
71
                    Yii::error([
72
                        'message' => 'An unexpected error occured when handling authentication request.',
73
                        'exception' => $e
74
                    ]);
75
                }
76
            }
77
        }
78
79
        $this->handleFailure($response);
80
    }
81
82
    /**
83
     * Retrieves a Token object from an access token string
84
     * @param string $accessToken
85
     * @return \yrc\models\redis\Token
86
     */
87
    private function getTokenFromAccessToken(string $accessToken)
88
    {
89
        try {
90
            $tokenClass = (Yii::$app->user->identityClass::TOKEN_CLASS);
91
            $token = $tokenClass::find()
92
                ->where(['access_token' => $accessToken])
93
                ->one();
94
        } catch (\Exception $e) {
95
            return null;
96
        }
97
98
        if ($token === null || $token->isExpired()) {
99
            return null;
100
        }
101
102
        return $token;
103
    }
104
105
    /**
106
     * Retrieves the appropriate request body
107
     * @param \yii\web\Request $request
108
     * @return mixed
109
     */
110
    private function getBodyFromRequest(Request $request)
111
    {
112
        $body = $request->getDecryptedBody();
113
        if ($body === '') {
114
            return "";
115
        }
116
117
        if ($request->headers->has('X-Force-Serialize')) {
118
            return Json::encode(Json::decode($body), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
119
        }
120
121
        return $body;
122
    }
123
}
124