feat(plugin-auth): use better-auth modelName/fields mapping for snake_case compatibility#897
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…_case compatibility - Add auth-schema-config.ts with model/field mapping constants for all 4 core auth models - Refactor objectql-adapter.ts to use createAdapterFactory from better-auth/adapters - Update auth-manager.ts to pass user/session/account/verification config with modelName and fields - Add tests for schema config, factory adapter, and config verification - Update README and authentication guide documentation - Keep legacy createObjectQLAdapter for backward compatibility Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
… ROADMAP Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot 仔细评估是否完成了任务中要求的所有开放 |
There was a problem hiding this comment.
Pull request overview
This PR updates @objectstack/plugin-auth to use better-auth’s official modelName / fields mapping along with createAdapterFactory, enabling automatic camelCase ↔ snake_case transformations so ObjectQL queries match ObjectStack’s snake_case auth objects.
Changes:
- Added
AUTH_*_CONFIGconstants declaring model/field mappings for user/session/account/verification. - Introduced
createObjectQLAdapterFactory()usingcreateAdapterFactoryand updatedAuthManagerto pass mapping config intobetterAuth(). - Updated tests and documentation to reflect the new mapping approach while retaining the legacy
createObjectQLAdapter()for backward compatibility.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/plugins/plugin-auth/src/objectql-adapter.ts | Adds factory-based adapter using createAdapterFactory; keeps legacy adapter. |
| packages/plugins/plugin-auth/src/objectql-adapter.test.ts | Adds assertions for mapping constants and minimal factory test. |
| packages/plugins/plugin-auth/src/index.ts | Exports new schema-config module from the package entrypoint. |
| packages/plugins/plugin-auth/src/auth-schema-config.ts | Introduces AUTH_*_CONFIG constants for modelName/fields mapping. |
| packages/plugins/plugin-auth/src/auth-manager.ts | Applies mapping constants to betterAuth() config and uses new adapter factory. |
| packages/plugins/plugin-auth/src/auth-manager.test.ts | Updates tests to validate mapping propagation into betterAuth() config. |
| packages/plugins/plugin-auth/README.md | Documents model/field mapping approach and adapter factory usage. |
| content/docs/guides/authentication.mdx | Updates guide to explain modelName/fields mapping and adapter factory behavior. |
| ROADMAP.md | Notes enhancement in migration section for the new mapping approach. |
| ```typescript | ||
| // Schema mapping constants (auth-schema-config.ts) | ||
| import { | ||
| AUTH_USER_CONFIG, | ||
| AUTH_SESSION_CONFIG, | ||
| AUTH_ACCOUNT_CONFIG, | ||
| AUTH_VERIFICATION_CONFIG, | ||
| } from '@objectstack/plugin-auth'; | ||
|
|
||
| // Applied to the betterAuth() config: | ||
| const auth = betterAuth({ | ||
| database: (options) => ({ | ||
| id: 'objectql', | ||
| ...adapter, | ||
| transaction: async (cb) => cb(adapter), | ||
| }), | ||
| // ... other config | ||
| database: createObjectQLAdapterFactory(dataEngine), | ||
| user: { ...AUTH_USER_CONFIG }, | ||
| session: { ...AUTH_SESSION_CONFIG, expiresIn: 604800 }, |
There was a problem hiding this comment.
In the README example, createObjectQLAdapterFactory(dataEngine) is used but not imported. This snippet won’t compile as-is; include createObjectQLAdapterFactory in the import list (or show a separate import) so readers can copy/paste successfully.
| better-auth internally uses camelCase model and field names (`user`, `emailVerified`, `userId`). | ||
| The plugin bridges this gap using better-auth's official **`modelName` / `fields` schema customisation API**: |
There was a problem hiding this comment.
This sentence says better-auth uses “camelCase model and field names”, but the model names shown (user, session, etc.) are not camelCase—only the field names are. Adjust wording to avoid confusion (e.g., “better-auth uses fixed model names like user and camelCase field names like emailVerified / userId”).
| describe('createObjectQLAdapterFactory', () => { | ||
| it('should return a function (adapter factory)', () => { | ||
| const mockEngine = { | ||
| insert: vi.fn(), | ||
| findOne: vi.fn(), | ||
| find: vi.fn(), | ||
| count: vi.fn(), | ||
| update: vi.fn(), | ||
| delete: vi.fn(), | ||
| } as unknown as IDataEngine; | ||
|
|
||
| const factory = createObjectQLAdapterFactory(mockEngine); | ||
| expect(typeof factory).toBe('function'); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
createObjectQLAdapterFactory is only tested for returning a function. Since this PR relies on createAdapterFactory for correct modelName/fields transformations, it would be valuable to add a test that invokes the returned factory with a minimal BetterAuthOptions-like object and asserts that adapter calls into dataEngine with the expected transformed model name and snake_case fields/where-clause keys.
…agicLink plugins - Add AUTH_ORGANIZATION_SCHEMA, AUTH_MEMBER_SCHEMA, AUTH_INVITATION_SCHEMA, AUTH_TEAM_SCHEMA, AUTH_TEAM_MEMBER_SCHEMA for organization plugin tables - Add AUTH_TWO_FACTOR_SCHEMA and AUTH_TWO_FACTOR_USER_FIELDS for 2FA plugin - Add AUTH_ORG_SESSION_FIELDS for org plugin session extensions - Add buildOrganizationPluginSchema() and buildTwoFactorPluginSchema() helpers - Wire up plugin registration in AuthManager.buildPluginList() based on AuthPluginConfig flags (organization, twoFactor, magicLink) - Add 14 new tests for plugin schema configs and plugin registration - Update README documentation with plugin table mappings Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Evaluated all open tasks from the issue. The original implementation only covered the 4 core models (user, session, account, verification). Per the issue requirement "需要考虑所有插件用到的表,包括org等", commit Organization plugin tables:
Two-Factor plugin:
Magic Link plugin:
Wiring:
All 53 tests pass, build succeeds, CodeQL clean. |
better-auth sends camelCase field names (
emailVerified,userId,createdAt) to the adapter, but ObjectStack's protocol layer expects snake_case (email_verified,user_id,created_at). This causes query failures on sign-in, sign-up, and account lookup.Approach
Leverage better-auth's official
modelName/fieldsschema customization API combined withcreateAdapterFactoryfrombetter-auth/adapters. The factory wrapper handles all camelCase ↔ snake_case transformation automatically — no manual conversion in the adapter.Changes
auth-schema-config.ts— Mapping constants for all 4 core models (AUTH_USER_CONFIG,AUTH_SESSION_CONFIG,AUTH_ACCOUNT_CONFIG,AUTH_VERIFICATION_CONFIG) plus 7 plugin table schemas (AUTH_ORGANIZATION_SCHEMA,AUTH_MEMBER_SCHEMA,AUTH_INVITATION_SCHEMA,AUTH_TEAM_SCHEMA,AUTH_TEAM_MEMBER_SCHEMA,AUTH_TWO_FACTOR_SCHEMA,AUTH_ORG_SESSION_FIELDS) with helper buildersbuildOrganizationPluginSchema()andbuildTwoFactorPluginSchema()objectql-adapter.ts— NewcreateObjectQLAdapterFactory()usingcreateAdapterFactory; legacycreateObjectQLAdapter()retained for backward compatauth-manager.ts—createAuthInstance()now passes model/field config tobetterAuth();createDatabaseConfig()uses the new factory; newbuildPluginList()conditionally registers organization, twoFactor, and magicLink plugins based onAuthPluginConfigflags, each with its own snake_case schema mappingPlugin Table Coverage
In addition to the 4 core models, all plugin tables are mapped (per issue requirement "需要考虑所有插件用到的表,包括org等"):
sys_organization,sys_member,sys_invitation,sys_team,sys_team_memberorganizationId→organization_id,inviterId→inviter_id,activeOrganizationId→active_organization_id, etc.sys_two_factor+ user field extensionbackupCodes→backup_codes,twoFactorEnabled→two_factor_enabledverificationtable)Example
The factory reads the schema built by
getAuthTables(options)and appliestransformInput/transformOutput/transformWhereClausebefore data reaches the ObjectQL adapter — single-point mapping, no runtime conversion in adapter code.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.