Skip to content

Commit 86da4f3

Browse files
Copilothotlong
andcommitted
Implement @objectstack/plugin-auth with Better-Auth integration
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 27f9c45 commit 86da4f3

12 files changed

Lines changed: 1088 additions & 1 deletion

File tree

.env.example

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Better-Auth Configuration
2+
# Required: Secret key for signing tokens (minimum 32 characters)
3+
BETTER_AUTH_SECRET=your-super-secret-key-change-this-in-production-min-32-chars
4+
5+
# Required: Base URL where your application is hosted
6+
BETTER_AUTH_URL=http://localhost:3000
7+
8+
# Optional: Node environment
9+
NODE_ENV=development
10+
11+
# Optional: Google OAuth (if using social auth)
12+
GOOGLE_CLIENT_ID=your-google-client-id
13+
GOOGLE_CLIENT_SECRET=your-google-client-secret
14+
15+
# Optional: GitHub OAuth (if using social auth)
16+
GITHUB_CLIENT_ID=your-github-client-id
17+
GITHUB_CLIENT_SECRET=your-github-client-secret
18+
19+
# Optional: Database connection (for ObjectQL)
20+
DATABASE_URL=postgresql://user:password@localhost:5432/dbname

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
dist
3+
*.log
4+
.DS_Store
5+
.env
6+
.env.local
7+
*.tsbuildinfo

README.md

Lines changed: 262 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,262 @@
1-
# plugin-auth
1+
# @objectstack/plugin-auth
2+
3+
🔐 Better-Auth plugin for ObjectStack - A battery-included authentication and identity layer for the ObjectStack ecosystem.
4+
5+
## Features
6+
7+
- **Storage Agnostic**: Uses ObjectQL as the storage adapter, meaning authentication data can live in Postgres, Redis, or even Excel files
8+
- **Type Safe**: End-to-end typed session objects with TypeScript
9+
- **RBAC Integration**: Automatic permission injection from ObjectOS into session objects
10+
- **React Hooks**: Pre-built hooks for easy client-side integration
11+
- **Framework Agnostic**: Works with any JavaScript framework
12+
13+
## Installation
14+
15+
```bash
16+
npm install @objectstack/plugin-auth better-auth
17+
```
18+
19+
## Quick Start
20+
21+
### Server Setup
22+
23+
```typescript
24+
import { createAuthPlugin } from '@objectstack/plugin-auth';
25+
import { createQLClient } from '@objectstack/ql';
26+
27+
// Initialize ObjectQL client
28+
const ql = createQLClient({
29+
driver: 'postgres', // or 'redis', 'excel', etc.
30+
connection: process.env.DATABASE_URL,
31+
});
32+
33+
// Create auth plugin
34+
const authPlugin = createAuthPlugin({
35+
secret: process.env.BETTER_AUTH_SECRET,
36+
baseURL: process.env.BETTER_AUTH_URL,
37+
38+
// RBAC Integration: Inject permissions into session
39+
onGetPermissions: async (userId) => {
40+
return await os.getPermissions(userId);
41+
},
42+
43+
// Optional: Configure email/password auth
44+
emailProvider: {
45+
enabled: true,
46+
sendVerificationEmail: async ({ user, url }) => {
47+
// Send email logic
48+
},
49+
},
50+
51+
// Optional: Configure social providers
52+
socialProviders: [
53+
{
54+
id: 'google',
55+
clientId: process.env.GOOGLE_CLIENT_ID,
56+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
57+
},
58+
],
59+
});
60+
61+
// Enable the plugin
62+
await authPlugin.onEnable({
63+
ql,
64+
app: yourExpressApp, // or any compatible server
65+
});
66+
```
67+
68+
### Client Setup (React)
69+
70+
```typescript
71+
import { useSession, usePermissions, signIn, signOut } from '@objectstack/plugin-auth';
72+
73+
function App() {
74+
const { data: session, isPending } = useSession();
75+
const { permissions } = usePermissions();
76+
77+
if (isPending) return <div>Loading...</div>;
78+
79+
if (!session) {
80+
return (
81+
<button onClick={() => signIn.email({
82+
email: 'user@example.com',
83+
password: 'password'
84+
})}>
85+
Sign In
86+
</button>
87+
);
88+
}
89+
90+
return (
91+
<div>
92+
<p>Welcome, {session.user.name}!</p>
93+
<p>Email: {session.user.email}</p>
94+
<p>Permissions: {permissions?.join(', ')}</p>
95+
<button onClick={() => signOut()}>Sign Out</button>
96+
</div>
97+
);
98+
}
99+
```
100+
101+
## Architecture
102+
103+
### ObjectQL Adapter
104+
105+
The plugin implements a custom Better-Auth adapter that maps all CRUD operations to ObjectQL entities:
106+
107+
```typescript
108+
// Example: Creating a user
109+
const user = await ql.entity('User').create({
110+
data: {
111+
email: 'user@example.com',
112+
name: 'John Doe',
113+
},
114+
});
115+
```
116+
117+
This means if ObjectQL is configured with an Excel driver, new users will be written as rows in an Excel file!
118+
119+
### Schema Definition
120+
121+
The plugin defines authentication entities in GraphQL:
122+
123+
```graphql
124+
type User {
125+
id: ID!
126+
email: String!
127+
emailVerified: Boolean!
128+
name: String
129+
image: String
130+
createdAt: DateTime!
131+
updatedAt: DateTime!
132+
}
133+
134+
type Session {
135+
id: ID!
136+
userId: ID!
137+
expiresAt: DateTime!
138+
token: String!
139+
# ... more fields
140+
}
141+
```
142+
143+
### RBAC Integration
144+
145+
The plugin bridges Better-Auth (authentication) with ObjectOS (authorization):
146+
147+
```typescript
148+
// In server config
149+
onGetPermissions: async (userId) => {
150+
// Query ObjectOS for permissions
151+
return await os.getPermissions(userId);
152+
}
153+
154+
// In client
155+
const { permissions } = usePermissions();
156+
// permissions are automatically available in session
157+
```
158+
159+
## API Reference
160+
161+
### Server API
162+
163+
#### `createAuthPlugin(config)`
164+
165+
Creates the auth plugin instance.
166+
167+
**Options:**
168+
- `secret` - Secret key for signing tokens
169+
- `baseURL` - Base URL for auth endpoints
170+
- `trustedOrigins` - Array of trusted origins for CORS
171+
- `emailProvider` - Email provider configuration
172+
- `socialProviders` - Array of social provider configs
173+
- `onGetPermissions` - Callback to get user permissions
174+
175+
#### `createAuthServer(config)`
176+
177+
Low-level API to create Better-Auth server instance.
178+
179+
#### `getSession(auth, request)`
180+
181+
Extract session from HTTP request.
182+
183+
### Client API
184+
185+
#### `useSession()`
186+
187+
React hook to get current session.
188+
189+
```typescript
190+
const { data: session, isPending, error } = useSession();
191+
```
192+
193+
#### `usePermissions()`
194+
195+
React hook to get user permissions.
196+
197+
```typescript
198+
const { permissions, isPending, isAuthenticated } = usePermissions();
199+
```
200+
201+
#### `useHasPermission(permission)`
202+
203+
Check if user has a specific permission.
204+
205+
```typescript
206+
const { hasPermission, isPending } = useHasPermission('admin.write');
207+
```
208+
209+
#### `signIn`
210+
211+
Sign in methods for different providers.
212+
213+
```typescript
214+
// Email/password
215+
await signIn.email({ email, password });
216+
217+
// Social
218+
await signIn.social({ provider: 'google' });
219+
```
220+
221+
#### `signOut`
222+
223+
Sign out the current user.
224+
225+
```typescript
226+
await signOut();
227+
```
228+
229+
## Environment Variables
230+
231+
```bash
232+
BETTER_AUTH_SECRET=your-secret-key-min-32-chars
233+
BETTER_AUTH_URL=http://localhost:3000
234+
NODE_ENV=development
235+
```
236+
237+
## Directory Structure
238+
239+
```
240+
src/
241+
├── adapter/
242+
│ └── index.ts # ObjectQL Adapter implementation
243+
├── schema/
244+
│ └── auth.gql # GraphQL schema definitions
245+
├── client/
246+
│ └── hooks.ts # React hooks
247+
├── server/
248+
│ └── index.ts # Server-side initialization
249+
└── index.ts # Main entry point
250+
```
251+
252+
## License
253+
254+
MIT
255+
256+
## Contributing
257+
258+
Contributions are welcome! Please read our contributing guidelines first.
259+
260+
## Support
261+
262+
For issues and questions, please open an issue on GitHub.

example.config.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Example configuration for @objectstack/plugin-auth
3+
*
4+
* Copy this file to your project and customize as needed
5+
*/
6+
7+
import { createAuthPlugin } from '@objectstack/plugin-auth';
8+
import type { AuthPluginConfig } from '@objectstack/plugin-auth';
9+
10+
// Example: Full configuration with all options
11+
export const authConfig: AuthPluginConfig = {
12+
// Required: Secret for signing tokens (min 32 characters)
13+
secret: process.env.BETTER_AUTH_SECRET,
14+
15+
// Required: Base URL where your app is hosted
16+
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:3000',
17+
18+
// Optional: Additional trusted origins for CORS
19+
trustedOrigins: [
20+
'http://localhost:5173', // Vite dev server
21+
'https://yourdomain.com',
22+
],
23+
24+
// Optional: Email/password authentication
25+
emailProvider: {
26+
enabled: true,
27+
28+
// Implement your email sending logic
29+
sendVerificationEmail: async ({ user, url }) => {
30+
console.log(`Send verification email to ${user.email}: ${url}`);
31+
// Example: await sendEmail(user.email, 'Verify Email', url);
32+
},
33+
34+
// Implement password reset email
35+
sendResetPasswordEmail: async ({ user, url }) => {
36+
console.log(`Send password reset to ${user.email}: ${url}`);
37+
// Example: await sendEmail(user.email, 'Reset Password', url);
38+
},
39+
},
40+
41+
// Optional: Social authentication providers
42+
socialProviders: [
43+
// Google OAuth
44+
{
45+
id: 'google',
46+
clientId: process.env.GOOGLE_CLIENT_ID!,
47+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
48+
},
49+
50+
// GitHub OAuth
51+
{
52+
id: 'github',
53+
clientId: process.env.GITHUB_CLIENT_ID!,
54+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
55+
},
56+
],
57+
58+
// Optional: RBAC Integration
59+
// Inject ObjectOS permissions into session
60+
onGetPermissions: async (userId: string) => {
61+
// Example: Query your permission system
62+
// const permissions = await os.getPermissions(userId);
63+
// return permissions;
64+
65+
// For now, return mock permissions
66+
return ['user.read', 'user.write'];
67+
},
68+
};
69+
70+
// Example: Minimal configuration
71+
export const minimalAuthConfig: AuthPluginConfig = {
72+
secret: process.env.BETTER_AUTH_SECRET,
73+
baseURL: process.env.BETTER_AUTH_URL,
74+
};
75+
76+
// Usage in your app:
77+
// const authPlugin = createAuthPlugin(authConfig);
78+
// await authPlugin.onEnable({ ql, app });

objectstack.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { ObjectStackConfig } from '@objectstack/protocol';
2+
3+
const config: ObjectStackConfig = {
4+
type: 'plugin',
5+
name: '@objectstack/plugin-auth',
6+
version: '0.1.0',
7+
description: 'Better-Auth authentication plugin for ObjectStack',
8+
entities: ['./src/schema/*.gql'],
9+
dependencies: {
10+
'@objectstack/ql': '*',
11+
'@objectstack/protocol': '*',
12+
'better-auth': '^1.0.0'
13+
}
14+
};
15+
16+
export default config;

0 commit comments

Comments
 (0)