GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

JsonWebToken   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
c 1
b 0
f 0
dl 0
loc 177
rs 9.92
wmc 31

4 Methods

Rating   Name   Duplication   Size   Complexity  
F decode() 0 113 24
A setKeyId() 0 5 1
A setLeeway() 0 5 1
A encode() 0 25 5
1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Security\Authentication;
15
16
// ------------------------------------------------------------------------
17
18
use O2System\Security\Encoders\Base64;
19
use O2System\Security\Encoders\Json;
20
use O2System\Security\Authentication\User\Signature;
21
use O2System\Security\Encryptions\Algorithm;
22
use O2System\Security\Generators\Token;
23
24
/**
25
 * Class JsonWebToken
26
 * @package O2System\Security\Authentication
27
 */
28
class JsonWebToken extends Token
29
{
30
    protected $keyId;
31
32
    /**
33
     * When checking nbf, iat or expiration times,
34
     * we want to provide some extra leeway time to
35
     * account for clock skew.
36
     */
37
    protected $leeway = 0;
38
39
    protected $headers = [
40
        'typ' => 'JWT',
41
    ];
42
43
    // ------------------------------------------------------------------------
44
45
    public function setKeyId($keyId)
46
    {
47
        $this->keyId = $keyId;
48
49
        return $this;
50
    }
51
52
    // ------------------------------------------------------------------------
53
54
    public function setLeeway($leeway)
55
    {
56
        $this->leeway = intval($leeway);
57
58
        return $this;
59
    }
60
61
    // ------------------------------------------------------------------------
62
63
    public function encode(array $payload, $key = null)
64
    {
65
        $key = empty($key) ? $this->key : $key;
66
        if (is_null($key)) {
67
            if (class_exists('O2System\Framework', false)) {
68
                $key = config()->getItem('security')->encryptionKey;
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

68
                $key = /** @scrutinizer ignore-call */ config()->getItem('security')->encryptionKey;
Loading history...
69
            }
70
        }
71
72
        $this->addHeader('alg', $this->algorithm);
73
74
        if ( ! empty($this->keyId)) {
75
            $this->addHeader('kid', $this->keyId);
76
        }
77
78
        // Create Header Segment
79
        $segments[] = Base64::encode(Json::encode($this->headers));
0 ignored issues
show
Comprehensibility Best Practice introduced by
$segments was never initialized. Although not strictly required by PHP, it is generally a good practice to add $segments = array(); before regardless.
Loading history...
80
81
        // Create Payload Segment
82
        $segments[] = Base64::encode(Json::encode($payload));
83
84
        // Create Signature Segment
85
        $segments[] = Base64::encode(Signature::generate($segments, $key, $this->algorithm));
86
87
        return implode('.', $segments);
88
    }
89
90
    // ------------------------------------------------------------------------
91
92
    public function decode($token, $key = null)
93
    {
94
        $key = empty($key) ? $this->key : $key;
95
        if (is_null($key)) {
96
            if (class_exists('O2System\Framework', false)) {
97
                $key = config()->getItem('security')->encryptionKey;
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

97
                $key = /** @scrutinizer ignore-call */ config()->getItem('security')->encryptionKey;
Loading history...
98
            }
99
        }
100
101
        $timestamp = empty($this->timestamp) ? time() : $this->timestamp;
102
103
        $segments = explode('.', $token);
104
        $segments = array_map('trim', $segments);
105
106
        if (count($segments) == 3) {
107
            list($headers, $payload, $signature) = $segments;
108
109
            // Base64 decode headers
110
            if (false === ($headers = Base64::decode($headers))) {
111
                $this->errors[] = 'Invalid header base64 decoding';
112
113
                return false;
114
            }
115
116
            // Json decode headers
117
            if (null === ($headers = Json::decode($headers))) {
118
                $this->errors[] = 'Invalid header json decoding';
119
120
                return false;
121
            }
122
123
            // Validate algorithm header
124
            if (empty($headers->alg)) {
125
                $this->errors[] = 'Invalid algorithm';
126
127
                return false;
128
            } elseif ( ! Algorithm::validate($headers->alg)) {
129
                $this->errors[] = 'Unsupported algorithm';
130
131
                return false;
132
            }
133
134
            // Validate algorithm key id
135
            if (is_array($key) or $key instanceof \ArrayAccess) {
136
                if (isset($headers->kid)) {
137
                    if ( ! isset($key[ $headers->kid ])) {
138
                        $this->errors[] = 'Invalid Key Id';
139
140
                        return false;
141
                    }
142
143
                    $key = $key[ $headers->kid ];
144
                } else {
145
                    $this->errors[] = 'Empty Key id';
146
147
                    return false;
148
                }
149
            }
150
151
            // Base64 decode payload
152
            if (false === ($payload = Base64::decode($payload))) {
153
                $this->errors[] = 'Invalid payload base64 decoding';
154
155
                return false;
156
            }
157
158
            // Json decode payload
159
            if (null === ($payload = Json::decode($payload))) {
160
                $this->errors[] = 'Invalid payload json decoding';
161
162
                return false;
163
            }
164
165
            // Base64 decode payload
166
            if (false === ($signature = Base64::decode($signature))) {
167
                $this->errors[] = 'Invalid signature base64 decoding';
168
169
                return false;
170
            }
171
172
            if (Signature::verify($token, $signature, $key, $headers->alg) === false) {
173
                $this->errors[] = 'Invalid signature';
174
175
                return false;
176
            }
177
178
            // Check if the nbf if it is defined. This is the time that the
179
            // token can actually be used. If it's not yet that time, abort.
180
            if (isset($payload->nbf) && $payload->nbf > ($timestamp + $this->leeway)) {
181
                $this->errors[] = 'Cannot handle token prior to ' . date(\DateTime::ISO8601, $payload->nbf);
182
183
                return false;
184
            }
185
186
            // Check that this token has been created before 'now'. This prevents
187
            // using tokens that have been created for later use (and haven't
188
            // correctly used the nbf claim).
189
            if (isset($payload->iat) && $payload->iat > ($timestamp + $this->leeway)) {
190
                $this->errors[] = 'Cannot handle token prior to ' . date(\DateTime::ISO8601, $payload->iat);
191
192
                return false;
193
            }
194
            // Check if this token has expired.
195
            if (isset($payload->exp) && ($timestamp - $this->leeway) >= $payload->exp) {
196
                $this->errors[] = 'Expired token';
197
198
                return false;
199
            }
200
201
            return $payload;
202
        }
203
204
        return false;
205
    }
206
}