@artilingo/artiframe-cli 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +660 -660
- package/README.md +64 -118
- package/bin/artiframe.js +0 -0
- package/bin/artiframe.php +0 -0
- package/core-stubs/app/ApiAuthControl.php +76 -0
- package/core-stubs/app/ApiControl.php +81 -35
- package/core-stubs/app/AuthControl.php +39 -0
- package/core-stubs/app/Database.php +36 -35
- package/core-stubs/app/DotEnv.php +19 -1
- package/core-stubs/app/ViewControl.php +47 -1
- package/core-stubs/bin/SQLMethod.php +261 -0
- package/core-stubs/bin/SystemMethod.php +115 -74
- package/core-stubs/bin/ViewMethod.php +19 -5
- package/core-stubs/docs/de.html +4325 -4347
- package/core-stubs/docs/en.html +4325 -4347
- package/core-stubs/docs/es.html +4325 -4347
- package/core-stubs/docs/fr.html +4325 -4347
- package/core-stubs/docs/tr.html +117 -46
- package/core-stubs/public/403.php +52 -0
- package/core-stubs/public/assets/css/components/header.css +174 -0
- package/core-stubs/public/assets/css/components/mobilenav.css +198 -0
- package/core-stubs/public/assets/css/components/sidebar.css +184 -0
- package/core-stubs/public/assets/css/root/app.css +975 -38
- package/core-stubs/public/assets/css/root/root.css +64 -0
- package/core-stubs/public/assets/js/components/header.js +380 -0
- package/core-stubs/public/assets/js/components/mobilenav.js +97 -0
- package/core-stubs/public/assets/js/components/theme-modal.js +74 -0
- package/core-stubs/public/assets/js/root/app.js +1870 -0
- package/core-stubs/public/includes/footer.php +4 -5
- package/core-stubs/public/includes/head-auth.php +65 -0
- package/core-stubs/public/includes/head.php +56 -17
- package/core-stubs/public/includes/header.php +135 -1
- package/core-stubs/public/includes/mobilenav.php +75 -3
- package/core-stubs/public/includes/sidebar.php +186 -1
- package/core-stubs/public/includes/theme-modal.php +1 -0
- package/db-studio/debug.log +3 -0
- package/db-studio/index.html +1755 -0
- package/db-studio/launcher.html +102 -0
- package/db-studio/launcher.js +180 -0
- package/db-studio/logo.svg +22 -0
- package/db-studio/main.js +568 -0
- package/db-studio/package-lock.json +1034 -0
- package/db-studio/package.json +16 -0
- package/db-studio/preload.js +29 -0
- package/devops-studio/api-inspector.js +222 -0
- package/devops-studio/index.html +1892 -0
- package/devops-studio/logo.svg +22 -0
- package/devops-studio/main.js +67 -0
- package/devops-studio/make-wizard.js +635 -0
- package/devops-studio/package-lock.json +871 -0
- package/devops-studio/package.json +12 -0
- package/package.json +5 -2
- package/scripts/postinstall.js +44 -8
- package/server-studio/.json-404-guard +1 -0
- package/server-studio/index.html +283 -0
- package/server-studio/logo.svg +22 -0
- package/server-studio/main.js +133 -0
- package/server-studio/package-lock.json +1179 -0
- package/server-studio/package.json +15 -0
- package/server-studio/router.php +99 -0
- package/src/App.php +29 -0
- package/src/Commands/AddCommand.php +26 -9
- package/src/Commands/DbExportCommand.php +131 -0
- package/src/Commands/DbStartCommand.php +46 -0
- package/src/Commands/DevopsCommand.php +35 -0
- package/src/Commands/GoCommand.php +48 -48
- package/src/Commands/ListCommand.php +68 -68
- package/src/Commands/MakeApiCommand.php +6 -1
- package/src/Commands/MakeViewCommand.php +25 -2
- package/src/Commands/NewProjectCommand.php +16 -11
- package/src/Commands/OptimizeCommand.php +61 -0
- package/src/Commands/RemoveCommand.php +35 -7
- package/src/Commands/ServeCommand.php +46 -91
- package/src/Commands/ShowCommand.php +24 -24
- package/src/Commands/UpgradeCommand.php +39 -10
- package/stubs/make/api-switch-case.stub +1 -1
- package/stubs/make/auth-api-standart.stub +20 -0
- package/stubs/make/auth-api-switch-case.stub +38 -0
- package/stubs/make/auth-view.stub +26 -0
- package/stubs/make/class.stub +10 -2
- package/stubs/make/view.stub +3 -0
- package/stubs/service/image.stub +334 -334
- package/stubs/service/iyzico.stub +637 -637
- package/stubs/service/jwt.stub +312 -312
- package/stubs/service/phpmailer.stub +143 -143
- package/stubs/service/pusher.stub +232 -232
- package/stubs/service/qrcode.stub +169 -169
- package/stubs/service/redis.stub +361 -361
- package/stubs/service/s3.stub +377 -377
- package/stubs/service/stripe.stub +526 -526
- package/stubs/service/twilio.stub +361 -361
package/stubs/service/jwt.stub
CHANGED
|
@@ -1,312 +1,312 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace Src\Auth;
|
|
4
|
-
|
|
5
|
-
use Firebase\JWT\JWT;
|
|
6
|
-
use Firebase\JWT\Key;
|
|
7
|
-
use Firebase\JWT\ExpiredException;
|
|
8
|
-
use Firebase\JWT\SignatureInvalidException;
|
|
9
|
-
use Firebase\JWT\BeforeValidException;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* JWT Authentication Service
|
|
13
|
-
*
|
|
14
|
-
* Provides a clean wrapper around firebase/php-jwt for token generation,
|
|
15
|
-
* verification, refresh, and HTTP middleware integration.
|
|
16
|
-
*
|
|
17
|
-
* Required environment variables:
|
|
18
|
-
* JWT_SECRET_KEY — Secret key used for signing and verifying tokens
|
|
19
|
-
* JWT_ALGORITHM — Signing algorithm (e.g. HS256, HS384, HS512)
|
|
20
|
-
* JWT_EXPIRY — Default token lifetime in seconds (e.g. 3600)
|
|
21
|
-
*
|
|
22
|
-
* @package Auth
|
|
23
|
-
*/
|
|
24
|
-
class JwtAuth
|
|
25
|
-
{
|
|
26
|
-
/**
|
|
27
|
-
* Secret key for signing tokens.
|
|
28
|
-
*
|
|
29
|
-
* @var string
|
|
30
|
-
*/
|
|
31
|
-
private string $secretKey;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Signing algorithm (HS256, HS384, HS512, etc.).
|
|
35
|
-
*
|
|
36
|
-
* @var string
|
|
37
|
-
*/
|
|
38
|
-
private string $algorithm;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Default token expiry in seconds.
|
|
42
|
-
*
|
|
43
|
-
* @var int
|
|
44
|
-
*/
|
|
45
|
-
private int $expiry;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create a new JwtAuth instance.
|
|
49
|
-
*
|
|
50
|
-
* Loads the secret key, algorithm, and default expiry from $_ENV.
|
|
51
|
-
*/
|
|
52
|
-
public function __construct()
|
|
53
|
-
{
|
|
54
|
-
$this->secretKey = $_ENV['JWT_SECRET_KEY'];
|
|
55
|
-
$this->algorithm = $_ENV['JWT_ALGORITHM'] ?? 'HS256';
|
|
56
|
-
$this->expiry = (int) ($_ENV['JWT_EXPIRY'] ?? 3600);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Generate a signed JWT token.
|
|
61
|
-
*
|
|
62
|
-
* Merges the given payload with standard claims (iat, exp) and encodes it.
|
|
63
|
-
*
|
|
64
|
-
* @param array $payload Custom claims to include in the token
|
|
65
|
-
* @param int|null $expiry Token lifetime in seconds (null = use default from $_ENV)
|
|
66
|
-
* @return string Encoded JWT token string
|
|
67
|
-
*/
|
|
68
|
-
public function generate(array $payload, int $expiry = null): string
|
|
69
|
-
{
|
|
70
|
-
$now = time();
|
|
71
|
-
$expiresIn = $expiry ?? $this->expiry;
|
|
72
|
-
|
|
73
|
-
$tokenPayload = array_merge($payload, [
|
|
74
|
-
'iat' => $now,
|
|
75
|
-
'exp' => $now + $expiresIn,
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
return JWT::encode($tokenPayload, $this->secretKey, $this->algorithm);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Verify and decode a JWT token.
|
|
83
|
-
*
|
|
84
|
-
* Returns a standardised response array with the decoded payload on success
|
|
85
|
-
* or an error message on failure.
|
|
86
|
-
*
|
|
87
|
-
* @param string $token The JWT token to verify
|
|
88
|
-
* @return array {
|
|
89
|
-
* @type string $status 'success' or 'error'
|
|
90
|
-
* @type string $message Human-readable result description
|
|
91
|
-
* @type array $data Decoded payload (on success only)
|
|
92
|
-
* }
|
|
93
|
-
*/
|
|
94
|
-
public function verify(string $token): array
|
|
95
|
-
{
|
|
96
|
-
try {
|
|
97
|
-
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
98
|
-
$payload = (array) $decoded;
|
|
99
|
-
|
|
100
|
-
return [
|
|
101
|
-
'status' => 'success',
|
|
102
|
-
'message' => 'Token is valid.',
|
|
103
|
-
'data' => $payload,
|
|
104
|
-
];
|
|
105
|
-
} catch (ExpiredException $e) {
|
|
106
|
-
return [
|
|
107
|
-
'status' => 'error',
|
|
108
|
-
'message' => 'Token has expired.',
|
|
109
|
-
'data' => [],
|
|
110
|
-
];
|
|
111
|
-
} catch (SignatureInvalidException $e) {
|
|
112
|
-
return [
|
|
113
|
-
'status' => 'error',
|
|
114
|
-
'message' => 'Invalid token signature.',
|
|
115
|
-
'data' => [],
|
|
116
|
-
];
|
|
117
|
-
} catch (BeforeValidException $e) {
|
|
118
|
-
return [
|
|
119
|
-
'status' => 'error',
|
|
120
|
-
'message' => 'Token is not yet valid.',
|
|
121
|
-
'data' => [],
|
|
122
|
-
];
|
|
123
|
-
} catch (\Exception $e) {
|
|
124
|
-
return [
|
|
125
|
-
'status' => 'error',
|
|
126
|
-
'message' => 'Token verification failed: ' . $e->getMessage(),
|
|
127
|
-
'data' => [],
|
|
128
|
-
];
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Extract the payload from a token without throwing exceptions.
|
|
134
|
-
*
|
|
135
|
-
* Useful when you need to inspect claims even if the token might be invalid.
|
|
136
|
-
* Returns null on any failure instead of throwing.
|
|
137
|
-
*
|
|
138
|
-
* @param string $token The JWT token to decode
|
|
139
|
-
* @return array|null Decoded payload or null on failure
|
|
140
|
-
*/
|
|
141
|
-
public function payload(string $token): ?array
|
|
142
|
-
{
|
|
143
|
-
try {
|
|
144
|
-
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
145
|
-
return (array) $decoded;
|
|
146
|
-
} catch (\Exception $e) {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Refresh a token by generating a new one with the same payload.
|
|
153
|
-
*
|
|
154
|
-
* Strips the old iat/exp claims and re-signs with a fresh expiry.
|
|
155
|
-
* Returns null if the original token cannot be decoded.
|
|
156
|
-
*
|
|
157
|
-
* @param string $token The current JWT token to refresh
|
|
158
|
-
* @param int|null $expiry New expiry duration in seconds (null = use default)
|
|
159
|
-
* @return string|null New JWT token or null on failure
|
|
160
|
-
*/
|
|
161
|
-
public function refresh(string $token, int $expiry = null): ?string
|
|
162
|
-
{
|
|
163
|
-
try {
|
|
164
|
-
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
165
|
-
$payload = (array) $decoded;
|
|
166
|
-
|
|
167
|
-
// Remove old timing claims so generate() sets fresh ones
|
|
168
|
-
unset($payload['iat'], $payload['exp'], $payload['nbf']);
|
|
169
|
-
|
|
170
|
-
return $this->generate($payload, $expiry);
|
|
171
|
-
} catch (\Exception $e) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Check whether a token has expired.
|
|
178
|
-
*
|
|
179
|
-
* Attempts to decode the token and catches ExpiredException specifically.
|
|
180
|
-
* Returns true if expired, false if still valid or on other errors.
|
|
181
|
-
*
|
|
182
|
-
* @param string $token The JWT token to check
|
|
183
|
-
* @return bool True if the token is expired
|
|
184
|
-
*/
|
|
185
|
-
public function isExpired(string $token): bool
|
|
186
|
-
{
|
|
187
|
-
try {
|
|
188
|
-
JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
189
|
-
return false;
|
|
190
|
-
} catch (ExpiredException $e) {
|
|
191
|
-
return true;
|
|
192
|
-
} catch (\Exception $e) {
|
|
193
|
-
// Token is invalid for another reason, not strictly "expired"
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Extract the user_id claim from a token.
|
|
200
|
-
*
|
|
201
|
-
* A convenience shortcut for the most common payload lookup.
|
|
202
|
-
*
|
|
203
|
-
* @param string $token The JWT token
|
|
204
|
-
* @return mixed The user_id value or null if not present / token invalid
|
|
205
|
-
*/
|
|
206
|
-
public function getUserId(string $token): mixed
|
|
207
|
-
{
|
|
208
|
-
$payload = $this->payload($token);
|
|
209
|
-
|
|
210
|
-
if ($payload === null) {
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return $payload['user_id'] ?? null;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Generate a long-lived refresh token.
|
|
219
|
-
*
|
|
220
|
-
* Identical to generate() but with a longer default expiry (7 days).
|
|
221
|
-
* Adds a 'token_type' => 'refresh' claim to distinguish from access tokens.
|
|
222
|
-
*
|
|
223
|
-
* @param array $payload Custom claims to include
|
|
224
|
-
* @param int $expiry Lifetime in seconds (default: 604800 = 7 days)
|
|
225
|
-
* @return string Encoded refresh token
|
|
226
|
-
*/
|
|
227
|
-
public function generateRefreshToken(array $payload, int $expiry = 604800): string
|
|
228
|
-
{
|
|
229
|
-
$payload['token_type'] = 'refresh';
|
|
230
|
-
|
|
231
|
-
return $this->generate($payload, $expiry);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* HTTP middleware that validates the Authorization header.
|
|
236
|
-
*
|
|
237
|
-
* Reads the Bearer token from the Authorization header, verifies it, and
|
|
238
|
-
* sets $_REQUEST['jwt_user'] with the decoded payload on success.
|
|
239
|
-
* On failure, sends a 401 JSON response via apiResponse() and exits.
|
|
240
|
-
*
|
|
241
|
-
* Usage:
|
|
242
|
-
* JwtAuth::middleware();
|
|
243
|
-
* // If execution reaches here, $_REQUEST['jwt_user'] is available
|
|
244
|
-
*
|
|
245
|
-
* @return void
|
|
246
|
-
*/
|
|
247
|
-
public static function middleware(): void
|
|
248
|
-
{
|
|
249
|
-
$instance = new self();
|
|
250
|
-
|
|
251
|
-
// Extract Bearer token from Authorization header
|
|
252
|
-
$authHeader = $_SERVER['HTTP_AUTHORIZATION']
|
|
253
|
-
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
|
|
254
|
-
?? '';
|
|
255
|
-
|
|
256
|
-
if (empty($authHeader)) {
|
|
257
|
-
if (function_exists('apiResponse')) {
|
|
258
|
-
apiResponse(401, 'Authorization header is missing.');
|
|
259
|
-
} else {
|
|
260
|
-
http_response_code(401);
|
|
261
|
-
header('Content-Type: application/json');
|
|
262
|
-
echo json_encode([
|
|
263
|
-
'status' => 'error',
|
|
264
|
-
'code' => 401,
|
|
265
|
-
'message' => 'Authorization header is missing.',
|
|
266
|
-
]);
|
|
267
|
-
}
|
|
268
|
-
exit;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Strip "Bearer " prefix
|
|
272
|
-
$token = '';
|
|
273
|
-
if (preg_match('/^Bearer\s+(.+)$/i', $authHeader, $matches)) {
|
|
274
|
-
$token = $matches[1];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (empty($token)) {
|
|
278
|
-
if (function_exists('apiResponse')) {
|
|
279
|
-
apiResponse(401, 'Bearer token is missing.');
|
|
280
|
-
} else {
|
|
281
|
-
http_response_code(401);
|
|
282
|
-
header('Content-Type: application/json');
|
|
283
|
-
echo json_encode([
|
|
284
|
-
'status' => 'error',
|
|
285
|
-
'code' => 401,
|
|
286
|
-
'message' => 'Bearer token is missing.',
|
|
287
|
-
]);
|
|
288
|
-
}
|
|
289
|
-
exit;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
$result = $instance->verify($token);
|
|
293
|
-
|
|
294
|
-
if ($result['status'] !== 'success') {
|
|
295
|
-
if (function_exists('apiResponse')) {
|
|
296
|
-
apiResponse(401, $result['message']);
|
|
297
|
-
} else {
|
|
298
|
-
http_response_code(401);
|
|
299
|
-
header('Content-Type: application/json');
|
|
300
|
-
echo json_encode([
|
|
301
|
-
'status' => 'error',
|
|
302
|
-
'code' => 401,
|
|
303
|
-
'message' => $result['message'],
|
|
304
|
-
]);
|
|
305
|
-
}
|
|
306
|
-
exit;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Attach decoded user payload to the request superglobal
|
|
310
|
-
$_REQUEST['jwt_user'] = $result['data'];
|
|
311
|
-
}
|
|
312
|
-
}
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Src\Auth;
|
|
4
|
+
|
|
5
|
+
use Firebase\JWT\JWT;
|
|
6
|
+
use Firebase\JWT\Key;
|
|
7
|
+
use Firebase\JWT\ExpiredException;
|
|
8
|
+
use Firebase\JWT\SignatureInvalidException;
|
|
9
|
+
use Firebase\JWT\BeforeValidException;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* JWT Authentication Service
|
|
13
|
+
*
|
|
14
|
+
* Provides a clean wrapper around firebase/php-jwt for token generation,
|
|
15
|
+
* verification, refresh, and HTTP middleware integration.
|
|
16
|
+
*
|
|
17
|
+
* Required environment variables:
|
|
18
|
+
* JWT_SECRET_KEY — Secret key used for signing and verifying tokens
|
|
19
|
+
* JWT_ALGORITHM — Signing algorithm (e.g. HS256, HS384, HS512)
|
|
20
|
+
* JWT_EXPIRY — Default token lifetime in seconds (e.g. 3600)
|
|
21
|
+
*
|
|
22
|
+
* @package Auth
|
|
23
|
+
*/
|
|
24
|
+
class JwtAuth
|
|
25
|
+
{
|
|
26
|
+
/**
|
|
27
|
+
* Secret key for signing tokens.
|
|
28
|
+
*
|
|
29
|
+
* @var string
|
|
30
|
+
*/
|
|
31
|
+
private string $secretKey;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Signing algorithm (HS256, HS384, HS512, etc.).
|
|
35
|
+
*
|
|
36
|
+
* @var string
|
|
37
|
+
*/
|
|
38
|
+
private string $algorithm;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Default token expiry in seconds.
|
|
42
|
+
*
|
|
43
|
+
* @var int
|
|
44
|
+
*/
|
|
45
|
+
private int $expiry;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a new JwtAuth instance.
|
|
49
|
+
*
|
|
50
|
+
* Loads the secret key, algorithm, and default expiry from $_ENV.
|
|
51
|
+
*/
|
|
52
|
+
public function __construct()
|
|
53
|
+
{
|
|
54
|
+
$this->secretKey = $_ENV['JWT_SECRET_KEY'];
|
|
55
|
+
$this->algorithm = $_ENV['JWT_ALGORITHM'] ?? 'HS256';
|
|
56
|
+
$this->expiry = (int) ($_ENV['JWT_EXPIRY'] ?? 3600);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate a signed JWT token.
|
|
61
|
+
*
|
|
62
|
+
* Merges the given payload with standard claims (iat, exp) and encodes it.
|
|
63
|
+
*
|
|
64
|
+
* @param array $payload Custom claims to include in the token
|
|
65
|
+
* @param int|null $expiry Token lifetime in seconds (null = use default from $_ENV)
|
|
66
|
+
* @return string Encoded JWT token string
|
|
67
|
+
*/
|
|
68
|
+
public function generate(array $payload, int $expiry = null): string
|
|
69
|
+
{
|
|
70
|
+
$now = time();
|
|
71
|
+
$expiresIn = $expiry ?? $this->expiry;
|
|
72
|
+
|
|
73
|
+
$tokenPayload = array_merge($payload, [
|
|
74
|
+
'iat' => $now,
|
|
75
|
+
'exp' => $now + $expiresIn,
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
return JWT::encode($tokenPayload, $this->secretKey, $this->algorithm);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Verify and decode a JWT token.
|
|
83
|
+
*
|
|
84
|
+
* Returns a standardised response array with the decoded payload on success
|
|
85
|
+
* or an error message on failure.
|
|
86
|
+
*
|
|
87
|
+
* @param string $token The JWT token to verify
|
|
88
|
+
* @return array {
|
|
89
|
+
* @type string $status 'success' or 'error'
|
|
90
|
+
* @type string $message Human-readable result description
|
|
91
|
+
* @type array $data Decoded payload (on success only)
|
|
92
|
+
* }
|
|
93
|
+
*/
|
|
94
|
+
public function verify(string $token): array
|
|
95
|
+
{
|
|
96
|
+
try {
|
|
97
|
+
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
98
|
+
$payload = (array) $decoded;
|
|
99
|
+
|
|
100
|
+
return [
|
|
101
|
+
'status' => 'success',
|
|
102
|
+
'message' => 'Token is valid.',
|
|
103
|
+
'data' => $payload,
|
|
104
|
+
];
|
|
105
|
+
} catch (ExpiredException $e) {
|
|
106
|
+
return [
|
|
107
|
+
'status' => 'error',
|
|
108
|
+
'message' => 'Token has expired.',
|
|
109
|
+
'data' => [],
|
|
110
|
+
];
|
|
111
|
+
} catch (SignatureInvalidException $e) {
|
|
112
|
+
return [
|
|
113
|
+
'status' => 'error',
|
|
114
|
+
'message' => 'Invalid token signature.',
|
|
115
|
+
'data' => [],
|
|
116
|
+
];
|
|
117
|
+
} catch (BeforeValidException $e) {
|
|
118
|
+
return [
|
|
119
|
+
'status' => 'error',
|
|
120
|
+
'message' => 'Token is not yet valid.',
|
|
121
|
+
'data' => [],
|
|
122
|
+
];
|
|
123
|
+
} catch (\Exception $e) {
|
|
124
|
+
return [
|
|
125
|
+
'status' => 'error',
|
|
126
|
+
'message' => 'Token verification failed: ' . $e->getMessage(),
|
|
127
|
+
'data' => [],
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Extract the payload from a token without throwing exceptions.
|
|
134
|
+
*
|
|
135
|
+
* Useful when you need to inspect claims even if the token might be invalid.
|
|
136
|
+
* Returns null on any failure instead of throwing.
|
|
137
|
+
*
|
|
138
|
+
* @param string $token The JWT token to decode
|
|
139
|
+
* @return array|null Decoded payload or null on failure
|
|
140
|
+
*/
|
|
141
|
+
public function payload(string $token): ?array
|
|
142
|
+
{
|
|
143
|
+
try {
|
|
144
|
+
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
145
|
+
return (array) $decoded;
|
|
146
|
+
} catch (\Exception $e) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Refresh a token by generating a new one with the same payload.
|
|
153
|
+
*
|
|
154
|
+
* Strips the old iat/exp claims and re-signs with a fresh expiry.
|
|
155
|
+
* Returns null if the original token cannot be decoded.
|
|
156
|
+
*
|
|
157
|
+
* @param string $token The current JWT token to refresh
|
|
158
|
+
* @param int|null $expiry New expiry duration in seconds (null = use default)
|
|
159
|
+
* @return string|null New JWT token or null on failure
|
|
160
|
+
*/
|
|
161
|
+
public function refresh(string $token, int $expiry = null): ?string
|
|
162
|
+
{
|
|
163
|
+
try {
|
|
164
|
+
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
165
|
+
$payload = (array) $decoded;
|
|
166
|
+
|
|
167
|
+
// Remove old timing claims so generate() sets fresh ones
|
|
168
|
+
unset($payload['iat'], $payload['exp'], $payload['nbf']);
|
|
169
|
+
|
|
170
|
+
return $this->generate($payload, $expiry);
|
|
171
|
+
} catch (\Exception $e) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check whether a token has expired.
|
|
178
|
+
*
|
|
179
|
+
* Attempts to decode the token and catches ExpiredException specifically.
|
|
180
|
+
* Returns true if expired, false if still valid or on other errors.
|
|
181
|
+
*
|
|
182
|
+
* @param string $token The JWT token to check
|
|
183
|
+
* @return bool True if the token is expired
|
|
184
|
+
*/
|
|
185
|
+
public function isExpired(string $token): bool
|
|
186
|
+
{
|
|
187
|
+
try {
|
|
188
|
+
JWT::decode($token, new Key($this->secretKey, $this->algorithm));
|
|
189
|
+
return false;
|
|
190
|
+
} catch (ExpiredException $e) {
|
|
191
|
+
return true;
|
|
192
|
+
} catch (\Exception $e) {
|
|
193
|
+
// Token is invalid for another reason, not strictly "expired"
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Extract the user_id claim from a token.
|
|
200
|
+
*
|
|
201
|
+
* A convenience shortcut for the most common payload lookup.
|
|
202
|
+
*
|
|
203
|
+
* @param string $token The JWT token
|
|
204
|
+
* @return mixed The user_id value or null if not present / token invalid
|
|
205
|
+
*/
|
|
206
|
+
public function getUserId(string $token): mixed
|
|
207
|
+
{
|
|
208
|
+
$payload = $this->payload($token);
|
|
209
|
+
|
|
210
|
+
if ($payload === null) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return $payload['user_id'] ?? null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Generate a long-lived refresh token.
|
|
219
|
+
*
|
|
220
|
+
* Identical to generate() but with a longer default expiry (7 days).
|
|
221
|
+
* Adds a 'token_type' => 'refresh' claim to distinguish from access tokens.
|
|
222
|
+
*
|
|
223
|
+
* @param array $payload Custom claims to include
|
|
224
|
+
* @param int $expiry Lifetime in seconds (default: 604800 = 7 days)
|
|
225
|
+
* @return string Encoded refresh token
|
|
226
|
+
*/
|
|
227
|
+
public function generateRefreshToken(array $payload, int $expiry = 604800): string
|
|
228
|
+
{
|
|
229
|
+
$payload['token_type'] = 'refresh';
|
|
230
|
+
|
|
231
|
+
return $this->generate($payload, $expiry);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* HTTP middleware that validates the Authorization header.
|
|
236
|
+
*
|
|
237
|
+
* Reads the Bearer token from the Authorization header, verifies it, and
|
|
238
|
+
* sets $_REQUEST['jwt_user'] with the decoded payload on success.
|
|
239
|
+
* On failure, sends a 401 JSON response via apiResponse() and exits.
|
|
240
|
+
*
|
|
241
|
+
* Usage:
|
|
242
|
+
* JwtAuth::middleware();
|
|
243
|
+
* // If execution reaches here, $_REQUEST['jwt_user'] is available
|
|
244
|
+
*
|
|
245
|
+
* @return void
|
|
246
|
+
*/
|
|
247
|
+
public static function middleware(): void
|
|
248
|
+
{
|
|
249
|
+
$instance = new self();
|
|
250
|
+
|
|
251
|
+
// Extract Bearer token from Authorization header
|
|
252
|
+
$authHeader = $_SERVER['HTTP_AUTHORIZATION']
|
|
253
|
+
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
|
|
254
|
+
?? '';
|
|
255
|
+
|
|
256
|
+
if (empty($authHeader)) {
|
|
257
|
+
if (function_exists('apiResponse')) {
|
|
258
|
+
apiResponse(401, 'Authorization header is missing.');
|
|
259
|
+
} else {
|
|
260
|
+
http_response_code(401);
|
|
261
|
+
header('Content-Type: application/json');
|
|
262
|
+
echo json_encode([
|
|
263
|
+
'status' => 'error',
|
|
264
|
+
'code' => 401,
|
|
265
|
+
'message' => 'Authorization header is missing.',
|
|
266
|
+
]);
|
|
267
|
+
}
|
|
268
|
+
exit;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Strip "Bearer " prefix
|
|
272
|
+
$token = '';
|
|
273
|
+
if (preg_match('/^Bearer\s+(.+)$/i', $authHeader, $matches)) {
|
|
274
|
+
$token = $matches[1];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (empty($token)) {
|
|
278
|
+
if (function_exists('apiResponse')) {
|
|
279
|
+
apiResponse(401, 'Bearer token is missing.');
|
|
280
|
+
} else {
|
|
281
|
+
http_response_code(401);
|
|
282
|
+
header('Content-Type: application/json');
|
|
283
|
+
echo json_encode([
|
|
284
|
+
'status' => 'error',
|
|
285
|
+
'code' => 401,
|
|
286
|
+
'message' => 'Bearer token is missing.',
|
|
287
|
+
]);
|
|
288
|
+
}
|
|
289
|
+
exit;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
$result = $instance->verify($token);
|
|
293
|
+
|
|
294
|
+
if ($result['status'] !== 'success') {
|
|
295
|
+
if (function_exists('apiResponse')) {
|
|
296
|
+
apiResponse(401, $result['message']);
|
|
297
|
+
} else {
|
|
298
|
+
http_response_code(401);
|
|
299
|
+
header('Content-Type: application/json');
|
|
300
|
+
echo json_encode([
|
|
301
|
+
'status' => 'error',
|
|
302
|
+
'code' => 401,
|
|
303
|
+
'message' => $result['message'],
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
exit;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Attach decoded user payload to the request superglobal
|
|
310
|
+
$_REQUEST['jwt_user'] = $result['data'];
|
|
311
|
+
}
|
|
312
|
+
}
|