@objectstack/platform-objects 15.0.0 → 15.1.1

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.
@@ -53,7 +53,7 @@ var SysUser = data.ObjectSchema.create({
53
53
  // "add a teammate" affordance — the Users list is always reachable —
54
54
  // and every add flows through better-auth invitations, never bespoke
55
55
  // sys_user CRUD.
56
- visible: "features.organization != false",
56
+ requiresFeature: "organization",
57
57
  successMessage: "Invitation sent",
58
58
  refreshAfter: true,
59
59
  params: [
@@ -64,9 +64,11 @@ var SysUser = data.ObjectSchema.create({
64
64
  // ── Platform admin operations (require better-auth `admin` plugin) ─
65
65
  //
66
66
  // These actions hit /api/v1/auth/admin/* endpoints that are only
67
- // wired when `auth.plugins.admin` is enabled. When the plugin is
68
- // disabled the actions still render (schema is static) but server
69
- // returns 404. UI surfaces them under the row menu AND the
67
+ // wired when `auth.plugins.admin` is enabled, so each one is gated on
68
+ // `requiresFeature: 'admin'` (#2874) when the plugin is off the UI
69
+ // hides them instead of rendering buttons that 404. SCIM deployments
70
+ // keep them: SCIM forces the admin plugin (and `features.admin`) on
71
+ // (ADR-0071). UI surfaces them under the row menu AND the
70
72
  // record-detail header (`record_header`, overflowing into the ⋯
71
73
  // "More" menu) so platform admins can manage an account from either
72
74
  // the Users list or an open user record — without dropping to SQL or
@@ -79,6 +81,7 @@ var SysUser = data.ObjectSchema.create({
79
81
  locations: ["list_item", "record_header"],
80
82
  type: "api",
81
83
  target: "/api/v1/auth/admin/ban-user",
84
+ requiresFeature: "admin",
82
85
  recordIdParam: "userId",
83
86
  successMessage: "User banned",
84
87
  refreshAfter: true,
@@ -95,6 +98,7 @@ var SysUser = data.ObjectSchema.create({
95
98
  locations: ["list_item", "record_header"],
96
99
  type: "api",
97
100
  target: "/api/v1/auth/admin/unban-user",
101
+ requiresFeature: "admin",
98
102
  recordIdParam: "userId",
99
103
  successMessage: "User unbanned",
100
104
  refreshAfter: true
@@ -110,6 +114,7 @@ var SysUser = data.ObjectSchema.create({
110
114
  locations: ["list_item", "record_header"],
111
115
  type: "api",
112
116
  target: "/api/v1/auth/admin/unlock-user",
117
+ requiresFeature: "admin",
113
118
  recordIdParam: "userId",
114
119
  successMessage: "Account unlocked",
115
120
  refreshAfter: true
@@ -129,7 +134,7 @@ var SysUser = data.ObjectSchema.create({
129
134
  locations: ["list_toolbar"],
130
135
  type: "api",
131
136
  target: "/api/v1/auth/admin/create-user",
132
- visible: "features.admin == true",
137
+ requiresFeature: "admin",
133
138
  successMessage: "User created",
134
139
  refreshAfter: true,
135
140
  params: [
@@ -146,7 +151,7 @@ var SysUser = data.ObjectSchema.create({
146
151
  // otherwise the create-user endpoint rejects a phone with
147
152
  // "Phone numbers require the phoneNumber auth plugin". `features.phoneNumber`
148
153
  // is served in /api/v1/auth/config (getPublicConfig).
149
- visible: "features.phoneNumber == true"
154
+ requiresFeature: "phoneNumber"
150
155
  },
151
156
  { field: "name", required: false },
152
157
  {
@@ -188,6 +193,7 @@ var SysUser = data.ObjectSchema.create({
188
193
  // legacy role scalar), can mint a temporary password, and stamps
189
194
  // must_change_password.
190
195
  target: "/api/v1/auth/admin/set-user-password",
196
+ requiresFeature: "admin",
191
197
  recordIdParam: "userId",
192
198
  successMessage: "Password updated",
193
199
  refreshAfter: false,
@@ -225,6 +231,7 @@ var SysUser = data.ObjectSchema.create({
225
231
  locations: ["list_item", "record_header"],
226
232
  type: "api",
227
233
  target: "/api/v1/auth/admin/set-role",
234
+ requiresFeature: "admin",
228
235
  recordIdParam: "userId",
229
236
  successMessage: "Role updated",
230
237
  refreshAfter: true,
@@ -240,6 +247,7 @@ var SysUser = data.ObjectSchema.create({
240
247
  locations: ["list_item", "record_header"],
241
248
  type: "api",
242
249
  target: "/api/v1/auth/admin/impersonate-user",
250
+ requiresFeature: "admin",
243
251
  recordIdParam: "userId",
244
252
  successMessage: "Now impersonating user",
245
253
  refreshAfter: true,
@@ -357,6 +365,7 @@ var SysUser = data.ObjectSchema.create({
357
365
  type: "api",
358
366
  target: "/api/v1/auth/two-factor/enable",
359
367
  visible: "record.id == ctx.user.id && record.two_factor_enabled != true",
368
+ requiresFeature: "twoFactor",
360
369
  successMessage: "Two-factor authentication enabled. Scan the QR code or paste the otpauth URI into your authenticator app, then verify a code to complete setup.",
361
370
  refreshAfter: true,
362
371
  params: [
@@ -372,6 +381,7 @@ var SysUser = data.ObjectSchema.create({
372
381
  type: "api",
373
382
  target: "/api/v1/auth/two-factor/disable",
374
383
  visible: "record.id == ctx.user.id && record.two_factor_enabled == true",
384
+ requiresFeature: "twoFactor",
375
385
  confirmText: "Turn off two-factor authentication? Your account will be less secure.",
376
386
  successMessage: "Two-factor authentication disabled.",
377
387
  refreshAfter: true,
@@ -388,6 +398,7 @@ var SysUser = data.ObjectSchema.create({
388
398
  type: "api",
389
399
  target: "/api/v1/auth/two-factor/generate-backup-codes",
390
400
  visible: "record.id == ctx.user.id && record.two_factor_enabled == true",
401
+ requiresFeature: "twoFactor",
391
402
  confirmText: "Generate a new set of backup codes? Any previously generated codes will stop working.",
392
403
  successMessage: "New backup codes generated \u2014 save them somewhere safe.",
393
404
  refreshAfter: false,
@@ -1195,7 +1206,7 @@ var SysOrganization = data.ObjectSchema.create({
1195
1206
  // populated by the console/account shells from `/auth/config`;
1196
1207
  // we default to visible when the flag is undefined so we don't
1197
1208
  // accidentally hide the button while auth config is still loading.
1198
- visible: "features.multiOrgEnabled != false",
1209
+ requiresFeature: "multiOrgEnabled",
1199
1210
  params: [
1200
1211
  { field: "name", required: true },
1201
1212
  { field: "slug", required: true },
@@ -1216,7 +1227,7 @@ var SysOrganization = data.ObjectSchema.create({
1216
1227
  // Org-admin actions are multi-org-only; hide them in single-org for
1217
1228
  // consistency with `create_organization` (the org list is empty there,
1218
1229
  // but this also guards direct record-URL access).
1219
- visible: "features.multiOrgEnabled != false",
1230
+ requiresFeature: "multiOrgEnabled",
1220
1231
  successMessage: "Organization updated",
1221
1232
  refreshAfter: true,
1222
1233
  params: [
@@ -1235,7 +1246,7 @@ var SysOrganization = data.ObjectSchema.create({
1235
1246
  type: "api",
1236
1247
  target: "/api/v1/auth/organization/delete",
1237
1248
  recordIdParam: "organizationId",
1238
- visible: "features.multiOrgEnabled != false",
1249
+ requiresFeature: "multiOrgEnabled",
1239
1250
  confirmText: "Delete this organization? All members will lose access immediately. This cannot be undone.",
1240
1251
  successMessage: "Organization deleted",
1241
1252
  refreshAfter: true
@@ -1254,7 +1265,7 @@ var SysOrganization = data.ObjectSchema.create({
1254
1265
  type: "api",
1255
1266
  target: "/api/v1/auth/organization/set-active",
1256
1267
  recordIdParam: "organizationId",
1257
- visible: "features.multiOrgEnabled != false",
1268
+ requiresFeature: "multiOrgEnabled",
1258
1269
  successMessage: "Active organization switched",
1259
1270
  refreshAfter: true
1260
1271
  },
@@ -1271,7 +1282,7 @@ var SysOrganization = data.ObjectSchema.create({
1271
1282
  type: "api",
1272
1283
  target: "/api/v1/auth/organization/leave",
1273
1284
  recordIdParam: "organizationId",
1274
- visible: "features.multiOrgEnabled != false",
1285
+ requiresFeature: "multiOrgEnabled",
1275
1286
  confirmText: "Leave this organization? You will lose access to all of its resources.",
1276
1287
  successMessage: "You have left the organization",
1277
1288
  refreshAfter: true
@@ -1293,7 +1304,7 @@ var SysOrganization = data.ObjectSchema.create({
1293
1304
  type: "api",
1294
1305
  target: "/api/v1/cloud/organizations/{id}/change-slug",
1295
1306
  method: "POST",
1296
- visible: "features.multiOrgEnabled != false",
1307
+ requiresFeature: "multiOrgEnabled",
1297
1308
  confirmText: "Renaming the slug rewrites every platform subdomain for this org and parks the old slug for 90 days. Continue?",
1298
1309
  successMessage: "Organization slug changed",
1299
1310
  refreshAfter: true,
@@ -1431,7 +1442,7 @@ var SysMember = data.ObjectSchema.create({
1431
1442
  // better-auth endpoints resolve the session's active org, which
1432
1443
  // single-org mode now guarantees via plugin-auth's default-org
1433
1444
  // bootstrap. Same gate on every membership mutation below.
1434
- visible: "features.organization != false",
1445
+ requiresFeature: "organization",
1435
1446
  successMessage: "Member added",
1436
1447
  refreshAfter: true,
1437
1448
  params: [
@@ -1449,7 +1460,7 @@ var SysMember = data.ObjectSchema.create({
1449
1460
  type: "api",
1450
1461
  target: "/api/v1/auth/organization/update-member-role",
1451
1462
  recordIdParam: "memberId",
1452
- visible: "features.organization != false",
1463
+ requiresFeature: "organization",
1453
1464
  successMessage: "Member role updated",
1454
1465
  refreshAfter: true,
1455
1466
  params: [
@@ -1466,7 +1477,7 @@ var SysMember = data.ObjectSchema.create({
1466
1477
  type: "api",
1467
1478
  target: "/api/v1/auth/organization/remove-member",
1468
1479
  recordIdParam: "memberIdOrEmail",
1469
- visible: "features.organization != false",
1480
+ requiresFeature: "organization",
1470
1481
  confirmText: "Remove this member from the organization? They will lose access to all org resources.",
1471
1482
  successMessage: "Member removed",
1472
1483
  refreshAfter: true
@@ -1488,7 +1499,10 @@ var SysMember = data.ObjectSchema.create({
1488
1499
  target: "/api/v1/auth/organization/update-member-role",
1489
1500
  recordIdParam: "memberId",
1490
1501
  bodyExtra: { role: "owner" },
1491
- visible: "record.role != 'owner' && features.organization != false",
1502
+ // The residual row predicate stays hand-written; the feature gate is
1503
+ // AND-composed onto it by the requiresFeature lowering.
1504
+ visible: "record.role != 'owner'",
1505
+ requiresFeature: "organization",
1492
1506
  confirmText: "Transfer ownership of this organization to the selected member? You will be demoted to admin and lose owner-only privileges.",
1493
1507
  successMessage: "Ownership transferred",
1494
1508
  refreshAfter: true
@@ -1596,7 +1610,7 @@ var SysInvitation = data.ObjectSchema.create({
1596
1610
  // sys_organization.create_organization). The recipient-side
1597
1611
  // accept/reject actions below stay record-gated — they are
1598
1612
  // unreachable in single-org anyway (no invitation rows exist).
1599
- visible: "features.organization != false",
1613
+ requiresFeature: "organization",
1600
1614
  successMessage: "Invitation sent",
1601
1615
  refreshAfter: true,
1602
1616
  params: [
@@ -1614,7 +1628,7 @@ var SysInvitation = data.ObjectSchema.create({
1614
1628
  type: "api",
1615
1629
  target: "/api/v1/auth/organization/cancel-invitation",
1616
1630
  recordIdParam: "invitationId",
1617
- visible: "features.organization != false",
1631
+ requiresFeature: "organization",
1618
1632
  confirmText: "Cancel this invitation? The recipient will no longer be able to accept it.",
1619
1633
  successMessage: "Invitation canceled",
1620
1634
  refreshAfter: true
@@ -1628,7 +1642,7 @@ var SysInvitation = data.ObjectSchema.create({
1628
1642
  type: "api",
1629
1643
  target: "/api/v1/auth/organization/invite-member",
1630
1644
  bodyExtra: { resend: true },
1631
- visible: "features.organization != false",
1645
+ requiresFeature: "organization",
1632
1646
  successMessage: "Invitation resent",
1633
1647
  refreshAfter: true,
1634
1648
  params: [
@@ -1818,7 +1832,7 @@ var SysTeam = data.ObjectSchema.create({
1818
1832
  // Teams are nested inside organizations — a multi-org-only concept.
1819
1833
  // Gate every team mutation on the multi-org flag so the affordances
1820
1834
  // disappear in single-org (mirrors sys_organization.create_organization).
1821
- visible: "features.organization != false",
1835
+ requiresFeature: "organization",
1822
1836
  successMessage: "Team created",
1823
1837
  refreshAfter: true,
1824
1838
  params: [
@@ -1839,7 +1853,7 @@ var SysTeam = data.ObjectSchema.create({
1839
1853
  target: "/api/v1/auth/organization/update-team",
1840
1854
  recordIdParam: "teamId",
1841
1855
  bodyShape: { wrap: "data" },
1842
- visible: "features.organization != false",
1856
+ requiresFeature: "organization",
1843
1857
  successMessage: "Team updated",
1844
1858
  refreshAfter: true,
1845
1859
  params: [
@@ -1858,7 +1872,7 @@ var SysTeam = data.ObjectSchema.create({
1858
1872
  type: "api",
1859
1873
  target: "/api/v1/auth/organization/remove-team",
1860
1874
  recordIdParam: "teamId",
1861
- visible: "features.organization != false",
1875
+ requiresFeature: "organization",
1862
1876
  confirmText: "Delete this team? Members will lose any team-scoped access. This cannot be undone.",
1863
1877
  successMessage: "Team deleted",
1864
1878
  refreshAfter: true
@@ -1972,7 +1986,7 @@ var SysTeamMember = data.ObjectSchema.create({
1972
1986
  // Team membership lives under organizations — multi-org-only. Gate
1973
1987
  // both mutations so they vanish in single-org (mirrors
1974
1988
  // sys_organization.create_organization).
1975
- visible: "features.organization != false",
1989
+ requiresFeature: "organization",
1976
1990
  successMessage: "Team member added",
1977
1991
  refreshAfter: true,
1978
1992
  params: [
@@ -1993,7 +2007,7 @@ var SysTeamMember = data.ObjectSchema.create({
1993
2007
  locations: ["list_item"],
1994
2008
  type: "api",
1995
2009
  target: "/api/v1/auth/organization/remove-team-member",
1996
- visible: "features.organization != false",
2010
+ requiresFeature: "organization",
1997
2011
  confirmText: "Remove this user from the team? They will lose any team-scoped access.",
1998
2012
  successMessage: "Team member removed",
1999
2013
  refreshAfter: true,
@@ -2549,6 +2563,7 @@ var SysTwoFactor = data.ObjectSchema.create({
2549
2563
  locations: ["list_toolbar"],
2550
2564
  type: "api",
2551
2565
  target: "/api/v1/auth/two-factor/enable",
2566
+ requiresFeature: "twoFactor",
2552
2567
  refreshAfter: true,
2553
2568
  params: [
2554
2569
  { name: "password", label: "Current Password", type: "text", required: true }
@@ -2571,6 +2586,7 @@ var SysTwoFactor = data.ObjectSchema.create({
2571
2586
  locations: ["list_toolbar"],
2572
2587
  type: "api",
2573
2588
  target: "/api/v1/auth/two-factor/disable",
2589
+ requiresFeature: "twoFactor",
2574
2590
  confirmText: "Disable two-factor authentication on your account?",
2575
2591
  successMessage: "2FA disabled",
2576
2592
  refreshAfter: true,
@@ -2586,6 +2602,7 @@ var SysTwoFactor = data.ObjectSchema.create({
2586
2602
  locations: ["list_toolbar", "list_item"],
2587
2603
  type: "api",
2588
2604
  target: "/api/v1/auth/two-factor/generate-backup-codes",
2605
+ requiresFeature: "twoFactor",
2589
2606
  confirmText: "Regenerate backup codes? All previous backup codes will stop working immediately.",
2590
2607
  refreshAfter: true,
2591
2608
  params: [
@@ -2900,6 +2917,7 @@ var SysOauthApplication = data.ObjectSchema.create({
2900
2917
  type: "api",
2901
2918
  method: "POST",
2902
2919
  target: "/api/v1/auth/admin/oauth2/toggle-disabled",
2920
+ requiresFeature: "oidcProvider",
2903
2921
  confirmText: "Disable this OAuth application? Active access/refresh tokens issued to it will continue to be rejected at the token, authorize, and introspect endpoints. Existing integrations will stop working immediately.",
2904
2922
  successMessage: "OAuth application disabled",
2905
2923
  refreshAfter: true,
@@ -2919,6 +2937,7 @@ var SysOauthApplication = data.ObjectSchema.create({
2919
2937
  type: "api",
2920
2938
  method: "POST",
2921
2939
  target: "/api/v1/auth/admin/oauth2/toggle-disabled",
2940
+ requiresFeature: "oidcProvider",
2922
2941
  confirmText: "Re-enable this OAuth application? Token issuance, authorization, and introspection will resume immediately.",
2923
2942
  successMessage: "OAuth application enabled",
2924
2943
  refreshAfter: true,
@@ -2938,6 +2957,7 @@ var SysOauthApplication = data.ObjectSchema.create({
2938
2957
  type: "api",
2939
2958
  method: "POST",
2940
2959
  target: "/api/v1/auth/sys-oauth-application/register",
2960
+ requiresFeature: "oidcProvider",
2941
2961
  refreshAfter: true,
2942
2962
  params: [
2943
2963
  { name: "name", label: "Application Name", type: "text", required: true },
@@ -2969,6 +2989,7 @@ var SysOauthApplication = data.ObjectSchema.create({
2969
2989
  type: "api",
2970
2990
  method: "POST",
2971
2991
  target: "/api/v1/auth/oauth2/client/rotate-secret",
2992
+ requiresFeature: "oidcProvider",
2972
2993
  confirmText: "Rotate this OAuth client's secret? The previous secret will stop working immediately and any integrations using it will break until they are updated with the new secret. The new secret is shown only once.",
2973
2994
  refreshAfter: true,
2974
2995
  params: [
@@ -2993,6 +3014,7 @@ var SysOauthApplication = data.ObjectSchema.create({
2993
3014
  type: "api",
2994
3015
  method: "POST",
2995
3016
  target: "/api/v1/auth/oauth2/delete-client",
3017
+ requiresFeature: "oidcProvider",
2996
3018
  confirmText: "Permanently delete this OAuth application? All issued tokens and consents will be invalidated and integrations using this client_id will stop working immediately. This cannot be undone.",
2997
3019
  successMessage: "OAuth application deleted",
2998
3020
  refreshAfter: true,
@@ -3172,6 +3194,25 @@ var SysOauthApplication = data.ObjectSchema.create({
3172
3194
  description: "OIDC subject type (e.g. public, pairwise)",
3173
3195
  group: "Credentials"
3174
3196
  }),
3197
+ jwks: data.Field.textarea({
3198
+ label: "JWKS",
3199
+ required: false,
3200
+ description: "Client JSON Web Key Set (for private_key_jwt / signed-request verification)",
3201
+ group: "Credentials"
3202
+ }),
3203
+ jwks_uri: data.Field.url({
3204
+ label: "JWKS URI",
3205
+ required: false,
3206
+ description: "URL of the client JSON Web Key Set",
3207
+ group: "Credentials"
3208
+ }),
3209
+ dpop_bound_access_tokens: data.Field.boolean({
3210
+ label: "DPoP-bound Access Tokens",
3211
+ required: false,
3212
+ defaultValue: false,
3213
+ description: "Require access tokens issued to this client to be DPoP-bound (RFC 9449)",
3214
+ group: "Credentials"
3215
+ }),
3175
3216
  // ── Behaviour flags ──────────────────────────────────────────
3176
3217
  disabled: data.Field.boolean({
3177
3218
  label: "Disabled",
@@ -3191,6 +3232,18 @@ var SysOauthApplication = data.ObjectSchema.create({
3191
3232
  description: "Allow the client to call the OIDC end-session endpoint",
3192
3233
  group: "Behaviour"
3193
3234
  }),
3235
+ backchannel_logout_uri: data.Field.url({
3236
+ label: "Back-channel Logout URI",
3237
+ required: false,
3238
+ description: "OIDC back-channel logout endpoint of the client",
3239
+ group: "Behaviour"
3240
+ }),
3241
+ backchannel_logout_session_required: data.Field.boolean({
3242
+ label: "Back-channel Logout Session Required",
3243
+ required: false,
3244
+ description: "Whether the back-channel logout token must include a sid claim",
3245
+ group: "Behaviour"
3246
+ }),
3194
3247
  // ── Software statement (RFC 7591 §2.3) ───────────────────────
3195
3248
  software_id: data.Field.text({
3196
3249
  label: "Software ID",
@@ -3316,6 +3369,22 @@ var SysOauthAccessToken = data.ObjectSchema.create({
3316
3369
  maxLength: 255,
3317
3370
  description: "Caller-supplied correlation identifier"
3318
3371
  }),
3372
+ authorization_code_id: data.Field.text({
3373
+ label: "Authorization Code ID",
3374
+ required: false,
3375
+ maxLength: 255,
3376
+ description: "ID of the authorization-code grant this token originates from"
3377
+ }),
3378
+ resources: data.Field.textarea({
3379
+ label: "Resources",
3380
+ required: false,
3381
+ description: "JSON-serialized list of RFC 8707 resource indicators bound to this token"
3382
+ }),
3383
+ requested_user_info_claims: data.Field.textarea({
3384
+ label: "Requested UserInfo Claims",
3385
+ required: false,
3386
+ description: "JSON-serialized list of OIDC claims requested for the userinfo endpoint"
3387
+ }),
3319
3388
  scopes: data.Field.textarea({
3320
3389
  label: "Scopes",
3321
3390
  required: true,
@@ -3329,6 +3398,16 @@ var SysOauthAccessToken = data.ObjectSchema.create({
3329
3398
  label: "Created At",
3330
3399
  defaultValue: "NOW()",
3331
3400
  readonly: true
3401
+ }),
3402
+ revoked: data.Field.datetime({
3403
+ label: "Revoked At",
3404
+ required: false,
3405
+ description: "Timestamp at which this access token was revoked"
3406
+ }),
3407
+ confirmation: data.Field.textarea({
3408
+ label: "Confirmation",
3409
+ required: false,
3410
+ description: "JSON RFC 7800 cnf claim (e.g. DPoP key thumbprint) binding this token to a key"
3332
3411
  })
3333
3412
  },
3334
3413
  indexes: [
@@ -3336,7 +3415,8 @@ var SysOauthAccessToken = data.ObjectSchema.create({
3336
3415
  { fields: ["client_id"] },
3337
3416
  { fields: ["session_id"] },
3338
3417
  { fields: ["user_id"] },
3339
- { fields: ["refresh_id"] }
3418
+ { fields: ["refresh_id"] },
3419
+ { fields: ["authorization_code_id"] }
3340
3420
  ],
3341
3421
  enable: {
3342
3422
  trackHistory: false,
@@ -3402,6 +3482,22 @@ var SysOauthRefreshToken = data.ObjectSchema.create({
3402
3482
  maxLength: 255,
3403
3483
  description: "Caller-supplied correlation identifier"
3404
3484
  }),
3485
+ authorization_code_id: data.Field.text({
3486
+ label: "Authorization Code ID",
3487
+ required: false,
3488
+ maxLength: 255,
3489
+ description: "ID of the authorization-code grant this token chain originates from"
3490
+ }),
3491
+ resources: data.Field.textarea({
3492
+ label: "Resources",
3493
+ required: false,
3494
+ description: "JSON-serialized list of RFC 8707 resource indicators bound to this token"
3495
+ }),
3496
+ requested_user_info_claims: data.Field.textarea({
3497
+ label: "Requested UserInfo Claims",
3498
+ required: false,
3499
+ description: "JSON-serialized list of OIDC claims requested for the userinfo endpoint"
3500
+ }),
3405
3501
  scopes: data.Field.textarea({
3406
3502
  label: "Scopes",
3407
3503
  required: true,
@@ -3421,17 +3517,38 @@ var SysOauthRefreshToken = data.ObjectSchema.create({
3421
3517
  required: false,
3422
3518
  description: "Timestamp at which this refresh token was revoked"
3423
3519
  }),
3520
+ rotated_at: data.Field.datetime({
3521
+ label: "Rotated At",
3522
+ required: false,
3523
+ description: "Timestamp at which this token was rotated (superseded by a new row)"
3524
+ }),
3525
+ rotation_replay_response: data.Field.textarea({
3526
+ label: "Rotation Replay Response",
3527
+ required: false,
3528
+ description: "Cached token response replayed when the old token is re-presented within the reuse interval"
3529
+ }),
3530
+ rotation_replay_expires_at: data.Field.datetime({
3531
+ label: "Rotation Replay Expires At",
3532
+ required: false,
3533
+ description: "End of the post-rotation reuse interval during which the replay response is served"
3534
+ }),
3424
3535
  auth_time: data.Field.datetime({
3425
3536
  label: "Auth Time",
3426
3537
  required: false,
3427
3538
  description: "When the user originally authenticated for this token chain"
3539
+ }),
3540
+ confirmation: data.Field.textarea({
3541
+ label: "Confirmation",
3542
+ required: false,
3543
+ description: "JSON RFC 7800 cnf claim (e.g. DPoP key thumbprint) binding this token to a key"
3428
3544
  })
3429
3545
  },
3430
3546
  indexes: [
3431
3547
  { fields: ["token"], unique: true },
3432
3548
  { fields: ["client_id"] },
3433
3549
  { fields: ["session_id"] },
3434
- { fields: ["user_id"] }
3550
+ { fields: ["user_id"] },
3551
+ { fields: ["authorization_code_id"] }
3435
3552
  ],
3436
3553
  enable: {
3437
3554
  trackHistory: false,
@@ -3481,6 +3598,16 @@ var SysOauthConsent = data.ObjectSchema.create({
3481
3598
  maxLength: 255,
3482
3599
  description: "Caller-supplied correlation identifier"
3483
3600
  }),
3601
+ resources: data.Field.textarea({
3602
+ label: "Resources",
3603
+ required: false,
3604
+ description: "JSON-serialized list of RFC 8707 resource indicators the consent covers"
3605
+ }),
3606
+ requested_user_info_claims: data.Field.textarea({
3607
+ label: "Requested UserInfo Claims",
3608
+ required: false,
3609
+ description: "JSON-serialized list of OIDC claims the user consented to expose"
3610
+ }),
3484
3611
  scopes: data.Field.textarea({
3485
3612
  label: "Scopes",
3486
3613
  required: true,
@@ -3510,6 +3637,215 @@ var SysOauthConsent = data.ObjectSchema.create({
3510
3637
  mru: false
3511
3638
  }
3512
3639
  });
3640
+ var SysOauthResource = data.ObjectSchema.create({
3641
+ name: "sys_oauth_resource",
3642
+ label: "OAuth Resource",
3643
+ pluralLabel: "OAuth Resources",
3644
+ icon: "server",
3645
+ isSystem: true,
3646
+ managedBy: "better-auth",
3647
+ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3648
+ // but may add overlay row-level config. Use `no-overlay` if you need to
3649
+ // forbid sys_metadata overlays entirely.
3650
+ protection: {
3651
+ lock: "full",
3652
+ reason: "Identity table managed by better-auth \u2014 see ADR-0010.",
3653
+ docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
3654
+ },
3655
+ description: "Registered OAuth protected resources (RFC 8707 resource indicators)",
3656
+ displayNameField: "name",
3657
+ nameField: "name",
3658
+ highlightFields: ["name", "identifier", "disabled"],
3659
+ fields: {
3660
+ id: data.Field.text({
3661
+ label: "ID",
3662
+ required: true,
3663
+ readonly: true
3664
+ }),
3665
+ identifier: data.Field.text({
3666
+ label: "Identifier",
3667
+ required: true,
3668
+ maxLength: 1024,
3669
+ description: "Resource indicator URI presented in the RFC 8707 resource parameter"
3670
+ }),
3671
+ name: data.Field.text({
3672
+ label: "Name",
3673
+ required: true,
3674
+ maxLength: 255
3675
+ }),
3676
+ access_token_ttl: data.Field.number({
3677
+ label: "Access Token TTL",
3678
+ required: false,
3679
+ description: "Access-token lifetime in seconds for this resource (overrides the server default)"
3680
+ }),
3681
+ refresh_token_ttl: data.Field.number({
3682
+ label: "Refresh Token TTL",
3683
+ required: false,
3684
+ description: "Refresh-token lifetime in seconds for this resource (overrides the server default)"
3685
+ }),
3686
+ signing_algorithm: data.Field.text({
3687
+ label: "Signing Algorithm",
3688
+ required: false,
3689
+ maxLength: 32,
3690
+ description: "JWS algorithm used to sign access tokens for this resource"
3691
+ }),
3692
+ signing_key_id: data.Field.text({
3693
+ label: "Signing Key ID",
3694
+ required: false,
3695
+ maxLength: 255,
3696
+ description: "Key id (kid) used to sign access tokens for this resource"
3697
+ }),
3698
+ allowed_scopes: data.Field.textarea({
3699
+ label: "Allowed Scopes",
3700
+ required: false,
3701
+ description: "JSON-serialized list of scopes clients may request for this resource"
3702
+ }),
3703
+ custom_claims: data.Field.textarea({
3704
+ label: "Custom Claims",
3705
+ required: false,
3706
+ description: "JSON object of extra claims stamped on access tokens for this resource"
3707
+ }),
3708
+ dpop_bound_access_tokens_required: data.Field.boolean({
3709
+ label: "DPoP Required",
3710
+ required: false,
3711
+ defaultValue: false,
3712
+ description: "Require access tokens for this resource to be DPoP-bound (RFC 9449)"
3713
+ }),
3714
+ disabled: data.Field.boolean({
3715
+ label: "Disabled",
3716
+ required: false,
3717
+ defaultValue: false
3718
+ }),
3719
+ policy_version: data.Field.number({
3720
+ label: "Policy Version",
3721
+ required: false,
3722
+ defaultValue: 1,
3723
+ description: "Monotonic version of the resource token policy"
3724
+ }),
3725
+ metadata: data.Field.textarea({
3726
+ label: "Metadata",
3727
+ required: false,
3728
+ description: "JSON object of additional resource metadata"
3729
+ }),
3730
+ created_at: data.Field.datetime({
3731
+ label: "Created At",
3732
+ defaultValue: "NOW()",
3733
+ readonly: true
3734
+ }),
3735
+ updated_at: data.Field.datetime({
3736
+ label: "Updated At",
3737
+ defaultValue: "NOW()",
3738
+ readonly: true
3739
+ })
3740
+ },
3741
+ indexes: [
3742
+ { fields: ["identifier"], unique: true }
3743
+ ],
3744
+ enable: {
3745
+ trackHistory: false,
3746
+ searchable: false,
3747
+ apiEnabled: false,
3748
+ apiMethods: [],
3749
+ trash: false,
3750
+ mru: false
3751
+ }
3752
+ });
3753
+ var SysOauthClientResource = data.ObjectSchema.create({
3754
+ name: "sys_oauth_client_resource",
3755
+ label: "OAuth Client Resource",
3756
+ pluralLabel: "OAuth Client Resources",
3757
+ icon: "link",
3758
+ isSystem: true,
3759
+ managedBy: "better-auth",
3760
+ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3761
+ // but may add overlay row-level config. Use `no-overlay` if you need to
3762
+ // forbid sys_metadata overlays entirely.
3763
+ protection: {
3764
+ lock: "full",
3765
+ reason: "Identity table managed by better-auth \u2014 see ADR-0010.",
3766
+ docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
3767
+ },
3768
+ description: "Grants allowing an OAuth client to request tokens for a protected resource",
3769
+ highlightFields: ["client_id", "resource_id"],
3770
+ fields: {
3771
+ id: data.Field.text({
3772
+ label: "ID",
3773
+ required: true,
3774
+ readonly: true
3775
+ }),
3776
+ client_id: data.Field.text({
3777
+ label: "Client ID",
3778
+ required: true,
3779
+ description: "Foreign key to sys_oauth_application.client_id"
3780
+ }),
3781
+ resource_id: data.Field.text({
3782
+ label: "Resource ID",
3783
+ required: true,
3784
+ maxLength: 1024,
3785
+ description: "Foreign key to sys_oauth_resource.identifier"
3786
+ }),
3787
+ metadata: data.Field.textarea({
3788
+ label: "Metadata",
3789
+ required: false,
3790
+ description: "JSON object of additional grant metadata"
3791
+ }),
3792
+ created_at: data.Field.datetime({
3793
+ label: "Created At",
3794
+ defaultValue: "NOW()",
3795
+ readonly: true
3796
+ })
3797
+ },
3798
+ indexes: [
3799
+ { fields: ["client_id"] },
3800
+ { fields: ["resource_id"] }
3801
+ ],
3802
+ enable: {
3803
+ trackHistory: false,
3804
+ searchable: false,
3805
+ apiEnabled: false,
3806
+ apiMethods: [],
3807
+ trash: false,
3808
+ mru: false
3809
+ }
3810
+ });
3811
+ var SysOauthClientAssertion = data.ObjectSchema.create({
3812
+ name: "sys_oauth_client_assertion",
3813
+ label: "OAuth Client Assertion",
3814
+ pluralLabel: "OAuth Client Assertions",
3815
+ icon: "shield-check",
3816
+ isSystem: true,
3817
+ managedBy: "better-auth",
3818
+ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3819
+ // but may add overlay row-level config. Use `no-overlay` if you need to
3820
+ // forbid sys_metadata overlays entirely.
3821
+ protection: {
3822
+ lock: "full",
3823
+ reason: "Identity table managed by better-auth \u2014 see ADR-0010.",
3824
+ docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
3825
+ },
3826
+ description: "Consumed OAuth client-assertion JTIs (RFC 7523 replay prevention)",
3827
+ highlightFields: ["expires_at"],
3828
+ fields: {
3829
+ id: data.Field.text({
3830
+ label: "ID",
3831
+ required: true,
3832
+ readonly: true
3833
+ }),
3834
+ expires_at: data.Field.datetime({
3835
+ label: "Expires At",
3836
+ required: true,
3837
+ description: "Assertion expiry \u2014 rows past this instant are safe to prune"
3838
+ })
3839
+ },
3840
+ enable: {
3841
+ trackHistory: false,
3842
+ searchable: false,
3843
+ apiEnabled: false,
3844
+ apiMethods: [],
3845
+ trash: false,
3846
+ mru: false
3847
+ }
3848
+ });
3513
3849
  var SysJwks = data.ObjectSchema.create({
3514
3850
  name: "sys_jwks",
3515
3851
  label: "JWKS Key",
@@ -3551,6 +3887,21 @@ var SysJwks = data.ObjectSchema.create({
3551
3887
  required: true,
3552
3888
  description: "JSON-serialized JWK private key (encrypted at rest)"
3553
3889
  }),
3890
+ // better-auth 1.7 records the key's signing algorithm and (for EdDSA/EC
3891
+ // keys) its curve alongside the key material, so tokens can advertise the
3892
+ // correct `alg`/`crv` in the JWKS response. Both are optional — legacy rows
3893
+ // minted before 1.7 leave them null and better-auth falls back to the
3894
+ // configured `keyPairConfig.alg` (default `EdDSA`).
3895
+ alg: data.Field.text({
3896
+ label: "Algorithm",
3897
+ required: false,
3898
+ description: "JWK signing algorithm, e.g. `EdDSA` (better-auth 1.7+)"
3899
+ }),
3900
+ crv: data.Field.text({
3901
+ label: "Curve",
3902
+ required: false,
3903
+ description: "JWK curve for EdDSA/EC keys, e.g. `Ed25519` (better-auth 1.7+)"
3904
+ }),
3554
3905
  created_at: data.Field.datetime({
3555
3906
  label: "Created At",
3556
3907
  required: true,
@@ -3593,7 +3944,7 @@ var SysSsoProvider = data.ObjectSchema.create({
3593
3944
  // Together: admins see all env providers; non-admins get 403. better-auth's
3594
3945
  // own endpoints already read via a system context. (Env-only object — no
3595
3946
  // control-plane cross-tenant risk.)
3596
- tenancy: { enabled: false, strategy: "shared" },
3947
+ tenancy: { enabled: false },
3597
3948
  requiredPermissions: ["manage_platform_settings"],
3598
3949
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema.
3599
3950
  protection: {
@@ -3935,8 +4286,11 @@ exports.SysJwks = SysJwks;
3935
4286
  exports.SysMember = SysMember;
3936
4287
  exports.SysOauthAccessToken = SysOauthAccessToken;
3937
4288
  exports.SysOauthApplication = SysOauthApplication;
4289
+ exports.SysOauthClientAssertion = SysOauthClientAssertion;
4290
+ exports.SysOauthClientResource = SysOauthClientResource;
3938
4291
  exports.SysOauthConsent = SysOauthConsent;
3939
4292
  exports.SysOauthRefreshToken = SysOauthRefreshToken;
4293
+ exports.SysOauthResource = SysOauthResource;
3940
4294
  exports.SysOrganization = SysOrganization;
3941
4295
  exports.SysScimProvider = SysScimProvider;
3942
4296
  exports.SysSession = SysSession;