PluginProbe
Activity Logs, User Activity Tracking, Multisite Activity Log from Logtivity / trunk
Activity Logs, User Activity Tracking, Multisite Activity Log from Logtivity vtrunk
3.3.8 trunk 1.0 1.1.0 1.10.0 1.11.0 1.11.1 1.12.0 1.13.0 1.14.0 1.15.0 1.16.0 1.17.0 1.17.1 1.18.0 1.19.0 1.2.0 1.20.0 1.20.1 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 1.6.1 All 66 releases
logtivity / Core / Services / Logtivity_JsonWebToken.php

Logtivity_JsonWebToken.php in Activity Logs, User Activity Tracking, Multisite Activity Log from Logtivity trunk, at Core/Services/Logtivity_JsonWebToken.php

185 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package Logtivity
5 * @copyright 2025-2026 Logtivity. All rights reserved
6 * @contact logtivity.io, hello@logtivity.io
7 *
8 * This file is part of Logtivity
9 */
10
11 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
12
13 class Logtivity_JsonWebToken
14 {
15 /**
16 * @var string
17 */
18 protected string $algorithm = 'sha512';
19
20 /**
21 * @var int
22 */
23 protected int $expireSeconds = 5;
24
25 /**
26 * @param string $secret
27 * @param array $payload
28 * @param ?string $algorithm
29 *
30 * @return string
31 * @throws Exception
32 */
33 public function create(string $secret, array $payload = [], ?string $algorithm = null): string
34 {
35 $header = [
36 'alg' => $this->algorithm,
37 'typ' => 'JWT',
38 ];
39
40 if (empty($payload['exp'])) {
41 $payload['exp'] = time() + $this->expireSeconds;
42 }
43
44 $signInput = join('.', [
45 static::base64Encode(static::jsonEncode($header)),
46 static::base64Encode(static::jsonEncode($payload)),
47 ]);
48
49 if ($algorithm) {
50 $this->useAlgorithm($algorithm);
51 }
52 $signature = hash_hmac($this->algorithm, $signInput, $secret, true);
53
54 return $signInput . '.' . static::base64Encode($signature);
55 }
56
57 /**
58 * Parse a JWT token created by this helper
59 *
60 * @param string $token
61 * @param string $secret
62 *
63 * @return object
64 * @throws Exception
65 */
66 public function parse(string $token, string $secret): object
67 {
68 $atoms = explode('.', $token);
69 if (count($atoms) != 3) {
70 throw new Exception('Invalid JWT Token');
71
72 } else {
73 [$header64, $payload64, $signature64] = $atoms;
74
75 $signature = $this->base64Decode($signature64);
76 if (!$signature) {
77 throw new Exception('Invalid JWT Signature');
78
79 } elseif ($payloadJson = $this->base64Decode($payload64)) {
80 $hash = hash_hmac($this->algorithm, "{$header64}.{$payload64}", $secret, true);
81 if (hash_equals($hash, $signature)) {
82 $payload = $this->jsonDecode($payloadJson);
83 if (empty($payload->exp) == false && $payload->exp < time()) {
84 throw new Exception('Expired JWT token');
85 }
86
87 return $payload;
88
89 } else {
90 throw new Exception('Invalid JWT Token');
91 }
92 } else {
93 throw new Exception('Invalid JWT Payload');
94 }
95 }
96 }
97
98 /**
99 * @param string $algorithm
100 *
101 * @return $this
102 * @throws Exception
103 */
104 public function useAlgorithm(string $algorithm): self
105 {
106 if (in_array($algorithm, hash_algos(), true)) {
107 $this->algorithm = $algorithm;
108
109 return $this;
110 }
111
112 throw new Exception('Invalid Hash Algorithm');
113 }
114
115 /**
116 * Base64 encoding that is url safe
117 *
118 * @param string $input
119 *
120 * @return string
121 */
122 protected function base64Encode(string $input): string
123 {
124 return str_replace(
125 '=',
126 '',
127 strtr(
128 base64_encode($input),
129 '+/',
130 '-_'
131 )
132 );
133 }
134
135 /**
136 * Restore URL safe base64 string
137 *
138 * @param string $input
139 *
140 * @return string
141 */
142 protected function base64Decode(string $input): string
143 {
144 $remainder = strlen($input) % 4;
145 if ($remainder) {
146 $padding = 4 - $remainder;
147 $input .= str_repeat('=', $padding);
148 }
149
150 return base64_decode(strtr($input, '-_', '+/'));
151 }
152
153 /**
154 * JSON Encode without escape characters
155 *
156 * @param array $input
157 *
158 * @return string
159 */
160 protected function jsonEncode(array $input): string
161 {
162 return json_encode($input, JSON_UNESCAPED_SLASHES);
163 }
164
165 /**
166 * @param ?string $input
167 *
168 * @return ?object
169 * @throws Exception
170 */
171 protected function jsonDecode(?string $input): ?object
172 {
173 $result = json_decode((string)$input, false, 512, JSON_BIGINT_AS_STRING);
174 if ($result && is_array($result)) {
175 $result = (object)$result;
176 }
177
178 if ($input && empty($result)) {
179 throw new Exception('Invalid JSON');
180 }
181
182 return $result ?: null;
183 }
184 }
185