create-prisma-php-app 5.0.0-alpha.2 → 5.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.
@@ -22,6 +22,7 @@ class Auth
22
22
  public const PAYLOAD_NAME = 'payload_name_8639D';
23
23
  public const ROLE_NAME = 'role';
24
24
  public const PAYLOAD_SESSION_KEY = 'payload_session_key_2183A';
25
+ private const OAUTH_STATE_SESSION_KEY = 'oauth_state_61e4a';
25
26
 
26
27
  public static string $cookieName = '';
27
28
 
@@ -32,7 +33,10 @@ class Auth
32
33
 
33
34
  private function __construct()
34
35
  {
35
- $this->secretKey = Env::string('AUTH_SECRET', 'CD24eEv4qbsC5LOzqeaWbcr58mBMSvA4Mkii8GjRiHkt');
36
+ $this->secretKey = Env::string('AUTH_SECRET', '');
37
+ if ($this->secretKey === '') {
38
+ throw new InvalidArgumentException('AUTH_SECRET is required for authentication.');
39
+ }
36
40
  self::$cookieName = self::getCookieName();
37
41
  }
38
42
 
@@ -117,7 +121,7 @@ class Auth
117
121
 
118
122
  $jwt = $_COOKIE[self::$cookieName];
119
123
  $verifyToken = $this->verifyToken($jwt);
120
- if ($verifyToken === false) {
124
+ if ($verifyToken === null) {
121
125
  return false;
122
126
  }
123
127
 
@@ -258,8 +262,8 @@ class Auth
258
262
  {
259
263
  $secret = Env::string('FUNCTION_CALL_SECRET', '');
260
264
 
261
- if (empty($secret)) {
262
- return;
265
+ if ($secret === '') {
266
+ throw new InvalidArgumentException('FUNCTION_CALL_SECRET is required for CSRF protection.');
263
267
  }
264
268
 
265
269
  $nonce = bin2hex(random_bytes(16));
@@ -386,26 +390,34 @@ class Auth
386
390
  {
387
391
  $dynamicRouteParams = Request::$dynamicParams[self::PPAUTH] ?? [];
388
392
 
389
- if (Request::$isGet && in_array('signin', $dynamicRouteParams)) {
393
+ if (Request::$isGet && in_array('signin', $dynamicRouteParams, true)) {
390
394
  foreach ($providers as $provider) {
391
- if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams)) {
392
- $githubAuthUrl = "https://github.com/login/oauth/authorize?scope=user:email%20read:user&client_id={$provider->clientId}";
395
+ if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams, true)) {
396
+ $state = $this->createOAuthState('github');
397
+ $githubAuthUrl = "https://github.com/login/oauth/authorize?scope=user:email%20read:user&client_id={$provider->clientId}&state=" . urlencode($state);
393
398
  Request::redirect($githubAuthUrl);
394
- } elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams)) {
399
+ } elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams, true)) {
400
+ $state = $this->createOAuthState('google');
395
401
  $googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth?"
396
402
  . "scope=" . urlencode('email profile') . "&"
397
403
  . "response_type=code&"
398
404
  . "client_id=" . urlencode($provider->clientId) . "&"
399
- . "redirect_uri=" . urlencode($provider->redirectUri);
405
+ . "redirect_uri=" . urlencode($provider->redirectUri) . "&"
406
+ . "state=" . urlencode($state);
400
407
  Request::redirect($googleAuthUrl);
401
408
  }
402
409
  }
403
410
  }
404
411
 
405
412
  $authCode = Validator::string($_GET['code'] ?? '');
413
+ $authState = Validator::string($_GET['state'] ?? '', false);
414
+
415
+ if (Request::$isGet && in_array('callback', $dynamicRouteParams, true) && $authCode !== '') {
416
+ if (in_array('github', $dynamicRouteParams, true)) {
417
+ if (!$this->consumeOAuthState('github', $authState)) {
418
+ exit("Error occurred. Please try again.");
419
+ }
406
420
 
407
- if (Request::$isGet && in_array('callback', $dynamicRouteParams) && isset($authCode)) {
408
- if (in_array('github', $dynamicRouteParams)) {
409
421
  $provider = $this->findProvider($providers, GithubProvider::class);
410
422
 
411
423
  if (!$provider) {
@@ -413,7 +425,11 @@ class Auth
413
425
  }
414
426
 
415
427
  return $this->githubProvider($provider, $authCode);
416
- } elseif (in_array('google', $dynamicRouteParams)) {
428
+ } elseif (in_array('google', $dynamicRouteParams, true)) {
429
+ if (!$this->consumeOAuthState('google', $authState)) {
430
+ exit("Error occurred. Please try again.");
431
+ }
432
+
417
433
  $provider = $this->findProvider($providers, GoogleProvider::class);
418
434
 
419
435
  if (!$provider) {
@@ -554,6 +570,31 @@ class Auth
554
570
  }
555
571
  }
556
572
 
573
+ private function createOAuthState(string $provider): string
574
+ {
575
+ $state = bin2hex(random_bytes(16));
576
+ $_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider] = $state;
577
+
578
+ return $state;
579
+ }
580
+
581
+ private function consumeOAuthState(string $provider, string $state): bool
582
+ {
583
+ $expectedState = $_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider] ?? '';
584
+
585
+ if (!is_string($expectedState) || $expectedState === '' || $state === '' || !hash_equals($expectedState, $state)) {
586
+ return false;
587
+ }
588
+
589
+ unset($_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider]);
590
+
591
+ if (empty($_SESSION[self::OAUTH_STATE_SESSION_KEY])) {
592
+ unset($_SESSION[self::OAUTH_STATE_SESSION_KEY]);
593
+ }
594
+
595
+ return true;
596
+ }
597
+
557
598
  private static function getCookieName(): string
558
599
  {
559
600
  $authCookieName = Env::string('AUTH_COOKIE_NAME', 'auth_cookie_name_d36e5');
@@ -123,11 +123,11 @@ final class AuthMiddleware
123
123
 
124
124
  $verifyToken = $auth->verifyToken($jwt);
125
125
 
126
- if ($verifyToken === false) {
126
+ if ($verifyToken === null) {
127
127
  return false;
128
128
  }
129
129
 
130
- return isset($verifyToken->{Auth::PAYLOAD_NAME});
130
+ return true;
131
131
  }
132
132
 
133
133
  protected static function hasRequiredRole(string $requestPathname): string
@@ -17,6 +17,10 @@ final class CorsMiddleware
17
17
 
18
18
  $cfg = self::buildConfig($overrides);
19
19
 
20
+ if ($cfg['allowCredentials'] && self::listHasWildcard($cfg['allowedOrigins'])) {
21
+ return;
22
+ }
23
+
20
24
  if (!self::isAllowedOrigin($origin, $cfg['allowedOrigins'])) {
21
25
  return;
22
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "5.0.0-alpha.2",
3
+ "version": "5.0.0-alpha.21",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/dist/README.md DELETED
@@ -1,196 +0,0 @@
1
- # Prisma PHP
2
-
3
- Prisma PHP is a modern full-stack PHP framework that combines native PHP, PulsePoint reactivity, PHPX components, and a Prisma-inspired ORM into one cohesive developer experience.
4
-
5
- Build modern, reactive interfaces with a server-first mental model, type-safe data access, and a project structure designed for real applications.
6
-
7
- ## Getting Started
8
-
9
- First, create a new Prisma PHP project:
10
-
11
- ```bash
12
- npx create-prisma-php-app@latest my-app
13
- ```
14
-
15
- Start the development server:
16
-
17
- ```bash
18
- npm run dev
19
- ```
20
-
21
- Open your local app in the browser after the dev server starts.
22
-
23
- ## Prerequisites
24
-
25
- Before creating a Prisma PHP project, make sure you have:
26
-
27
- - Node.js 22.x or higher
28
- - PHP 8.2 or higher
29
- - Composer 2.x or higher
30
- - XAMPP or another local PHP environment
31
-
32
- If you are using XAMPP on Windows, enabling `extension=zip` in `php.ini` is recommended so Composer dependencies install correctly.
33
-
34
- ## What Prisma PHP Includes
35
-
36
- Prisma PHP brings together the core pieces needed to build full-stack PHP apps:
37
-
38
- - **Native PHP + modern reactivity** with PulsePoint
39
- - **PHPX component system** inspired by JSX and React
40
- - **Prisma PHP ORM** for schema-first, type-safe database access
41
- - **CLI scaffolding** for new apps, starter kits, and optional features
42
- - **Flexible deployment options** for traditional hosting, CI/CD, and containerized setups
43
-
44
- ## Common Create Commands
45
-
46
- Create a default full-stack app:
47
-
48
- ```bash
49
- npx create-prisma-php-app@latest my-app
50
- ```
51
-
52
- Create a project with common options:
53
-
54
- ```bash
55
- npx create-prisma-php-app@latest my-app --tailwindcss --typescript
56
- ```
57
-
58
- Use a starter kit:
59
-
60
- ```bash
61
- npx create-prisma-php-app my-app --starter-kit=fullstack
62
- ```
63
-
64
- Other documented flags include:
65
-
66
- - `--websocket`
67
- - `--mcp`
68
- - `--backend-only`
69
- - `--swagger-docs`
70
- - `--docker`
71
-
72
- ## Project Structure
73
-
74
- A generated Prisma PHP project typically includes folders like these:
75
-
76
- ```text
77
- prisma-php-project/
78
- ├── prisma/ # schema, migrations, seed files
79
- ├── public/ # public entry point and assets
80
- ├── settings/ # project configuration
81
- ├── src/ # application source code
82
- ├── package.json # frontend/dev scripts
83
- ├── composer.json # PHP dependencies
84
- └── prisma-php.json # Prisma PHP project capability manifest
85
- ```
86
-
87
- ## Project Capability Manifest
88
-
89
- Prisma PHP uses `prisma-php.json` at the repository root as the source of truth for enabled framework features and local environment configuration.
90
-
91
- Tools, scripts, and AI agents should inspect this file before making decisions about:
92
-
93
- - frontend tooling
94
- - backend-only mode
95
- - Prisma ORM usage
96
- - Tailwind CSS availability
97
- - Swagger docs
98
- - Websocket support
99
- - MCP support
100
- - TypeScript support
101
- - local development paths and BrowserSync config
102
-
103
- A typical file looks like this:
104
-
105
- ```json
106
- {
107
- "projectName": "test-latest",
108
- "projectRootPath": "C:\\xampp\\htdocs\\projects\\prisma-php\\test\\test-latest",
109
- "phpEnvironment": "XAMPP",
110
- "phpRootPathExe": "C:\\xampp\\php\\php.exe",
111
- "bsTarget": "http://localhost/projects/prisma-php/test/test-latest/",
112
- "bsPathRewrite": {
113
- "^/": "/projects/prisma-php/test/test-latest/"
114
- },
115
- "backendOnly": false,
116
- "swaggerDocs": false,
117
- "tailwindcss": true,
118
- "websocket": false,
119
- "mcp": false,
120
- "prisma": false,
121
- "typescript": false,
122
- "version": "4.4.9",
123
- "componentScanDirs": ["src", "vendor/tsnc/prisma-php/src"],
124
- "excludeFiles": []
125
- }
126
- ```
127
-
128
- ## AI Agent Guidance
129
-
130
- If you are using AI-assisted development in this project:
131
-
132
- - treat `prisma-php.json` as the capability manifest for the current app
133
- - do not assume Tailwind CSS, Prisma ORM, MCP, Swagger, TypeScript, or websocket support is enabled unless the file says so
134
- - read the installed Prisma PHP docs for the current version before changing routing, layouts, middleware, or framework-specific behavior
135
- - use `prisma-php.json` together with the installed docs to make safer decisions
136
-
137
- ## PHPX and Reactivity
138
-
139
- Prisma PHP uses **PHPX**, a template system inspired by JSX and React, but designed for `.php` files. Combined with **PulsePoint**, it gives you interactive UI behavior without adopting a full JavaScript framework mental model.
140
-
141
- You can keep your application logic in PHP while using browser-resident state for fast UI updates.
142
-
143
- ## ORM
144
-
145
- Prisma PHP ORM is a schema-first database toolkit for PHP. You define your models in `schema.prisma`, generate a typed PHP client, and use expressive query methods such as:
146
-
147
- - `findMany`
148
- - `findFirst`
149
- - `findUnique`
150
- - `create`
151
- - `update`
152
- - `delete`
153
- - `upsert`
154
-
155
- It also includes CLI support for workflows like:
156
-
157
- - `migrate`
158
- - `generate`
159
- - `push`
160
- - `reset`
161
-
162
- ## Deployment
163
-
164
- Prisma PHP supports multiple deployment styles:
165
-
166
- - **Traditional deployment** using ZIP/FTP to shared hosting environments such as cPanel
167
- - **CI/CD deployment** with GitHub Actions
168
- - **Docker deployment** for containerized environments
169
-
170
- A common production flow is:
171
-
172
- ```bash
173
- npm run build
174
- ```
175
-
176
- Then upload or deploy the built project using your preferred hosting workflow.
177
-
178
- ## Learn More
179
-
180
- To learn more about Prisma PHP, explore the official resources:
181
-
182
- - Website: [https://prismaphp.tsnc.tech](https://prismaphp.tsnc.tech)
183
- - Documentation: [https://prismaphp.tsnc.tech/docs](https://prismaphp.tsnc.tech/docs)
184
-
185
- ## Ecosystem
186
-
187
- Prisma PHP is part of a broader ecosystem that includes:
188
-
189
- - PulsePoint
190
- - PHPX UI
191
- - PP Icons
192
-
193
- ## Notes
194
-
195
- - For the best PHPX development experience in VS Code, install the **PHPX Tag Support** extension.
196
- - If PowerShell blocks local scripts during `npm run dev`, check your Windows execution policy.