create-prisma-php-app 4.0.0-alpha.2 → 4.0.0-alpha.21

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.
Files changed (59) hide show
  1. package/dist/.htaccess +54 -41
  2. package/dist/bootstrap.php +143 -98
  3. package/dist/index.js +264 -99
  4. package/dist/settings/auto-swagger-docs.ts +196 -95
  5. package/dist/settings/bs-config.ts +56 -58
  6. package/dist/settings/files-list.json +1 -1
  7. package/dist/settings/restart-mcp.ts +58 -0
  8. package/dist/settings/restart-websocket.ts +51 -45
  9. package/dist/settings/utils.ts +240 -0
  10. package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
  11. package/dist/src/Lib/Auth/Auth.php +544 -0
  12. package/dist/src/Lib/Auth/AuthConfig.php +89 -0
  13. package/dist/src/Lib/CacheHandler.php +121 -0
  14. package/dist/src/Lib/ErrorHandler.php +322 -0
  15. package/dist/src/Lib/FileManager/UploadFile.php +383 -0
  16. package/dist/src/Lib/Headers/Boom.php +192 -0
  17. package/dist/src/Lib/IncludeTracker.php +59 -0
  18. package/dist/src/Lib/MCP/WeatherTools.php +104 -0
  19. package/dist/src/Lib/MCP/mcp-server.php +80 -0
  20. package/dist/src/Lib/MainLayout.php +230 -0
  21. package/dist/src/Lib/Middleware/AuthMiddleware.php +154 -0
  22. package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
  23. package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
  24. package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
  25. package/dist/src/Lib/PHPX/Fragment.php +32 -0
  26. package/dist/src/Lib/PHPX/IPHPX.php +22 -0
  27. package/dist/src/Lib/PHPX/PHPX.php +287 -0
  28. package/dist/src/Lib/PHPX/TemplateCompiler.php +641 -0
  29. package/dist/src/Lib/PHPX/TwMerge.php +346 -0
  30. package/dist/src/Lib/PHPX/TypeCoercer.php +490 -0
  31. package/dist/src/Lib/PartialRenderer.php +40 -0
  32. package/dist/src/Lib/PrismaPHPSettings.php +181 -0
  33. package/dist/src/Lib/Request.php +479 -0
  34. package/dist/src/Lib/Security/RateLimiter.php +33 -0
  35. package/dist/src/Lib/Set.php +102 -0
  36. package/dist/src/Lib/StateManager.php +127 -0
  37. package/dist/src/Lib/Validator.php +752 -0
  38. package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
  39. package/dist/src/Lib/Websocket/websocket-server.php +118 -0
  40. package/dist/src/app/error.php +1 -1
  41. package/dist/src/app/index.php +24 -5
  42. package/dist/src/app/js/index.js +1 -1
  43. package/dist/src/app/layout.php +2 -2
  44. package/package.json +1 -1
  45. package/dist/settings/restart-websocket.bat +0 -28
  46. package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
  47. package/dist/websocket-server.php +0 -22
  48. package/vendor/autoload.php +0 -25
  49. package/vendor/composer/ClassLoader.php +0 -579
  50. package/vendor/composer/InstalledVersions.php +0 -359
  51. package/vendor/composer/LICENSE +0 -21
  52. package/vendor/composer/autoload_classmap.php +0 -10
  53. package/vendor/composer/autoload_namespaces.php +0 -9
  54. package/vendor/composer/autoload_psr4.php +0 -10
  55. package/vendor/composer/autoload_real.php +0 -38
  56. package/vendor/composer/autoload_static.php +0 -25
  57. package/vendor/composer/installed.json +0 -825
  58. package/vendor/composer/installed.php +0 -132
  59. package/vendor/composer/platform_check.php +0 -26
@@ -0,0 +1,544 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\Auth;
6
+
7
+ use Firebase\JWT\JWT;
8
+ use Firebase\JWT\Key;
9
+ use DateInterval;
10
+ use DateTime;
11
+ use Lib\Validator;
12
+ use GuzzleHttp\Client;
13
+ use GuzzleHttp\Exception\RequestException;
14
+ use Lib\Request;
15
+ use Exception;
16
+ use InvalidArgumentException;
17
+ use ArrayObject;
18
+
19
+ class Auth
20
+ {
21
+ public const PAYLOAD_NAME = 'payload_name_8639D';
22
+ public const ROLE_NAME = '';
23
+ public const PAYLOAD_SESSION_KEY = 'payload_session_key_2183A';
24
+
25
+ public static string $cookieName = '';
26
+
27
+ private static ?Auth $instance = null;
28
+ private const PPHPAUTH = 'pphpauth';
29
+ private string $secretKey;
30
+ private string $defaultTokenValidity = '1h'; // Default to 1 hour
31
+
32
+ /**
33
+ * Private constructor to prevent direct instantiation.
34
+ * Use Auth::getInstance() to get the singleton instance.
35
+ */
36
+ private function __construct()
37
+ {
38
+ $this->secretKey = $_ENV['AUTH_SECRET'] ?? 'CD24eEv4qbsC5LOzqeaWbcr58mBMSvA4Mkii8GjRiHkt';
39
+ self::$cookieName = self::getCookieName();
40
+ }
41
+
42
+ /**
43
+ * Returns the singleton instance of the Auth class.
44
+ *
45
+ * @return Auth The singleton instance.
46
+ */
47
+ public static function getInstance(): Auth
48
+ {
49
+ if (self::$instance === null) {
50
+ self::$instance = new self();
51
+ }
52
+ return self::$instance;
53
+ }
54
+
55
+ /**
56
+ * Authenticates a user and generates a JWT (JSON Web Token) based on the specified user data
57
+ * and token validity duration. The method first checks if the secret key is set, calculates
58
+ * the token's expiration time, sets the necessary payload, and encodes it into a JWT.
59
+ * If possible (HTTP headers not yet sent), it also sets cookies with the JWT for client-side storage.
60
+ *
61
+ * @param mixed $data User data which can be a simple string or an instance of AuthRole.
62
+ * If an instance of AuthRole is provided, its `value` property will be used as the role in the token.
63
+ * @param string|null $tokenValidity Optional parameter specifying the duration the token is valid for (e.g., '10m', '1h').
64
+ * If null, the default validity period set in the class property is used, which is 1 hour.
65
+ * The format should be a number followed by a time unit ('s' for seconds, 'm' for minutes,
66
+ * 'h' for hours, 'd' for days), and this is parsed to calculate the exact expiration time.
67
+ *
68
+ * @return string Returns the encoded JWT as a string.
69
+ *
70
+ * @throws InvalidArgumentException Thrown if the secret key is not set or if the duration format is invalid.
71
+ *
72
+ * Example:
73
+ * $auth = Auth::getInstance();
74
+ * $auth->setSecretKey('your_secret_key');
75
+ * try {
76
+ * $jwt = $auth->signIn('Admin', '1h');
77
+ * echo "JWT: " . $jwt;
78
+ * } catch (InvalidArgumentException $e) {
79
+ * echo "Error: " . $e->getMessage();
80
+ * }
81
+ */
82
+ public function signIn($data, ?string $tokenValidity = null): string
83
+ {
84
+ if (!$this->secretKey) {
85
+ throw new InvalidArgumentException("Secret key is required for authentication.");
86
+ }
87
+
88
+ $expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
89
+
90
+ if ($data instanceof AuthRole) {
91
+ $data = $data->value;
92
+ }
93
+
94
+ $payload = [
95
+ self::PAYLOAD_NAME => $data,
96
+ 'exp' => $expirationTime,
97
+ ];
98
+
99
+ // Set the payload in the session
100
+ $_SESSION[self::PAYLOAD_SESSION_KEY] = $payload;
101
+
102
+ // Encode the JWT
103
+ $jwt = JWT::encode($payload, $this->secretKey, 'HS256');
104
+
105
+ if (!headers_sent()) {
106
+ $this->setCookies($jwt, $expirationTime);
107
+ }
108
+
109
+ return $jwt;
110
+ }
111
+
112
+ /**
113
+ * Checks if the user is authenticated based on the presence of the payload in the session.
114
+ * Returns true if the user is authenticated, false otherwise.
115
+ *
116
+ * @return bool Returns true if the user is authenticated, false otherwise.
117
+ */
118
+ public function isAuthenticated(): bool
119
+ {
120
+ if (!isset($_COOKIE[self::$cookieName])) {
121
+ unset($_SESSION[self::PAYLOAD_SESSION_KEY]);
122
+ return false;
123
+ }
124
+
125
+ if (Request::$fileToInclude === 'route.php') {
126
+ $bearerToken = Request::getBearerToken();
127
+ $verifyBearerToken = $this->verifyToken($bearerToken);
128
+ if (!$verifyBearerToken) {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ $jwt = $_COOKIE[self::$cookieName];
134
+ $verifyToken = $this->verifyToken($jwt);
135
+ if ($verifyToken === false) {
136
+ return false;
137
+ }
138
+
139
+ if (!isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
140
+ return false;
141
+ }
142
+
143
+ return true;
144
+ }
145
+
146
+ private function calculateExpirationTime(string $duration): int
147
+ {
148
+ $now = new DateTime();
149
+ $interval = $this->convertDurationToInterval($duration);
150
+ $futureDate = $now->add($interval);
151
+ return $futureDate->getTimestamp();
152
+ }
153
+
154
+ private function convertDurationToInterval(string $duration): DateInterval
155
+ {
156
+ if (preg_match('/^(\d+)(s|m|h|d)$/', $duration, $matches)) {
157
+ $value = (int)$matches[1];
158
+ $unit = $matches[2];
159
+
160
+ switch ($unit) {
161
+ case 's':
162
+ return new DateInterval("PT{$value}S");
163
+ case 'm':
164
+ return new DateInterval("PT{$value}M");
165
+ case 'h':
166
+ return new DateInterval("PT{$value}H");
167
+ case 'd':
168
+ return new DateInterval("P{$value}D");
169
+ default:
170
+ throw new InvalidArgumentException("Invalid duration format: {$duration}");
171
+ }
172
+ }
173
+
174
+ throw new InvalidArgumentException("Invalid duration format: {$duration}");
175
+ }
176
+
177
+ /**
178
+ * Verifies the JWT token and returns the decoded payload if the token is valid.
179
+ * If the token is invalid or expired, null is returned.
180
+ *
181
+ * @param string $jwt The JWT token to verify.
182
+ * @return object|null Returns the decoded payload if the token is valid, or null if invalid or expired.
183
+ */
184
+ public function verifyToken(?string $jwt): ?object
185
+ {
186
+ try {
187
+ if (!$jwt) {
188
+ return null;
189
+ }
190
+
191
+ $token = JWT::decode($jwt, new Key($this->secretKey, 'HS256'));
192
+
193
+ if (empty($token->{Auth::PAYLOAD_NAME})) {
194
+ return null;
195
+ }
196
+
197
+ if (isset($token->exp) && time() >= $token->exp) {
198
+ return null;
199
+ }
200
+
201
+ return $token;
202
+ } catch (Exception) {
203
+ return null;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Refreshes the JWT token by updating the expiration time and encoding the new payload into a JWT.
209
+ * If the token validity duration is not specified, the default token validity period is used.
210
+ * If possible (HTTP headers not yet sent), it also sets cookies with the new JWT for client-side storage.
211
+ *
212
+ * @param string $jwt The JWT token to refresh.
213
+ * @param string|null $tokenValidity Optional parameter specifying the duration the token is valid for (e.g., '10m', '1h').
214
+ * If null, the default validity period set in the class property is used.
215
+ * The format should be a number followed by a time unit ('s' for seconds, 'm' for minutes,
216
+ * 'h' for hours, 'd' for days), and this is parsed to calculate the exact expiration time.
217
+ *
218
+ * @return string Returns the refreshed JWT as a string.
219
+ *
220
+ * @throws InvalidArgumentException Thrown if the token is invalid.
221
+ */
222
+ public function refreshToken(string $jwt, ?string $tokenValidity = null): string
223
+ {
224
+ $decodedToken = $this->verifyToken($jwt);
225
+
226
+ if (!$decodedToken) {
227
+ throw new InvalidArgumentException("Invalid token.");
228
+ }
229
+
230
+ $expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
231
+
232
+ $decodedToken->exp = $expirationTime;
233
+ $newJwt = JWT::encode((array)$decodedToken, $this->secretKey, 'HS256');
234
+
235
+ if (!headers_sent()) {
236
+ $this->setCookies($newJwt, $expirationTime);
237
+ }
238
+
239
+ return $newJwt;
240
+ }
241
+
242
+ protected function setCookies(string $jwt, int $expirationTime)
243
+ {
244
+ if (!headers_sent()) {
245
+ setcookie(self::$cookieName, $jwt, [
246
+ 'expires' => $expirationTime,
247
+ 'path' => '/', // Set the path to '/' to make the cookie available site-wide
248
+ 'domain' => '', // Specify your domain
249
+ 'secure' => true, // Set to true if using HTTPS
250
+ 'httponly' => true, // Prevent JavaScript access to the cookie
251
+ 'samesite' => 'Lax', // or 'Strict' depending on your requirements
252
+ ]);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Logs out the user by unsetting the session payload and deleting the authentication cookie.
258
+ * If a redirect URL is provided, the user is redirected to that URL after logging out.
259
+ *
260
+ * @param string|null $redirect Optional parameter specifying the URL to redirect to after logging out.
261
+ *
262
+ * Example:
263
+ * $auth = Auth::getInstance();
264
+ * $auth->signOut('/login');
265
+ *
266
+ * @return void
267
+ */
268
+ public function signOut(?string $redirect = null)
269
+ {
270
+ if (isset($_COOKIE[self::$cookieName])) {
271
+ unset($_COOKIE[self::$cookieName]);
272
+ setcookie(self::$cookieName, '', time() - 3600, '/');
273
+ }
274
+
275
+ if (isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
276
+ unset($_SESSION[self::PAYLOAD_SESSION_KEY]);
277
+ }
278
+
279
+ if ($redirect) {
280
+ Request::redirect($redirect);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Returns the role of the authenticated user based on the payload stored in the session.
286
+ * If the user is not authenticated, null is returned.
287
+ *
288
+ * @return mixed|null Returns the role of the authenticated user or null if the user is not authenticated.
289
+ */
290
+ public function getPayload()
291
+ {
292
+ if (isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
293
+ $value = $_SESSION[self::PAYLOAD_SESSION_KEY][self::PAYLOAD_NAME];
294
+ return is_array($value) ? new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS) : $value;
295
+ }
296
+
297
+ return null;
298
+ }
299
+
300
+ private function exchangeCode($data, $apiUrl)
301
+ {
302
+ try {
303
+ $client = new Client();
304
+ $response = $client->post($apiUrl, [
305
+ 'headers' => [
306
+ 'Accept' => 'application/json',
307
+ ],
308
+ 'form_params' => $data,
309
+ ]);
310
+
311
+ if ($response->getStatusCode() === 200) {
312
+ return json_decode($response->getBody()->getContents());
313
+ }
314
+
315
+ return false;
316
+ } catch (RequestException) {
317
+ return false;
318
+ }
319
+ }
320
+
321
+ private function saveAuthInfo($responseInfo, $accountData)
322
+ {
323
+ // Save user data to the database
324
+ }
325
+
326
+ private function findProvider(array $providers, string $type): ?object
327
+ {
328
+ foreach ($providers as $provider) {
329
+ if (is_object($provider) && get_class($provider) === $type) {
330
+ return $provider;
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+
336
+ /**
337
+ * Authenticates a user using OAuth providers such as Google or GitHub.
338
+ * The method first checks if the request is a GET request and if the route is a sign-in route.
339
+ * It then processes the authentication code received from the provider and retrieves the user's data.
340
+ * The user data is saved to the database, and the user is authenticated using the authenticate method.
341
+ *
342
+ * @param mixed ...$providers An array of provider objects such as GoogleProvider or GithubProvider.
343
+ *
344
+ * Example:
345
+ * $auth = Auth::getInstance();
346
+ * $auth->authProviders(new GoogleProvider('client_id', 'client_secret', 'redirect_uri'));
347
+ */
348
+ public function authProviders(...$providers)
349
+ {
350
+ $dynamicRouteParams = Request::$dynamicParams[self::PPHPAUTH] ?? [];
351
+
352
+ if (Request::$isGet && in_array('signin', $dynamicRouteParams)) {
353
+ foreach ($providers as $provider) {
354
+ if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams)) {
355
+ $githubAuthUrl = "https://github.com/login/oauth/authorize?scope=user:email%20read:user&client_id={$provider->clientId}";
356
+ Request::redirect($githubAuthUrl);
357
+ } elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams)) {
358
+ $googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth?"
359
+ . "scope=" . urlencode('email profile') . "&"
360
+ . "response_type=code&"
361
+ . "client_id=" . urlencode($provider->clientId) . "&"
362
+ . "redirect_uri=" . urlencode($provider->redirectUri);
363
+ Request::redirect($googleAuthUrl);
364
+ }
365
+ }
366
+ }
367
+
368
+ $authCode = Validator::string($_GET['code'] ?? '');
369
+
370
+ if (Request::$isGet && in_array('callback', $dynamicRouteParams) && isset($authCode)) {
371
+ if (in_array('github', $dynamicRouteParams)) {
372
+ $provider = $this->findProvider($providers, GithubProvider::class);
373
+
374
+ if (!$provider) {
375
+ exit("Error occurred. Please try again.");
376
+ }
377
+
378
+ return $this->githubProvider($provider, $authCode);
379
+ } elseif (in_array('google', $dynamicRouteParams)) {
380
+ $provider = $this->findProvider($providers, GoogleProvider::class);
381
+
382
+ if (!$provider) {
383
+ exit("Error occurred. Please try again.");
384
+ }
385
+
386
+ return $this->googleProvider($provider, $authCode);
387
+ }
388
+ }
389
+
390
+ exit("Error occurred. Please try again.");
391
+ }
392
+
393
+ private function githubProvider(GithubProvider $githubProvider, string $authCode)
394
+ {
395
+ $gitToken = [
396
+ 'client_id' => $githubProvider->clientId,
397
+ 'client_secret' => $githubProvider->clientSecret,
398
+ 'code' => $authCode,
399
+ ];
400
+
401
+ $apiUrl = 'https://github.com/login/oauth/access_token';
402
+ $tokenData = (object)$this->exchangeCode($gitToken, $apiUrl);
403
+
404
+ if (!$tokenData) {
405
+ exit("Error occurred. Please try again.");
406
+ }
407
+
408
+ if (isset($tokenData->error)) {
409
+ exit("Error occurred. Please try again.");
410
+ }
411
+
412
+ if (isset($tokenData->access_token)) {
413
+ $client = new Client();
414
+ $emailResponse = $client->get('https://api.github.com/user/emails', [
415
+ 'headers' => [
416
+ 'Authorization' => 'Bearer ' . $tokenData->access_token,
417
+ 'Accept' => 'application/json',
418
+ ],
419
+ ]);
420
+
421
+ $emails = json_decode($emailResponse->getBody()->getContents(), true);
422
+
423
+ $primaryEmail = array_reduce($emails, function ($carry, $item) {
424
+ return ($item['primary'] && $item['verified']) ? $item['email'] : $carry;
425
+ }, null);
426
+
427
+ $response = $client->get('https://api.github.com/user', [
428
+ 'headers' => [
429
+ 'Accept' => 'application/json',
430
+ 'Authorization' => 'Bearer ' . $tokenData->access_token,
431
+ ],
432
+ ]);
433
+
434
+ if ($response->getStatusCode() == 200) {
435
+ $responseInfo = json_decode($response->getBody()->getContents());
436
+
437
+ $accountData = [
438
+ 'provider' => 'github',
439
+ 'type' => 'oauth',
440
+ 'providerAccountId' => "$responseInfo->id",
441
+ 'access_token' => $tokenData->access_token,
442
+ 'expires_at' => $tokenData->expires_at ?? null,
443
+ 'token_type' => $tokenData->token_type,
444
+ 'scope' => $tokenData->scope,
445
+ ];
446
+
447
+ $this->saveAuthInfo($responseInfo, $accountData);
448
+
449
+ $userToAuthenticate = [
450
+ 'name' => $responseInfo->login,
451
+ 'email' => $primaryEmail,
452
+ 'image' => $responseInfo->avatar_url,
453
+ 'Account' => (object)$accountData
454
+ ];
455
+ $userToAuthenticate = (object)$userToAuthenticate;
456
+
457
+ $this->signIn($userToAuthenticate, $githubProvider->maxAge);
458
+ }
459
+ }
460
+ }
461
+
462
+ private function googleProvider(GoogleProvider $googleProvider, string $authCode)
463
+ {
464
+ $googleToken = [
465
+ 'client_id' => $googleProvider->clientId,
466
+ 'client_secret' => $googleProvider->clientSecret,
467
+ 'code' => $authCode,
468
+ 'grant_type' => 'authorization_code',
469
+ 'redirect_uri' => $googleProvider->redirectUri
470
+ ];
471
+
472
+ $apiUrl = 'https://oauth2.googleapis.com/token';
473
+ $tokenData = (object)$this->exchangeCode($googleToken, $apiUrl);
474
+
475
+ if (!$tokenData) {
476
+ exit("Error occurred. Please try again.");
477
+ }
478
+
479
+ if (isset($tokenData->error)) {
480
+ exit("Error occurred. Please try again.");
481
+ }
482
+
483
+ if (isset($tokenData->access_token)) {
484
+ $client = new Client();
485
+ $response = $client->get('https://www.googleapis.com/oauth2/v1/userinfo', [
486
+ 'headers' => [
487
+ 'Authorization' => 'Bearer ' . $tokenData->access_token,
488
+ 'Accept' => 'application/json',
489
+ ],
490
+ ]);
491
+
492
+ if ($response->getStatusCode() == 200) {
493
+ $responseInfo = json_decode($response->getBody()->getContents());
494
+
495
+ $accountData = [
496
+ 'provider' => 'google',
497
+ 'type' => 'oauth',
498
+ 'providerAccountId' => "$responseInfo->id",
499
+ 'access_token' => $tokenData->access_token,
500
+ 'expires_at' => $tokenData->expires_at ?? null,
501
+ 'token_type' => $tokenData->token_type,
502
+ 'scope' => $tokenData->scope,
503
+ ];
504
+
505
+ $this->saveAuthInfo($responseInfo, $accountData);
506
+
507
+ $userToAuthenticate = [
508
+ 'name' => $responseInfo->name,
509
+ 'email' => $responseInfo->email,
510
+ 'image' => $responseInfo->picture,
511
+ 'Account' => (object)$accountData
512
+ ];
513
+ $userToAuthenticate = (object)$userToAuthenticate;
514
+
515
+ $this->signIn($userToAuthenticate, $googleProvider->maxAge);
516
+ }
517
+ }
518
+ }
519
+
520
+ private static function getCookieName(): string
521
+ {
522
+ $authCookieName = $_ENV['AUTH_COOKIE_NAME'] ?? 'auth_cookie_name_d36e5';
523
+ return strtolower(preg_replace('/\s+/', '_', trim($authCookieName)));
524
+ }
525
+ }
526
+
527
+ class GoogleProvider
528
+ {
529
+ public function __construct(
530
+ public string $clientId,
531
+ public string $clientSecret,
532
+ public string $redirectUri,
533
+ public string $maxAge = '30d'
534
+ ) {}
535
+ }
536
+
537
+ class GithubProvider
538
+ {
539
+ public function __construct(
540
+ public string $clientId,
541
+ public string $clientSecret,
542
+ public string $maxAge = '30d'
543
+ ) {}
544
+ }
@@ -0,0 +1,89 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\Auth;
6
+
7
+ use ArrayObject;
8
+
9
+ enum AuthRole: string
10
+ {
11
+ case Admin = 'Admin';
12
+ case User = 'User';
13
+
14
+ public function equals($role)
15
+ {
16
+ return $this->value === $role;
17
+ }
18
+ }
19
+
20
+ final class AuthConfig
21
+ {
22
+ public const ROLE_IDENTIFIER = 'role';
23
+ public const IS_ROLE_BASE = false;
24
+ public const IS_TOKEN_AUTO_REFRESH = false;
25
+ public const IS_ALL_ROUTES_PRIVATE = false;
26
+
27
+ /**
28
+ * This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to false,
29
+ * An array of private routes that are accessible to all authenticated users
30
+ * without specific role-based access control. Routes should be listed as string paths.
31
+ * Example: public static $privateRoutes = ['/']; // This makes the home page private
32
+ * Example: public static $privateRoutes = ['/profile', '/dashboard/settings']; // These routes are private
33
+ */
34
+ public static array $privateRoutes = [];
35
+
36
+ /**
37
+ * This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to true,
38
+ * An array of public routes that are accessible to all users, authenticated or not.
39
+ */
40
+ public const DEFAULT_SIGNIN_REDIRECT = '/dashboard'; // Default redirect route after sign in
41
+ public const API_AUTH_PREFIX = '/api/auth'; // Prefix for third-party API authentication routes (github, google, etc.)
42
+
43
+ /**
44
+ * An array of public routes that are accessible to all users, authenticated or not.
45
+ * Routes should be listed as string paths.
46
+ * Example: public static $publicRoutes = ['/']; // This makes the home page public
47
+ * Example: public static $publicRoutes = ['/about', '/contact']; // These routes are public
48
+ */
49
+ public static array $publicRoutes = ['/'];
50
+ public static array $authRoutes = [
51
+ '/signin',
52
+ '/signup',
53
+ ];
54
+
55
+ /**
56
+ * An associative array mapping specific routes to required user roles for access control.
57
+ * Each route is a key with an array of roles that are allowed access.
58
+ * Format:
59
+ * 'route_path' => [self::ROLE_IDENTIFIER => [AuthRole::Role1, AuthRole::Role2, ...]],
60
+ * Example:
61
+ * public static $roleBasedRoutes = [
62
+ * 'dashboard' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]],
63
+ * 'dashboard/users' => [self::ROLE_IDENTIFIER => [AuthRole::Admin]],
64
+ * 'sales' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]]
65
+ * ];
66
+ */
67
+ public static array $roleBasedRoutes = [];
68
+
69
+ /**
70
+ * Checks if the given user role is authorized to access a set of roles.
71
+ *
72
+ * @param ArrayObject|string $userRole The user's role to check.
73
+ * @param array<AuthRole> $roles An array of AuthRole instances specifying allowed roles.
74
+ * @return bool Returns true if the user's role matches any of the allowed roles, false otherwise.
75
+ */
76
+ public static function checkAuthRole(ArrayObject|string $userRole, array $roles): bool
77
+ {
78
+ if ($userRole instanceof ArrayObject) {
79
+ $userRole = $userRole[Auth::ROLE_NAME] ?? '';
80
+ }
81
+
82
+ foreach ($roles as $role) {
83
+ if ($userRole === $role->value) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ }