@chidchanun/bcp 0.2.4 → 0.2.6

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.
@@ -0,0 +1,152 @@
1
+ # BCP Framework 0.2.5
2
+
3
+ ## Authentication Platform v2
4
+
5
+ `0.2.5` expands `bcp/auth` from signed JWT-cookie authentication into an optional revocable session platform while preserving the stateless mode used by existing applications.
6
+
7
+ ## Highlights
8
+
9
+ ### Server-side auth session store contract
10
+
11
+ New public types:
12
+
13
+ ```ts
14
+ import type {
15
+ AuthSessionStore,
16
+ AuthSessionStoreRecord,
17
+ } from "bcp/auth";
18
+ ```
19
+
20
+ The contract provides:
21
+
22
+ ```text
23
+ set
24
+ get
25
+ touch
26
+ revoke
27
+ revokeUser
28
+ ```
29
+
30
+ Applications can back this contract with Redis, SQL, or another shared server-side store.
31
+
32
+ ### Built-in memory store
33
+
34
+ Development and tests can use:
35
+
36
+ ```ts
37
+ import {
38
+ createMemoryAuthSessionStore,
39
+ } from "bcp/auth";
40
+ ```
41
+
42
+ The memory adapter is process-local and intentionally not presented as a distributed production session database.
43
+
44
+ ### Revocable sessions
45
+
46
+ When an auth store is configured, a valid signed JWT must also have an active server-side `sid` record.
47
+
48
+ BCP can therefore invalidate a session before the cookie's JWT expiry.
49
+
50
+ New APIs include:
51
+
52
+ ```text
53
+ logoutAll()
54
+ revokeSession()
55
+ revokeUserSessions()
56
+ ```
57
+
58
+ Normal `logout()` also revokes the current `sid` when a store is enabled.
59
+
60
+ ### Idle timeout
61
+
62
+ `AuthOptions` now accepts:
63
+
64
+ ```ts
65
+ idleTimeout: number
66
+ ```
67
+
68
+ The value is measured in seconds and requires a session store.
69
+
70
+ Successful authentication updates the store's `lastSeenAt`. Sessions that exceed the inactivity window are revoked and rejected.
71
+
72
+ BCP intentionally rejects `idleTimeout` without a store instead of silently providing a false sense of server-side inactivity enforcement.
73
+
74
+ ### Session rotation
75
+
76
+ `rotateSession()` continues issuing a fresh `sid`, JWT, expiry, and cookie.
77
+
78
+ With a session store enabled, the previous `sid` is revoked so old cookies no longer pass the server-side session check.
79
+
80
+ ### Guest route guards
81
+
82
+ New APIs:
83
+
84
+ ```ts
85
+ requireGuest()
86
+ createGuestGuard()
87
+ ```
88
+
89
+ These support login, registration, and similar pages that should continue for anonymous users but redirect users who are already authenticated.
90
+
91
+ Default authenticated-user behavior is a `303` redirect. `redirectTo: null` returns `409 Already authenticated`.
92
+
93
+ ## Backward compatibility
94
+
95
+ The default remains stateless signed JWT-cookie authentication:
96
+
97
+ ```ts
98
+ import {
99
+ auth,
100
+ login,
101
+ logout,
102
+ } from "bcp/auth";
103
+ ```
104
+
105
+ Applications do not need to configure a session store unless they need centralized revocation or idle-timeout behavior.
106
+
107
+ No existing public application entrypoint is intentionally removed in this release.
108
+
109
+ ## Security guidance
110
+
111
+ - JWT payloads remain signed rather than encrypted.
112
+ - Do not place passwords, password hashes, tokens, API keys, or credentials in auth payload data.
113
+ - Keep `BCP_SESSION_SECRET` outside source control and at least 32 bytes long.
114
+ - Use a shared durable session-store implementation for production applications that run multiple Node.js processes or containers.
115
+ - The built-in memory store is suitable for local development/tests and process-local prototypes.
116
+
117
+ ## Documentation
118
+
119
+ Updated guides:
120
+
121
+ ```text
122
+ docs/authentication.md
123
+ docs/auth-session-store.md
124
+ docs/auth-route-guards.md
125
+ docs/api-reference.md
126
+ ```
127
+
128
+ ## Validation
129
+
130
+ `0.2.5` adds unit and package smoke coverage for:
131
+
132
+ ```text
133
+ session-store registration and lookup
134
+ session revocation
135
+ session rotation with revocation
136
+ logout-all
137
+ idle timeout
138
+ guest route guards
139
+ bcp/auth publish surface
140
+ Authentication Platform v2 package contents
141
+ ```
142
+
143
+ Before tagging or publishing, run the complete RC sequence:
144
+
145
+ ```bash
146
+ npm run typecheck
147
+ npm run test:unit
148
+ npm run test:integration
149
+ npm run test:e2e
150
+ npm run test:package
151
+ npm run rc:check
152
+ ```
@@ -0,0 +1,194 @@
1
+ # BCP Framework 0.2.6 — Authorization & Security v2
2
+
3
+ `0.2.6` expands the server authorization model and adds request-origin/CSRF protection primitives while preserving the existing Authentication Platform v2 and route model.
4
+
5
+ > Release state: unreleased development target until RC validation, tagging and npm publication complete.
6
+
7
+ ## Authorization primitives
8
+
9
+ `bcp/auth` now provides flat permission checks:
10
+
11
+ ```ts
12
+ hasPermission()
13
+ assertPermission()
14
+ getUserPermissions()
15
+ ```
16
+
17
+ Permission requirements accept one permission or multiple permissions with `any` / `all` matching.
18
+
19
+ The default user field is:
20
+
21
+ ```text
22
+ permissions
23
+ ```
24
+
25
+ Applications can select another field, such as `scopes`.
26
+
27
+ ## Permission route guards
28
+
29
+ Route guards can require permissions directly:
30
+
31
+ ```ts
32
+ requirePermission()
33
+ createPermissionGuard()
34
+ ```
35
+
36
+ Unauthenticated behavior remains consistent with `requireAuth()`. Authenticated users that fail the permission check receive `403 Forbidden` by default or can be redirected explicitly.
37
+
38
+ ## Policy authorization
39
+
40
+ Resource-aware application policies are now supported through:
41
+
42
+ ```ts
43
+ defineAuthorizationPolicy()
44
+ can()
45
+ cannot()
46
+ authorize()
47
+ AuthorizationError
48
+ ```
49
+
50
+ Policies receive a typed context containing:
51
+
52
+ ```ts
53
+ {
54
+ user,
55
+ resource,
56
+ data,
57
+ }
58
+ ```
59
+
60
+ Policies may be synchronous or asynchronous. `authorize()` throws a `403`-classified `AuthorizationError` when access is denied.
61
+
62
+ ## Same-origin protection
63
+
64
+ `bcp/server` adds request-origin validation for unsafe methods:
65
+
66
+ ```ts
67
+ isSafeHttpMethod()
68
+ isSameOriginRequest()
69
+ requireSameOriginRequest()
70
+ ```
71
+
72
+ The current request origin is always allowed. Additional origins may be listed explicitly.
73
+
74
+ For unsafe methods, BCP checks `Origin` first and then `Referer`. Missing origin information is rejected by default unless the application explicitly enables `allowMissingOrigin`.
75
+
76
+ Malformed, opaque, or unsupported origins are treated as denied instead of surfacing as server errors.
77
+
78
+ ## CSRF protection
79
+
80
+ Signed CSRF tokens are available through:
81
+
82
+ ```ts
83
+ createCsrfToken()
84
+ destroyCsrfToken()
85
+ verifyCsrfToken()
86
+ verifyCsrfRequest()
87
+ requireCsrfRequest()
88
+ ```
89
+
90
+ The default request header is:
91
+
92
+ ```text
93
+ X-BCP-CSRF
94
+ ```
95
+
96
+ The default cookie is:
97
+
98
+ ```text
99
+ bcp_csrf
100
+ ```
101
+
102
+ CSRF cookies are HttpOnly, SameSite=Lax by default, Secure in production, and use a two-hour default lifetime.
103
+
104
+ Token signing resolves secrets in this order:
105
+
106
+ ```text
107
+ explicit secret
108
+ BCP_CSRF_SECRET
109
+ BCP_SESSION_SECRET
110
+ ```
111
+
112
+ Secrets must contain at least 32 UTF-8 bytes.
113
+
114
+ `requireCsrfRequest()` combines same-origin validation and token validation for unsafe methods. GET, HEAD and OPTIONS do not require CSRF tokens.
115
+
116
+ ## Security error classification
117
+
118
+ `RequestSecurityError` reports status `403` and distinguishes:
119
+
120
+ ```text
121
+ INVALID_ORIGIN
122
+ INVALID_CSRF_TOKEN
123
+ ```
124
+
125
+ This allows API routes/actions to map request-security failures to application-specific error responses while retaining a stable security classification.
126
+
127
+ ## Compatibility
128
+
129
+ `0.2.6` does not intentionally remove existing public entrypoints or authentication behavior.
130
+
131
+ Existing APIs remain available:
132
+
133
+ ```text
134
+ auth()
135
+ login()
136
+ logout()
137
+ logoutAll()
138
+ rotateSession()
139
+ requireAuth()
140
+ requireGuest()
141
+ requireRole()
142
+ createAuthGuard()
143
+ createGuestGuard()
144
+ createRoleGuard()
145
+ ```
146
+
147
+ Stateless JWT-cookie authentication remains supported. Revocable stores introduced in `0.2.5` are unchanged.
148
+
149
+ ## Testing and packaging
150
+
151
+ The release adds unit coverage for:
152
+
153
+ - permission `any` / `all` matching,
154
+ - resource authorization policies,
155
+ - permission route guards,
156
+ - same-origin mutation validation,
157
+ - signed CSRF tokens,
158
+ - invalid CSRF token rejection,
159
+ - cross-origin request rejection.
160
+
161
+ Prepared npm package smoke coverage verifies that the `bcp/auth` and `bcp/server` public entrypoints include the Authorization & Security v2 APIs and implementation files.
162
+
163
+ ## Documentation
164
+
165
+ New guide:
166
+
167
+ ```text
168
+ docs/authorization-security.md
169
+ ```
170
+
171
+ Updated documentation contracts include:
172
+
173
+ ```text
174
+ docs/platform-manifest.json
175
+ docs/docs-web-manifest.json
176
+ docs/api-manifest.json
177
+ docs/api-reference.md
178
+ README.md
179
+ ```
180
+
181
+ ## Release validation
182
+
183
+ Before publishing:
184
+
185
+ ```bash
186
+ npm run typecheck
187
+ npm run test:unit
188
+ npm run test:integration
189
+ npm run test:e2e
190
+ npm run test:package
191
+ npm run rc:check
192
+ ```
193
+
194
+ The final `v0.2.6` Git tag must point to the exact commit that passed the complete RC sequence.
package/docs/security.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Security
2
2
 
3
- BCP Framework applies a security gateway in both development and standalone production modes.
3
+ BCP Framework applies a security gateway in both development and standalone production modes and exposes server-side mutation protection primitives through `bcp/server`.
4
4
 
5
5
  ## Default response headers
6
6
 
@@ -40,6 +40,58 @@ server: {
40
40
 
41
41
  Requests exceeding the limit are rejected by the outer gateway with HTTP 413 before reaching the API handler.
42
42
 
43
+ ## Same-origin mutation protection — 0.2.6+
44
+
45
+ Cookie-authenticated unsafe requests can validate their browser origin:
46
+
47
+ ```ts
48
+ import {
49
+ requireSameOriginRequest,
50
+ } from "bcp/server";
51
+
52
+ await requireSameOriginRequest();
53
+ ```
54
+
55
+ BCP validates `Origin` first and falls back to `Referer`. GET, HEAD and OPTIONS are treated as safe methods. Unsafe methods without origin information are rejected by default.
56
+
57
+ Additional trusted origins can be configured per check with `allowedOrigins`.
58
+
59
+ Malformed, opaque or unsupported origins are denied instead of surfacing as server errors.
60
+
61
+ ## CSRF protection — 0.2.6+
62
+
63
+ BCP also exposes signed CSRF tokens:
64
+
65
+ ```ts
66
+ import {
67
+ createCsrfToken,
68
+ requireCsrfRequest,
69
+ } from "bcp/server";
70
+
71
+ const token =
72
+ await createCsrfToken();
73
+
74
+ // Render the token through a trusted application response,
75
+ // then submit it with the mutation.
76
+
77
+ await requireCsrfRequest({
78
+ token: submittedToken,
79
+ });
80
+ ```
81
+
82
+ Default header/cookie:
83
+
84
+ ```text
85
+ X-BCP-CSRF
86
+ bcp_csrf
87
+ ```
88
+
89
+ Token signing uses `BCP_CSRF_SECRET` when configured and otherwise falls back to `BCP_SESSION_SECRET`. Secrets must contain at least 32 UTF-8 bytes.
90
+
91
+ `requireCsrfRequest()` combines same-origin validation and CSRF token verification for unsafe methods.
92
+
93
+ Read more: [Authorization & Security v2](authorization-security.md).
94
+
43
95
  ## Static assets
44
96
 
45
97
  Public asset resolution decodes the URL, rejects null bytes, resolves the candidate beneath `public/`, resolves filesystem symlinks and verifies the final real path remains inside the public directory. This prevents path and symlink traversal from escaping the public root.
@@ -55,3 +107,5 @@ Security config values reject carriage return, line feed and null bytes to preve
55
107
  ## Deployment note
56
108
 
57
109
  The security gateway is the outer production layer, so security headers and body limits also apply to response-cache hits, middleware responses, static assets and error responses.
110
+
111
+ Authorization, same-origin checks and CSRF validation remain request-handler/guard responsibilities and should be enforced wherever a mutation or protected operation occurs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4,6 +4,9 @@ export {
4
4
  getSession,
5
5
  login,
6
6
  logout,
7
+ logoutAll,
8
+ revokeSession,
9
+ revokeUserSessions,
7
10
  rotateSession,
8
11
 
9
12
  type AuthApi,
@@ -13,17 +16,50 @@ export {
13
16
  type AuthUser,
14
17
  } from "../../server/src/auth.js";
15
18
 
19
+ export {
20
+ createMemoryAuthSessionStore,
21
+
22
+ type AuthSessionStore,
23
+ type AuthSessionStoreRecord,
24
+ type MemoryAuthSessionStore,
25
+ } from "../../server/src/auth-session-store.js";
26
+
27
+ export {
28
+ assertPermission,
29
+ authorize,
30
+ can,
31
+ cannot,
32
+ defineAuthorizationPolicy,
33
+ getUserPermissions,
34
+ hasPermission,
35
+ AuthorizationError,
36
+
37
+ type AuthorizationContext,
38
+ type AuthorizationMatch,
39
+ type AuthorizationPolicy,
40
+ type PermissionCheckOptions,
41
+ type PermissionRequirement,
42
+ } from "../../server/src/authorization.js";
43
+
16
44
  export {
17
45
  createAuthGuard,
46
+ createGuestGuard,
47
+ createPermissionGuard,
18
48
  createRoleGuard,
19
49
  getGuardAuth,
20
50
  requireAuth,
51
+ requireGuest,
52
+ requirePermission,
21
53
  requireRole,
22
54
 
23
55
  type AuthGuardData,
24
56
  type AuthGuardFunction,
25
57
  type AuthGuardResult,
58
+ type GuestGuardFunction,
59
+ type GuestGuardResult,
26
60
  type RequireAuthOptions,
61
+ type RequireGuestOptions,
62
+ type RequirePermissionOptions,
27
63
  type RequireRoleOptions,
28
64
  type RequiredRole,
29
65
  } from "../../server/src/auth-guard.js";
@@ -15,6 +15,22 @@ export {
15
15
  type ResponseCookieOptions,
16
16
  } from "../../server/src/request-context.js";
17
17
 
18
+ export {
19
+ createCsrfToken,
20
+ destroyCsrfToken,
21
+ isSafeHttpMethod,
22
+ isSameOriginRequest,
23
+ requireCsrfRequest,
24
+ requireSameOriginRequest,
25
+ verifyCsrfRequest,
26
+ verifyCsrfToken,
27
+ RequestSecurityError,
28
+
29
+ type CsrfTokenOptions,
30
+ type SameOriginOptions,
31
+ type VerifyCsrfRequestOptions,
32
+ } from "../../server/src/request-security.js";
33
+
18
34
  export {
19
35
  attachRequestId,
20
36
  createLogger,