Completed
Pull Request — master (#20)
by Дмитрий
09:49 queued 07:44
created

JWT::urlsafeB64Decode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace SocialConnect\OpenIDConnect;
7
8
use SocialConnect\OpenIDConnect\Exception\InvalidJWT;
9
10
class JWT
11
{
12
    public static $algorithms = array(
13
        'HS256' => array('hash_hmac', 'SHA256'),
14
        'HS512' => array('hash_hmac', 'SHA512'),
15
        'HS384' => array('hash_hmac', 'SHA384'),
16
        'RS256' => array('openssl', OPENSSL_ALGO_SHA256),
17
        'RS384' => array('openssl', OPENSSL_ALGO_SHA384),
18
        'RS512' => array('openssl', OPENSSL_ALGO_SHA512),
19
    );
20
21
    /**
22
     * @var array
23
     */
24
    protected $parts;
25
26
    /**
27
     * @var array
28
     */
29
    protected $header;
30
31
    /**
32
     * @var array
33
     */
34
    protected $payload;
35
36
    /**
37
     * @var string
38
     */
39
    protected $signature;
40
41
    /**
42
     * @param string $input
43
     * @return string
44
     */
45
    public static function urlsafeB64Decode($input)
46
    {
47
        $remainder = strlen($input) % 4;
48
49
        if ($remainder) {
50
            $padlen = 4 - $remainder;
51
            $input .= str_repeat('=', $padlen);
52
        }
53
54
        return base64_decode(strtr($input, '-_', '+/'));
55
    }
56
57
    /**
58
     * @param string $token
59
     * @param array $keys
60
     * @throws InvalidJWT
61
     */
62
    public function __construct($token, array $keys)
63
    {
64
        $parts = explode('.', $token);
65
        if (count($parts) !== 3) {
66
            throw new InvalidJWT('Wrong number of segments');
67
        }
68
69
        list ($header64, $payload64, $token64) = $parts;
70
71
        $headerPayload = base64_decode($header64, true);
72
        $this->header = json_decode($headerPayload);
0 ignored issues
show
Documentation Bug introduced by
It seems like json_decode($headerPayload) of type * is incompatible with the declared type array of property $header.

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...
73
74
        $decodedPayload = base64_decode($payload64, true);
75
        $this->payload = json_decode($decodedPayload);
0 ignored issues
show
Documentation Bug introduced by
It seems like json_decode($decodedPayload) of type * is incompatible with the declared type array of property $payload.

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...
76
77
        $this->signature = self::urlsafeB64Decode($token64);
78
79
        $this->validate("{$header64}.{$payload64}", $keys);
80
    }
81
82
    /**
83
     * @param string $data
84
     * @param array $keys
85
     * @throws InvalidJWT
86
     */
87
    protected function validate($data, array $keys)
88
    {
89
        if (!isset($this->header->alg)) {
90
            throw new InvalidJWT('No alg inside header');
91
        }
92
93
        if (!isset($this->header->kid)) {
94
            throw new InvalidJWT('No kid inside header');
95
        }
96
97
        $result = $this->verifySignature($data, $keys);
98
        if (!$result) {
99
            throw new InvalidJWT('Unexpected signature');
100
        }
101
    }
102
103
    /**
104
     * @param array $keys
105
     * @param string $kid
106
     * @return JWK
107
     * @throws \RuntimeException
108
     */
109
    protected function findKeyByKind(array $keys, $kid)
110
    {
111
        foreach ($keys as $key) {
112
            if ($key['kid'] === $kid) {
113
                return new JWK($key);
114
            }
115
        }
116
117
        throw new \RuntimeException('Unknown key');
118
    }
119
120
    /**
121
     * @return bool
122
     */
123
    protected function verifySignature($data, array $keys)
124
    {
125
        if (!function_exists('openssl_sign')) {
126
            throw new \RuntimeException('Openssl is required to use RSA encryption.');
127
        }
128
129
        $jwk = $this->findKeyByKind($keys, $this->header->kid);
130
131
        $result = openssl_verify(
132
            $data,
133
            $this->signature,
134
            $jwk->getPublicKey(),
135
            OPENSSL_ALGO_SHA256
136
        );
137
138
139
        return $result == 1;
140
    }
141
}
142