@artilingo/artiframe-cli 1.1.2 → 1.1.3

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.
@@ -1,312 +0,0 @@
1
- <?php
2
-
3
- namespace 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,232 +0,0 @@
1
- <?php
2
-
3
- namespace Service;
4
-
5
- use Pusher\Pusher;
6
-
7
- /**
8
- * Pusher Realtime Service
9
- *
10
- * Provides a clean interface for Pusher Channels — broadcasting events,
11
- * authenticating private/presence channels, and querying channel state.
12
- * Uses the pusher/pusher-php-server SDK. All configuration from $_ENV.
13
- *
14
- * Required environment variables:
15
- * PUSHER_APP_ID — Your Pusher application ID
16
- * PUSHER_APP_KEY — Your Pusher application key
17
- * PUSHER_APP_SECRET — Your Pusher application secret
18
- * PUSHER_CLUSTER — Pusher cluster region (e.g., 'eu', 'us2', 'ap1')
19
- *
20
- * @package Service
21
- */
22
- class PusherService
23
- {
24
- /**
25
- * The Pusher SDK client instance.
26
- *
27
- * @var \Pusher\Pusher
28
- */
29
- private Pusher $pusher;
30
-
31
- /**
32
- * Create a new PusherService instance.
33
- *
34
- * Initializes the Pusher SDK client using credentials
35
- * and cluster configuration from environment variables.
36
- *
37
- * @throws \Pusher\PusherException If credentials are invalid or missing
38
- */
39
- public function __construct()
40
- {
41
- $this->pusher = new Pusher(
42
- $_ENV['PUSHER_APP_KEY'],
43
- $_ENV['PUSHER_APP_SECRET'],
44
- $_ENV['PUSHER_APP_ID'],
45
- [
46
- 'cluster' => $_ENV['PUSHER_CLUSTER'] ?? 'mt1',
47
- 'useTLS' => true,
48
- 'encrypted' => true,
49
- ]
50
- );
51
- }
52
-
53
- /**
54
- * Broadcast an event to a single channel.
55
- *
56
- * Triggers an event on the specified channel, delivering
57
- * the given data payload to all subscribers of that channel.
58
- *
59
- * Channel naming conventions:
60
- * - Public channels: 'my-channel'
61
- * - Private channels: 'private-my-channel'
62
- * - Presence channels: 'presence-my-channel'
63
- *
64
- * @param string $channel The channel name to broadcast on
65
- * @param string $event The event name to trigger
66
- * @param array $data The event payload data
67
- * @return bool True on success, false on failure
68
- */
69
- public function broadcast(string $channel, string $event, array $data): bool
70
- {
71
- try {
72
- $response = $this->pusher->trigger($channel, $event, $data);
73
-
74
- return (bool) $response;
75
- } catch (\Exception $e) {
76
- return false;
77
- }
78
- }
79
-
80
- /**
81
- * Broadcast an event to multiple channels simultaneously.
82
- *
83
- * Triggers the same event with the same payload across
84
- * multiple channels in a single API call. Pusher limits
85
- * this to a maximum of 100 channels per call.
86
- *
87
- * @param array $channels List of channel names (max 100)
88
- * @param string $event The event name to trigger
89
- * @param array $data The event payload data
90
- * @return bool True on success, false on failure
91
- */
92
- public function broadcastToMany(array $channels, string $event, array $data): bool
93
- {
94
- try {
95
- $response = $this->pusher->trigger($channels, $event, $data);
96
-
97
- return (bool) $response;
98
- } catch (\Exception $e) {
99
- return false;
100
- }
101
- }
102
-
103
- /**
104
- * Send an event to a specific user via their user channel.
105
- *
106
- * Uses Pusher's user event feature to deliver a message directly
107
- * to a single authenticated user, regardless of which channels
108
- * they are subscribed to. The user must have been authenticated
109
- * with a user ID on the client side.
110
- *
111
- * @param string $userId The target user's unique identifier
112
- * @param string $event The event name to trigger
113
- * @param array $data The event payload data
114
- * @return bool True on success, false on failure
115
- */
116
- public function toUser(string $userId, string $event, array $data): bool
117
- {
118
- try {
119
- $response = $this->pusher->sendToUser($userId, $event, $data);
120
-
121
- return (bool) $response;
122
- } catch (\Exception $e) {
123
- return false;
124
- }
125
- }
126
-
127
- /**
128
- * Get information about a specific channel.
129
- *
130
- * Returns metadata about the channel including whether it is
131
- * currently occupied and, for presence channels, the user count.
132
- *
133
- * @param string $channel The channel name to query
134
- * @return array Channel info ['occupied' => bool, 'user_count' => int|null]
135
- * Returns empty array on failure
136
- */
137
- public function channelInfo(string $channel): array
138
- {
139
- try {
140
- $info = $this->pusher->getChannelInfo($channel, [
141
- 'info' => 'user_count,subscription_count',
142
- ]);
143
-
144
- return [
145
- 'occupied' => $info->occupied ?? false,
146
- 'user_count' => $info->user_count ?? null,
147
- 'subscription_count' => $info->subscription_count ?? null,
148
- ];
149
- } catch (\Exception $e) {
150
- return [];
151
- }
152
- }
153
-
154
- /**
155
- * List all active (occupied) channels.
156
- *
157
- * Optionally filters channels by a name prefix. For example,
158
- * passing 'presence-' will return only active presence channels.
159
- *
160
- * @param string $prefix Optional channel name prefix filter
161
- * @return array List of active channel names
162
- */
163
- public function channels(string $prefix = ''): array
164
- {
165
- try {
166
- $params = [];
167
-
168
- if ($prefix !== '') {
169
- $params['filter_by_prefix'] = $prefix;
170
- }
171
-
172
- $result = $this->pusher->getChannels($params);
173
-
174
- if (isset($result->channels)) {
175
- return array_keys((array) $result->channels);
176
- }
177
-
178
- return [];
179
- } catch (\Exception $e) {
180
- return [];
181
- }
182
- }
183
-
184
- /**
185
- * Authenticate a private or presence channel subscription.
186
- *
187
- * This method should be called from your authentication endpoint
188
- * when a client attempts to subscribe to a private or presence channel.
189
- * Returns the authentication signature string that the client SDK expects.
190
- *
191
- * For presence channels, use presenceAuth() instead to include user data.
192
- *
193
- * @param string $channelName The channel being subscribed to
194
- * @param string $socketId The connecting socket's ID
195
- * @return string JSON-encoded authentication response
196
- */
197
- public function auth(string $channelName, string $socketId): string
198
- {
199
- try {
200
- return $this->pusher->authorizeChannel($channelName, $socketId);
201
- } catch (\Exception $e) {
202
- return json_encode(['error' => $e->getMessage()]);
203
- }
204
- }
205
-
206
- /**
207
- * Authenticate a presence channel subscription with user data.
208
- *
209
- * Similar to auth() but also attaches user identity and optional
210
- * metadata to the presence channel. This allows other subscribers
211
- * to see who is present in the channel.
212
- *
213
- * @param string $channelName The presence channel being subscribed to
214
- * @param string $socketId The connecting socket's ID
215
- * @param string $userId The unique identifier for the subscribing user
216
- * @param array $userInfo Optional additional user metadata (e.g., name, avatar)
217
- * @return string JSON-encoded authentication response with user data
218
- */
219
- public function presenceAuth(string $channelName, string $socketId, string $userId, array $userInfo = []): string
220
- {
221
- try {
222
- $presenceData = [
223
- 'user_id' => $userId,
224
- 'user_info' => $userInfo,
225
- ];
226
-
227
- return $this->pusher->authorizePresenceChannel($channelName, $socketId, $userId, $userInfo);
228
- } catch (\Exception $e) {
229
- return json_encode(['error' => $e->getMessage()]);
230
- }
231
- }
232
- }