Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .changeset/oauth-provider-17-schema-drift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@objectstack/platform-objects': minor
'@objectstack/plugin-auth': patch
'@objectstack/spec': patch
---

Close the `@better-auth/oauth-provider` 1.7 schema drift that broke platform
SSO (token exchange 500: `table sys_oauth_access_token has no column named
authorizationCodeId`).

- `sys_oauth_access_token` / `sys_oauth_refresh_token`: add
`authorization_code_id`, `resources`, `requested_user_info_claims`,
`confirmation` (+ access-token `revoked`; + refresh-token `rotated_at`,
`rotation_replay_response`, `rotation_replay_expires_at`).
- `sys_oauth_consent`: add `resources`, `requested_user_info_claims`.
- `sys_oauth_application`: add `jwks`, `jwks_uri`, `backchannel_logout_uri`,
`backchannel_logout_session_required`, `dpop_bound_access_tokens`.
- New platform objects for the three models 1.7 introduced:
`sys_oauth_resource`, `sys_oauth_client_resource`,
`sys_oauth_client_assertion` (RFC 8707 resource indicators + RFC 7523
client-assertion replay prevention), registered in the auth manifest and
mapped in `buildOauthProviderPluginSchema()`.
- All camelCase→snake_case `fieldName` mappings extended accordingly, and a
new parity test (`oauth-provider-schema-parity.test.ts`) fails the build
whenever a future better-auth bump introduces model fields our objects or
mappings don't cover.
6 changes: 6 additions & 0 deletions packages/platform-objects/scripts/i18n-extract.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ import {
SysOauthAccessToken,
SysOauthRefreshToken,
SysOauthConsent,
SysOauthResource,
SysOauthClientResource,
SysOauthClientAssertion,
SysJwks,
} from '../src/identity/index.js';

Expand Down Expand Up @@ -144,6 +147,9 @@ export default defineStack({
SysOauthAccessToken,
SysOauthRefreshToken,
SysOauthConsent,
SysOauthResource,
SysOauthClientResource,
SysOauthClientAssertion,
SysJwks,

// Security: RBAC moved to @objectstack/plugin-security, sharing to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const OWNED_OBJECTS = new Set([
'sys_member', 'sys_invitation', 'sys_team', 'sys_team_member', 'sys_business_unit',
'sys_business_unit_member', 'sys_api_key', 'sys_two_factor', 'sys_device_code',
'sys_user_preference', 'sys_oauth_application', 'sys_oauth_access_token',
'sys_oauth_refresh_token', 'sys_oauth_consent', 'sys_jwks',
'sys_oauth_refresh_token', 'sys_oauth_consent', 'sys_oauth_resource',
'sys_oauth_client_resource', 'sys_oauth_client_assertion', 'sys_jwks',
// audit / messaging-adjacent (still owned here)
'sys_notification', 'sys_attachment', 'sys_email', 'sys_email_template',
'sys_saved_report', 'sys_report_schedule', 'sys_job', 'sys_job_run', 'sys_job_queue',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,18 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Subject Type",
help: "OIDC subject type (e.g. public, pairwise)"
},
jwks: {
label: "JWKS",
help: "Client JSON Web Key Set (for private_key_jwt / signed-request verification)"
},
jwks_uri: {
label: "JWKS URI",
help: "URL of the client JSON Web Key Set"
},
dpop_bound_access_tokens: {
label: "DPoP-bound Access Tokens",
help: "Require access tokens issued to this client to be DPoP-bound (RFC 9449)"
},
disabled: {
label: "Disabled"
},
Expand All @@ -1198,6 +1210,14 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Enable End Session",
help: "Allow the client to call the OIDC end-session endpoint"
},
backchannel_logout_uri: {
label: "Back-channel Logout URI",
help: "OIDC back-channel logout endpoint of the client"
},
backchannel_logout_session_required: {
label: "Back-channel Logout Session Required",
help: "Whether the back-channel logout token must include a sid claim"
},
software_id: {
label: "Software ID"
},
Expand Down Expand Up @@ -1312,6 +1332,18 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Reference ID",
help: "Caller-supplied correlation identifier"
},
authorization_code_id: {
label: "Authorization Code ID",
help: "ID of the authorization-code grant this token originates from"
},
resources: {
label: "Resources",
help: "JSON-serialized list of RFC 8707 resource indicators bound to this token"
},
requested_user_info_claims: {
label: "Requested UserInfo Claims",
help: "JSON-serialized list of OIDC claims requested for the userinfo endpoint"
},
scopes: {
label: "Scopes",
help: "JSON-serialized list of scopes granted to this token"
Expand All @@ -1321,6 +1353,14 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
},
created_at: {
label: "Created At"
},
revoked: {
label: "Revoked At",
help: "Timestamp at which this access token was revoked"
},
confirmation: {
label: "Confirmation",
help: "JSON RFC 7800 cnf claim (e.g. DPoP key thumbprint) binding this token to a key"
}
}
},
Expand Down Expand Up @@ -1352,6 +1392,18 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Reference ID",
help: "Caller-supplied correlation identifier"
},
authorization_code_id: {
label: "Authorization Code ID",
help: "ID of the authorization-code grant this token chain originates from"
},
resources: {
label: "Resources",
help: "JSON-serialized list of RFC 8707 resource indicators bound to this token"
},
requested_user_info_claims: {
label: "Requested UserInfo Claims",
help: "JSON-serialized list of OIDC claims requested for the userinfo endpoint"
},
scopes: {
label: "Scopes",
help: "JSON-serialized list of scopes granted to this token"
Expand All @@ -1366,9 +1418,25 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Revoked At",
help: "Timestamp at which this refresh token was revoked"
},
rotated_at: {
label: "Rotated At",
help: "Timestamp at which this token was rotated (superseded by a new row)"
},
rotation_replay_response: {
label: "Rotation Replay Response",
help: "Cached token response replayed when the old token is re-presented within the reuse interval"
},
rotation_replay_expires_at: {
label: "Rotation Replay Expires At",
help: "End of the post-rotation reuse interval during which the replay response is served"
},
auth_time: {
label: "Auth Time",
help: "When the user originally authenticated for this token chain"
},
confirmation: {
label: "Confirmation",
help: "JSON RFC 7800 cnf claim (e.g. DPoP key thumbprint) binding this token to a key"
}
}
},
Expand All @@ -1392,6 +1460,14 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
label: "Reference ID",
help: "Caller-supplied correlation identifier"
},
resources: {
label: "Resources",
help: "JSON-serialized list of RFC 8707 resource indicators the consent covers"
},
requested_user_info_claims: {
label: "Requested UserInfo Claims",
help: "JSON-serialized list of OIDC claims the user consented to expose"
},
scopes: {
label: "Scopes",
help: "JSON-serialized list of scopes the user consented to"
Expand All @@ -1404,6 +1480,107 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
}
}
},
sys_oauth_resource: {
label: "OAuth Resource",
pluralLabel: "OAuth Resources",
description: "Registered OAuth protected resources (RFC 8707 resource indicators)",
fields: {
id: {
label: "ID"
},
identifier: {
label: "Identifier",
help: "Resource indicator URI presented in the RFC 8707 resource parameter"
},
name: {
label: "Name"
},
access_token_ttl: {
label: "Access Token TTL",
help: "Access-token lifetime in seconds for this resource (overrides the server default)"
},
refresh_token_ttl: {
label: "Refresh Token TTL",
help: "Refresh-token lifetime in seconds for this resource (overrides the server default)"
},
signing_algorithm: {
label: "Signing Algorithm",
help: "JWS algorithm used to sign access tokens for this resource"
},
signing_key_id: {
label: "Signing Key ID",
help: "Key id (kid) used to sign access tokens for this resource"
},
allowed_scopes: {
label: "Allowed Scopes",
help: "JSON-serialized list of scopes clients may request for this resource"
},
custom_claims: {
label: "Custom Claims",
help: "JSON object of extra claims stamped on access tokens for this resource"
},
dpop_bound_access_tokens_required: {
label: "DPoP Required",
help: "Require access tokens for this resource to be DPoP-bound (RFC 9449)"
},
disabled: {
label: "Disabled"
},
policy_version: {
label: "Policy Version",
help: "Monotonic version of the resource token policy"
},
metadata: {
label: "Metadata",
help: "JSON object of additional resource metadata"
},
created_at: {
label: "Created At"
},
updated_at: {
label: "Updated At"
}
}
},
sys_oauth_client_resource: {
label: "OAuth Client Resource",
pluralLabel: "OAuth Client Resources",
description: "Grants allowing an OAuth client to request tokens for a protected resource",
fields: {
id: {
label: "ID"
},
client_id: {
label: "Client ID",
help: "Foreign key to sys_oauth_application.client_id"
},
resource_id: {
label: "Resource ID",
help: "Foreign key to sys_oauth_resource.identifier"
},
metadata: {
label: "Metadata",
help: "JSON object of additional grant metadata"
},
created_at: {
label: "Created At"
}
}
},
sys_oauth_client_assertion: {
label: "OAuth Client Assertion",
pluralLabel: "OAuth Client Assertions",
description: "Consumed OAuth client-assertion JTIs (RFC 7523 replay prevention)",
fields: {
id: {
label: "ID"
},
expires_at: {
label: "Expires At",
help: "Assertion expiry — rows past this instant are safe to prune"
}
}
},
sys_jwks: {
label: "JWKS Key",
pluralLabel: "JWKS Keys",
Expand Down
Loading
Loading