@chidchanun/bcp 0.2.5 → 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.
- package/README.md +140 -66
- package/docs/README.md +54 -48
- package/docs/api-manifest.json +5 -3
- package/docs/api-reference.md +42 -6
- package/docs/auth-route-guards.md +58 -50
- package/docs/authorization-security.md +298 -0
- package/docs/docs-web-manifest.json +6 -4
- package/docs/platform-manifest.json +11 -4
- package/docs/releases/0.2.6.md +194 -0
- package/docs/security.md +55 -1
- package/package.json +1 -1
- package/packages/client/src/auth.ts +20 -0
- package/packages/client/src/server.ts +16 -0
- package/packages/server/src/auth-guard.ts +111 -15
- package/packages/server/src/authorization.ts +368 -0
- package/packages/server/src/request-security.ts +596 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Auth Route Guards
|
|
2
2
|
|
|
3
|
-
BCP Framework integrates Authentication Platform v2 with route guards through the server-only `bcp/auth` entrypoint.
|
|
3
|
+
BCP Framework integrates Authentication Platform v2 and Authorization & Security v2 with route guards through the server-only `bcp/auth` entrypoint.
|
|
4
4
|
|
|
5
5
|
## Protect a route tree
|
|
6
6
|
|
|
@@ -30,7 +30,7 @@ export const guard =
|
|
|
30
30
|
});
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
## Guest-only routes — 0.2.5
|
|
33
|
+
## Guest-only routes — 0.2.5+
|
|
34
34
|
|
|
35
35
|
Login, registration, and password-recovery entry pages often should not remain accessible after the user has already authenticated.
|
|
36
36
|
|
|
@@ -60,30 +60,9 @@ signed-in user
|
|
|
60
60
|
303 /dashboard
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Use `redirectTo: null` to return `409 Already authenticated` instead of redirecting
|
|
63
|
+
Use `redirectTo: null` to return `409 Already authenticated` instead of redirecting.
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
export const guard =
|
|
67
|
-
createGuestGuard({
|
|
68
|
-
redirectTo:
|
|
69
|
-
null,
|
|
70
|
-
});
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
The low-level form is `requireGuest()`:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
import {
|
|
77
|
-
requireGuest,
|
|
78
|
-
} from "bcp/auth";
|
|
79
|
-
|
|
80
|
-
export function guard() {
|
|
81
|
-
return requireGuest({
|
|
82
|
-
redirectTo:
|
|
83
|
-
"/dashboard",
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
```
|
|
65
|
+
The low-level form is `requireGuest()`.
|
|
87
66
|
|
|
88
67
|
## Require a role
|
|
89
68
|
|
|
@@ -110,58 +89,87 @@ export const guard =
|
|
|
110
89
|
|
|
111
90
|
An authenticated user without the required role receives `403 Forbidden` by default.
|
|
112
91
|
|
|
113
|
-
|
|
92
|
+
Require one of several roles:
|
|
114
93
|
|
|
115
94
|
```ts
|
|
116
95
|
export const guard =
|
|
117
|
-
createRoleGuard<User>(
|
|
96
|
+
createRoleGuard<User>([
|
|
118
97
|
"admin",
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"/forbidden",
|
|
122
|
-
}
|
|
123
|
-
);
|
|
98
|
+
"manager",
|
|
99
|
+
]);
|
|
124
100
|
```
|
|
125
101
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
The default role matching mode is `any`:
|
|
102
|
+
Require all values from a role field:
|
|
129
103
|
|
|
130
104
|
```ts
|
|
131
105
|
export const guard =
|
|
132
|
-
createRoleGuard<User>(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
106
|
+
createRoleGuard<User>(
|
|
107
|
+
[
|
|
108
|
+
"admin",
|
|
109
|
+
"billing",
|
|
110
|
+
],
|
|
111
|
+
{
|
|
112
|
+
match: "all",
|
|
113
|
+
}
|
|
114
|
+
);
|
|
136
115
|
```
|
|
137
116
|
|
|
138
|
-
|
|
117
|
+
## Require permissions — 0.2.6+
|
|
139
118
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`requireRole()` and `createRoleGuard()` can read another user field and require every value:
|
|
119
|
+
Authorization & Security v2 adds a dedicated permission guard instead of requiring applications to overload role configuration.
|
|
143
120
|
|
|
144
121
|
```ts
|
|
122
|
+
import {
|
|
123
|
+
createPermissionGuard,
|
|
124
|
+
} from "bcp/auth";
|
|
125
|
+
|
|
145
126
|
interface User {
|
|
146
127
|
id: number;
|
|
147
128
|
permissions: string[];
|
|
148
129
|
}
|
|
149
130
|
|
|
150
131
|
export const guard =
|
|
151
|
-
|
|
132
|
+
createPermissionGuard<User>(
|
|
133
|
+
"users.read"
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The default permission field is `permissions`.
|
|
138
|
+
|
|
139
|
+
Require every permission:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
export const guard =
|
|
143
|
+
createPermissionGuard<User>(
|
|
152
144
|
[
|
|
153
145
|
"users.read",
|
|
154
146
|
"users.write",
|
|
155
147
|
],
|
|
156
148
|
{
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
149
|
+
match: "all",
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Use another user field such as `scopes`:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
export const guard =
|
|
158
|
+
createPermissionGuard<User>(
|
|
159
|
+
"billing.read",
|
|
160
|
+
{
|
|
161
|
+
permissionField:
|
|
162
|
+
"scopes",
|
|
161
163
|
}
|
|
162
164
|
);
|
|
163
165
|
```
|
|
164
166
|
|
|
167
|
+
The low-level form is `requirePermission()`.
|
|
168
|
+
|
|
169
|
+
Authenticated users without the required permission receive `403 Forbidden` by default. Use `forbiddenRedirectTo` when the application prefers a redirect.
|
|
170
|
+
|
|
171
|
+
For authorization decisions that depend on a resource or ownership rather than a flat permission, use the policy APIs documented in [Authorization & Security v2](authorization-security.md).
|
|
172
|
+
|
|
165
173
|
## Inline guard logic
|
|
166
174
|
|
|
167
175
|
Use `requireAuth()` when a guard needs additional application-specific checks:
|
|
@@ -198,7 +206,7 @@ export async function guard() {
|
|
|
198
206
|
|
|
199
207
|
## Session-store policies apply inside guards
|
|
200
208
|
|
|
201
|
-
`requireAuth()`, `requireRole()`, and `requireGuest()` all use the same `auth()` runtime.
|
|
209
|
+
`requireAuth()`, `requireRole()`, `requirePermission()`, and `requireGuest()` all use the same `auth()` runtime.
|
|
202
210
|
|
|
203
211
|
When auth options include a server-side store and idle timeout, guard evaluation observes revocation and inactivity before allowing the route.
|
|
204
212
|
|
|
@@ -254,7 +262,7 @@ export async function loader({
|
|
|
254
262
|
|
|
255
263
|
## Standalone production behavior
|
|
256
264
|
|
|
257
|
-
`bcp/auth` stays inside the production guard/runtime request-context graph. `requireAuth()`, `requireGuest()`, `requireRole()`, `auth()`, `getSession()`, and `cookies()` therefore observe the active request after `bcp build`.
|
|
265
|
+
`bcp/auth` stays inside the production guard/runtime request-context graph. `requireAuth()`, `requireGuest()`, `requireRole()`, `requirePermission()`, `auth()`, `getSession()`, and `cookies()` therefore observe the active request after `bcp build`.
|
|
258
266
|
|
|
259
267
|
The production form-action bundle uses the same runtime boundary because guards may execute before an action.
|
|
260
268
|
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Authorization & Security v2
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.2.6` adds permission- and policy-based authorization plus request-origin and CSRF protection primitives while keeping the existing authentication APIs backward compatible.
|
|
4
|
+
|
|
5
|
+
## Permission checks
|
|
6
|
+
|
|
7
|
+
Permission helpers are exported from the server-only `bcp/auth` entrypoint.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
hasPermission,
|
|
12
|
+
assertPermission,
|
|
13
|
+
} from "bcp/auth";
|
|
14
|
+
|
|
15
|
+
const user = {
|
|
16
|
+
id: 42,
|
|
17
|
+
permissions: [
|
|
18
|
+
"users.read",
|
|
19
|
+
"users.write",
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
hasPermission(
|
|
24
|
+
user,
|
|
25
|
+
"users.read"
|
|
26
|
+
); // true
|
|
27
|
+
|
|
28
|
+
hasPermission(
|
|
29
|
+
user,
|
|
30
|
+
[
|
|
31
|
+
"users.read",
|
|
32
|
+
"users.delete",
|
|
33
|
+
],
|
|
34
|
+
{
|
|
35
|
+
match: "all",
|
|
36
|
+
}
|
|
37
|
+
); // false
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The default permission field is `permissions`. Override it when an application uses another user shape:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
hasPermission(
|
|
44
|
+
user,
|
|
45
|
+
"billing.read",
|
|
46
|
+
{
|
|
47
|
+
field: "scopes",
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`assertPermission()` throws `AuthorizationError` with status `403` when the requirement is not satisfied.
|
|
53
|
+
|
|
54
|
+
## Permission route guards
|
|
55
|
+
|
|
56
|
+
Protect a route tree with a permission requirement:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import {
|
|
60
|
+
createPermissionGuard,
|
|
61
|
+
} from "bcp/auth";
|
|
62
|
+
|
|
63
|
+
export const guard =
|
|
64
|
+
createPermissionGuard(
|
|
65
|
+
"users.read"
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Require every permission:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
export const guard =
|
|
73
|
+
createPermissionGuard(
|
|
74
|
+
[
|
|
75
|
+
"users.read",
|
|
76
|
+
"users.write",
|
|
77
|
+
],
|
|
78
|
+
{
|
|
79
|
+
match: "all",
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
An unauthenticated request follows the normal `requireAuth()` behavior. An authenticated user without the required permission receives `403 Forbidden` by default.
|
|
85
|
+
|
|
86
|
+
A custom permission field and forbidden redirect are supported:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
export const guard =
|
|
90
|
+
createPermissionGuard(
|
|
91
|
+
"admin.access",
|
|
92
|
+
{
|
|
93
|
+
permissionField:
|
|
94
|
+
"scopes",
|
|
95
|
+
forbiddenRedirectTo:
|
|
96
|
+
"/forbidden",
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The lower-level `requirePermission()` helper is available for custom guard logic.
|
|
102
|
+
|
|
103
|
+
## Authorization policies
|
|
104
|
+
|
|
105
|
+
Policies are useful when authorization depends on a resource, ownership, tenant, state, or other application-specific data instead of a flat permission string.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import {
|
|
109
|
+
defineAuthorizationPolicy,
|
|
110
|
+
authorize,
|
|
111
|
+
can,
|
|
112
|
+
} from "bcp/auth";
|
|
113
|
+
|
|
114
|
+
interface User {
|
|
115
|
+
id: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface Project {
|
|
119
|
+
ownerId: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const updateProject =
|
|
123
|
+
defineAuthorizationPolicy<
|
|
124
|
+
User,
|
|
125
|
+
Project
|
|
126
|
+
>(
|
|
127
|
+
({
|
|
128
|
+
user,
|
|
129
|
+
resource,
|
|
130
|
+
}) =>
|
|
131
|
+
resource?.ownerId ===
|
|
132
|
+
user.id
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const allowed =
|
|
136
|
+
await can(
|
|
137
|
+
updateProject,
|
|
138
|
+
{
|
|
139
|
+
user,
|
|
140
|
+
resource: project,
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
await authorize(
|
|
145
|
+
updateProject,
|
|
146
|
+
{
|
|
147
|
+
user,
|
|
148
|
+
resource: project,
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`cannot()` is the inverse of `can()`. `authorize()` throws `AuthorizationError` when the policy returns a falsy result.
|
|
154
|
+
|
|
155
|
+
Policies may be synchronous or asynchronous.
|
|
156
|
+
|
|
157
|
+
## Same-origin mutation protection
|
|
158
|
+
|
|
159
|
+
Request-security helpers are exported from `bcp/server`.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import {
|
|
163
|
+
requireSameOriginRequest,
|
|
164
|
+
} from "bcp/server";
|
|
165
|
+
|
|
166
|
+
export async function POST() {
|
|
167
|
+
await requireSameOriginRequest();
|
|
168
|
+
|
|
169
|
+
// mutation
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
For unsafe HTTP methods BCP checks `Origin` first and then `Referer`. The application request origin is allowed automatically.
|
|
174
|
+
|
|
175
|
+
Additional trusted origins can be declared explicitly:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
await requireSameOriginRequest({
|
|
179
|
+
allowedOrigins: [
|
|
180
|
+
"https://admin.example.com",
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Missing origin information is rejected for unsafe methods by default. Set `allowMissingOrigin: true` only when a trusted non-browser client cannot send either header and another protection is in place.
|
|
186
|
+
|
|
187
|
+
## CSRF tokens
|
|
188
|
+
|
|
189
|
+
BCP provides an HttpOnly double-submit style CSRF cookie plus a signed token returned to server code.
|
|
190
|
+
|
|
191
|
+
Create a token during page rendering or another trusted same-origin response:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import {
|
|
195
|
+
createCsrfToken,
|
|
196
|
+
} from "bcp/server";
|
|
197
|
+
|
|
198
|
+
const csrfToken =
|
|
199
|
+
await createCsrfToken();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Pass the returned token to the page/form and submit it back in an application-controlled field or the default header:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
X-BCP-CSRF: <token>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Verify a mutation:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import {
|
|
212
|
+
requireCsrfRequest,
|
|
213
|
+
} from "bcp/server";
|
|
214
|
+
|
|
215
|
+
export async function POST() {
|
|
216
|
+
await requireCsrfRequest();
|
|
217
|
+
|
|
218
|
+
// protected mutation
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
When a form token is parsed from `FormData`, pass it explicitly:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
await requireCsrfRequest({
|
|
226
|
+
token:
|
|
227
|
+
String(
|
|
228
|
+
formData.get("csrf") ??
|
|
229
|
+
""
|
|
230
|
+
),
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`requireCsrfRequest()` combines same-origin validation and CSRF token validation for unsafe methods. Safe read requests do not require a token.
|
|
235
|
+
|
|
236
|
+
## CSRF secret
|
|
237
|
+
|
|
238
|
+
The token signer resolves secrets in this order:
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
explicit options.secret
|
|
242
|
+
BCP_CSRF_SECRET
|
|
243
|
+
BCP_SESSION_SECRET
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Secrets must contain at least 32 UTF-8 bytes.
|
|
247
|
+
|
|
248
|
+
For deployments that want separate authentication and CSRF key rotation, define:
|
|
249
|
+
|
|
250
|
+
```dotenv
|
|
251
|
+
BCP_SESSION_SECRET=...
|
|
252
|
+
BCP_CSRF_SECRET=...
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The default CSRF cookie is:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
name bcp_csrf
|
|
259
|
+
HttpOnly true
|
|
260
|
+
SameSite Lax
|
|
261
|
+
Secure true in production
|
|
262
|
+
max age 2 hours
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The cookie is signed but the token itself must still be treated as security-sensitive request state. Do not log CSRF tokens.
|
|
266
|
+
|
|
267
|
+
## Lower-level verification
|
|
268
|
+
|
|
269
|
+
The following helpers are available when applications need custom response behavior:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
isSafeHttpMethod()
|
|
273
|
+
isSameOriginRequest()
|
|
274
|
+
verifyCsrfToken()
|
|
275
|
+
verifyCsrfRequest()
|
|
276
|
+
destroyCsrfToken()
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`RequestSecurityError` uses status `403` and distinguishes `INVALID_ORIGIN` from `INVALID_CSRF_TOKEN`.
|
|
280
|
+
|
|
281
|
+
## Security model
|
|
282
|
+
|
|
283
|
+
Authorization and CSRF protection solve different problems:
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
auth() / session
|
|
287
|
+
-> who is this user?
|
|
288
|
+
|
|
289
|
+
permission / policy
|
|
290
|
+
-> may this user perform this operation?
|
|
291
|
+
|
|
292
|
+
origin + CSRF
|
|
293
|
+
-> did this browser mutation come from an allowed application context?
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Applications should still validate all mutation input and enforce authorization on the server. Client-side UI checks are only presentation logic and must not replace server authorization.
|
|
297
|
+
|
|
298
|
+
`bcp/auth` and the request-security helpers in `bcp/server` are server-only surfaces.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"versionTarget": "0.2.
|
|
4
|
+
"versionTarget": "0.2.6",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
6
|
"sections": [
|
|
7
7
|
{
|
|
@@ -35,12 +35,13 @@
|
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"id": "authentication",
|
|
38
|
-
"title": "Authentication",
|
|
39
|
-
"description": "Authentication Platform v2, revocable
|
|
38
|
+
"title": "Authentication & Authorization",
|
|
39
|
+
"description": "Authentication Platform v2, revocable sessions, permission/policy authorization, route guards and CSRF protection.",
|
|
40
40
|
"pages": [
|
|
41
41
|
{ "route": "/docs/authentication", "source": "authentication.md", "title": "Authentication" },
|
|
42
42
|
{ "route": "/docs/auth-session-store", "source": "auth-session-store.md", "title": "Auth Session Stores" },
|
|
43
43
|
{ "route": "/docs/auth-route-guards", "source": "auth-route-guards.md", "title": "Auth Route Guards" },
|
|
44
|
+
{ "route": "/docs/authorization-security", "source": "authorization-security.md", "title": "Authorization & Security v2" },
|
|
44
45
|
{ "route": "/docs/session-auth", "source": "session-auth.md", "title": "JWT Sessions" }
|
|
45
46
|
]
|
|
46
47
|
},
|
|
@@ -106,7 +107,8 @@
|
|
|
106
107
|
}
|
|
107
108
|
],
|
|
108
109
|
"releases": [
|
|
109
|
-
{ "route": "/releases/0.2.
|
|
110
|
+
{ "route": "/releases/0.2.6", "source": "releases/0.2.6.md", "version": "0.2.6", "state": "unreleased" },
|
|
111
|
+
{ "route": "/releases/0.2.5", "source": "releases/0.2.5.md", "version": "0.2.5" },
|
|
110
112
|
{ "route": "/releases/0.2.4", "source": "releases/0.2.4.md", "version": "0.2.4" },
|
|
111
113
|
{ "route": "/releases/0.2.3", "source": "releases/0.2.3.md", "version": "0.2.3" },
|
|
112
114
|
{ "route": "/releases/0.2.2", "source": "releases/0.2.2.md", "version": "0.2.2" },
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.6",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
|
-
"baseline": "
|
|
6
|
+
"baseline": "authorization-security-v2",
|
|
7
7
|
"runtime": {
|
|
8
8
|
"node": ">=24.11.0",
|
|
9
9
|
"react": "19",
|
|
@@ -53,6 +53,12 @@
|
|
|
53
53
|
"authLogoutAll": true,
|
|
54
54
|
"authIdleTimeout": true,
|
|
55
55
|
"authGuestGuard": true,
|
|
56
|
+
"authorizationSecurityV2": true,
|
|
57
|
+
"permissionAuthorization": true,
|
|
58
|
+
"authorizationPolicies": true,
|
|
59
|
+
"permissionRouteGuards": true,
|
|
60
|
+
"sameOriginProtection": true,
|
|
61
|
+
"csrfProtection": true,
|
|
56
62
|
"databaseMigrations": true,
|
|
57
63
|
"databaseAdapterContract": true,
|
|
58
64
|
"databasePostgresql": true,
|
|
@@ -92,7 +98,7 @@
|
|
|
92
98
|
"s3-compatible"
|
|
93
99
|
],
|
|
94
100
|
"compatibility": {
|
|
95
|
-
"previousBaseline": "0.2.
|
|
101
|
+
"previousBaseline": "0.2.5",
|
|
96
102
|
"intentionalBreakingChangesFromPreviousBaseline": false,
|
|
97
103
|
"migrationGuide": "migration-0.2.md"
|
|
98
104
|
},
|
|
@@ -107,7 +113,8 @@
|
|
|
107
113
|
"applicationPackaging": "application-packaging.md",
|
|
108
114
|
"authentication": "authentication.md",
|
|
109
115
|
"authSessionStore": "auth-session-store.md",
|
|
116
|
+
"authorizationSecurity": "authorization-security.md",
|
|
110
117
|
"migrationGuide": "migration-0.2.md",
|
|
111
|
-
"releaseNotes": "releases/0.2.
|
|
118
|
+
"releaseNotes": "releases/0.2.6.md"
|
|
112
119
|
}
|
|
113
120
|
}
|