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
85 changes: 77 additions & 8 deletions packages/plugins/plugin-auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Authentication & Identity Plugin for ObjectStack.

> **✨ Status:** Better-Auth library successfully integrated! Core authentication structure is in place with better-auth v1.4.18. Full API integration and advanced features are in active development.
> **✨ Status:** ObjectQL-based authentication implementation! Uses ObjectQL for data persistence (no third-party ORM required). Core authentication structure is in place with better-auth v1.4.18.

## Features

Expand All @@ -12,6 +12,7 @@ Authentication & Identity Plugin for ObjectStack.
- ✅ Service registration in ObjectKernel
- ✅ Configuration schema support
- ✅ **Better-Auth library integration (v1.4.18)**
- ✅ **ObjectQL-based database implementation (no ORM required)**
- ✅ **Direct request forwarding to better-auth handler**
- ✅ **Wildcard routing (`/api/v1/auth/*`)**
- ✅ **Full better-auth API access via `auth.api`**
Expand All @@ -28,10 +29,18 @@ Authentication & Identity Plugin for ObjectStack.
- ✅ **Magic Links** - Passwordless authentication (when enabled)
- ✅ **Organizations** - Multi-tenant support (when enabled)

### In Active Development
- 🔄 **Database Adapter** - Drizzle ORM integration for data persistence
### ObjectQL-Based Database Architecture
- ✅ **Native ObjectQL Data Persistence** - Uses ObjectQL's IDataEngine interface
- ✅ **No Third-Party ORM** - No dependency on drizzle-orm or other ORMs
- ✅ **Better-Auth Native Schema** - Uses better-auth's naming conventions for seamless migration
- ✅ **Object Definitions** - Auth objects defined using ObjectStack's Object Protocol
- `user` - User accounts (better-auth native table name)
- `session` - Active sessions (better-auth native table name)
- `account` - OAuth provider accounts (better-auth native table name)
- `verification` - Email/phone verification tokens (better-auth native table name)
- ✅ **ObjectQL Adapter** - Custom adapter bridges better-auth to ObjectQL

The plugin uses [better-auth](https://www.better-auth.com/) for robust, production-ready authentication functionality. All requests are forwarded directly to better-auth's universal handler, ensuring full compatibility with all better-auth features.
The plugin uses [better-auth](https://www.better-auth.com/) for robust, production-ready authentication functionality. All requests are forwarded directly to better-auth's universal handler, ensuring full compatibility with all better-auth features. Data persistence is handled by ObjectQL using **better-auth's native naming conventions** (camelCase) to ensure seamless migration for existing better-auth users.

## Installation

Expand All @@ -41,18 +50,22 @@ pnpm add @objectstack/plugin-auth

## Usage

### Basic Setup
### Basic Setup with ObjectQL

```typescript
import { ObjectKernel } from '@objectstack/core';
import { AuthPlugin } from '@objectstack/plugin-auth';
import { ObjectQL } from '@objectstack/objectql';

// Initialize ObjectQL as the data engine
const dataEngine = new ObjectQL();

const kernel = new ObjectKernel({
plugins: [
new AuthPlugin({
secret: process.env.AUTH_SECRET,
baseUrl: 'http://localhost:3000',
databaseUrl: process.env.DATABASE_URL,
// ObjectQL will be automatically injected by the kernel
providers: [
{
id: 'google',
Expand All @@ -65,13 +78,14 @@ const kernel = new ObjectKernel({
});
```

**Note:** The `databaseUrl` parameter is no longer used. The plugin now uses ObjectQL's IDataEngine interface, which is provided by the kernel's `data` service. This allows the plugin to work with any ObjectQL-compatible driver (memory, SQL, NoSQL, etc.) without requiring a specific ORM.

### With Organization Support

```typescript
new AuthPlugin({
secret: process.env.AUTH_SECRET,
baseUrl: 'http://localhost:3000',
databaseUrl: process.env.DATABASE_URL,
plugins: {
organization: true, // Enable organization/teams
twoFactor: true, // Enable 2FA
Expand Down Expand Up @@ -142,10 +156,12 @@ This package provides authentication services powered by better-auth. Current im
7. ✅ Full better-auth API support
8. ✅ OAuth providers (configurable)
9. ✅ 2FA, passkeys, magic links (configurable)
10. 🔄 Database adapter integration (in progress)
10. ✅ ObjectQL-based database implementation (no ORM required)

### Architecture

#### Request Flow

The plugin uses a **direct forwarding** approach:

```typescript
Expand All @@ -164,6 +180,59 @@ This architecture provides:
- ✅ **Type safety** - Full TypeScript support from better-auth
- ✅ **Programmatic API** - Access auth methods via `authManager.api`

#### ObjectQL Database Architecture

The plugin uses **ObjectQL** for data persistence instead of third-party ORMs:

```typescript
// Object definitions use better-auth's native naming conventions
export const AuthUser = ObjectSchema.create({
name: 'user', // better-auth native table name
fields: {
id: Field.text({ label: 'User ID', required: true }),
email: Field.email({ label: 'Email', required: true }),
emailVerified: Field.boolean({ label: 'Email Verified' }), // camelCase
name: Field.text({ label: 'Name', required: true }),
createdAt: Field.datetime({ label: 'Created At' }), // camelCase
updatedAt: Field.datetime({ label: 'Updated At' }), // camelCase
// ... other fields
},
indexes: [
{ fields: ['email'], unique: true }
]
});
```

**Benefits:**
- ✅ **No ORM Dependencies** - No drizzle-orm, Prisma, or other ORMs required
- ✅ **Unified Data Layer** - Uses same data engine as rest of ObjectStack
- ✅ **Driver Agnostic** - Works with memory, SQL, NoSQL via ObjectQL drivers
- ✅ **Type-Safe** - Zod-based schemas provide runtime + compile-time safety
- ✅ **"Data as Code"** - Object definitions are versioned, declarative code
- ✅ **Metadata Driven** - Supports migrations, validation, indexing via metadata
- ✅ **Seamless Migration** - Uses better-auth's native naming (camelCase) for easy migration

**Database Objects:**
Uses better-auth's native table and field names for compatibility:
- `user` - User accounts (id, email, name, emailVerified, createdAt, etc.)
- `session` - Active sessions (id, token, userId, expiresAt, ipAddress, etc.)
- `account` - OAuth provider accounts (id, providerId, accountId, userId, tokens, etc.)
- `verification` - Verification tokens (id, value, identifier, expiresAt, etc.)

**Adapter:**
The `createObjectQLAdapter()` function bridges better-auth's database interface to ObjectQL's IDataEngine using better-auth's native naming conventions:

```typescript
// Better-auth → ObjectQL Adapter (no name conversion needed)
const adapter = createObjectQLAdapter(dataEngine);

// Better-auth uses this adapter for all database operations
const auth = betterAuth({
database: adapter,
// ... other config
});
```

## Development

```bash
Expand Down
8 changes: 0 additions & 8 deletions packages/plugins/plugin-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,5 @@
"@types/node": "^25.2.2",
"typescript": "^5.0.0",
"vitest": "^4.0.18"
},
"peerDependencies": {
"drizzle-orm": "^0.41.0"
},
"peerDependenciesMeta": {
"drizzle-orm": {
"optional": true
}
}
}
37 changes: 22 additions & 15 deletions packages/plugins/plugin-auth/src/auth-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { betterAuth } from 'better-auth';
import type { Auth, BetterAuthOptions } from 'better-auth';
import type { AuthConfig } from '@objectstack/spec/system';
import type { IDataEngine } from '@objectstack/core';
import { createObjectQLAdapter } from './objectql-adapter.js';

/**
* Extended options for AuthManager
Expand All @@ -13,6 +15,12 @@ export interface AuthManagerOptions extends Partial<AuthConfig> {
* If not provided, one will be created from config
*/
authInstance?: Auth<any>;

/**
* ObjectQL Data Engine instance
* Required for database operations using ObjectQL instead of third-party ORMs
*/
dataEngine?: IDataEngine;
}

/**
Expand Down Expand Up @@ -82,25 +90,24 @@ export class AuthManager {
}

/**
* Create database configuration
* TODO: Implement proper database adapter when drizzle-orm is available
* Create database configuration using ObjectQL adapter
*/
private createDatabaseConfig(): any {
// If databaseUrl is provided, we would use drizzle adapter
// For now, this is a placeholder configuration
if (this.config.databaseUrl) {
console.warn(
'Database URL provided but adapter integration not yet complete. ' +
'Install drizzle-orm and configure a proper adapter for production use.'
);
// Use ObjectQL adapter if dataEngine is provided
if (this.config.dataEngine) {
return createObjectQLAdapter(this.config.dataEngine);
}

// Return a minimal configuration that better-auth can work with
// This will need to be replaced with a proper adapter
return {
// Placeholder - will be replaced with actual adapter
adapter: 'in-memory' as any,
};
// Fallback warning if no dataEngine is provided
console.warn(
'⚠️ WARNING: No dataEngine provided to AuthManager! ' +
'Using in-memory storage. This is NOT suitable for production. ' +
'Please provide a dataEngine instance (e.g., ObjectQL) in AuthManagerOptions.'
);

// Return a minimal in-memory configuration as fallback
// This allows the system to work in development/testing without a real database
return undefined; // better-auth will use its default in-memory adapter
}

/**
Expand Down
13 changes: 11 additions & 2 deletions packages/plugins/plugin-auth/src/auth-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,17 @@ export class AuthPlugin implements Plugin {
throw new Error('AuthPlugin: secret is required');
}

// Initialize auth manager
this.authManager = new AuthManager(this.options);
// Get data engine service for database operations
const dataEngine = ctx.getService<any>('data');
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx.getService('data') throws when the service is not registered (KernelBase.getService throws on missing services), so this init path will crash instead of falling back to in-memory as the log message suggests. Wrap the lookup in try/catch (or use a non-throwing service existence check) and only warn when the service is absent.

Suggested change
const dataEngine = ctx.getService<any>('data');
let dataEngine: any | undefined;
try {
dataEngine = ctx.getService<any>('data');
} catch {
ctx.logger.warn('No data engine service found - auth will use in-memory storage');
}

Copilot uses AI. Check for mistakes.
if (!dataEngine) {
ctx.logger.warn('No data engine service found - auth will use in-memory storage');
}

// Initialize auth manager with data engine
this.authManager = new AuthManager({
...this.options,
dataEngine,
});
Comment on lines +70 to +80
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new behavior (injecting dataEngine and supporting a missing data service) but there are no assertions covering it in the existing plugin test suite. Add tests that verify: (1) init does not throw when data is absent, and (2) when data is present it is passed into AuthManager.

Copilot uses AI. Check for mistakes.

// Register auth service
ctx.registerService('auth', this.authManager);
Expand Down
7 changes: 5 additions & 2 deletions packages/plugins/plugin-auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
*
* Authentication & Identity Plugin for ObjectStack
* Powered by better-auth for robust, secure authentication
* Uses ObjectQL for data persistence (no third-party ORM required)
*/

export * from './auth-plugin';
export * from './auth-manager';
export * from './auth-plugin.js';
export * from './auth-manager.js';
export * from './objectql-adapter.js';
export * from './objects/index.js';
export type { AuthConfig, AuthProviderConfig, AuthPluginConfig } from '@objectstack/spec/system';
Loading
Loading