Skip to content
Draft
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
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Dependencies
node_modules/
package-lock.json
yarn.lock
pnpm-lock.yaml

# Build outputs
dist/
*.tsbuildinfo

# Environment variables
.env
.env.local
.env.*.local

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Testing
coverage/
.nyc_output/

# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,71 @@
# plugin-auth
# @objectstack/plugin-auth

Authentication and identity layer for ObjectStack, wrapping Better-Auth with ObjectQL storage adapter.

## Overview

This plugin provides a **battery-included** authentication experience for the ObjectStack ecosystem by:

- 🔐 Wrapping [Better-Auth](https://better-auth.com) for robust authentication
- 💾 Using ObjectQL as the storage adapter (works with Postgres, Redis, Excel, etc.)
- 🔒 Providing end-to-end type-safe session objects
- 🎯 Framework agnostic design

## Features

- **Storage Agnostic**: Authentication data can live in any storage backend supported by ObjectQL
- **Type Safety**: Leverages Better-Auth's inference for type-safe session management
- **OAuth Support**: Built-in support for multiple OAuth providers
- **RBAC Integration**: Seamless integration with ObjectOS permissions

## Installation

```bash
npm install @objectstack/plugin-auth
```

## Project Structure

```
src/
├── adapter/ # ObjectQL adapter for Better-Auth
├── schema/ # GraphQL schema definitions (User, Session, etc.)
├── client/ # React hooks and components
├── server/ # Server-side initialization logic
└── index.ts # Main plugin entry point
```

## Schema

The plugin defines the following entities compatible with Better-Auth:

- **User**: Core user entity with email, name, and profile information
- **Session**: Active user sessions with token and expiration
- **Account**: OAuth provider accounts linked to users
- **VerificationToken**: Email verification and password reset tokens

See `src/schema/auth.gql` for the complete schema definition.

## Development

```bash
# Install dependencies
npm install

# Build the plugin
npm run build

# Watch mode for development
npm run dev
```

## Configuration

The plugin is configured via `objectstack.config.ts` and requires the following environment variables:

- `BETTER_AUTH_SECRET`: Secret key for Better-Auth
- `BETTER_AUTH_URL`: Base URL for authentication endpoints

## License

MIT
14 changes: 14 additions & 0 deletions objectstack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from '@objectstack/protocol';

/**
* ObjectStack Plugin Configuration for @objectstack/plugin-auth
*
* This configuration defines the authentication plugin as an ObjectStack plugin
* and registers the GraphQL schema entities required for Better-Auth integration.
*/
export default defineConfig({
type: 'plugin',
name: '@objectstack/plugin-auth',
entities: ['./src/schema/*.gql'],
version: '0.1.0',
});
57 changes: 57 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@objectstack/plugin-auth",
"version": "0.1.0",
"description": "Authentication and identity layer for ObjectStack, wrapping Better-Auth with ObjectQL storage adapter",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./client": {
"import": "./dist/client/index.js",
"types": "./dist/client/index.d.ts"
},
"./server": {
"import": "./dist/server/index.js",
"types": "./dist/server/index.d.ts"
}
},
"files": [
"dist",
"src"
],
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"clean": "rm -rf dist"
},
"keywords": [
"objectstack",
"authentication",
"auth",
"better-auth",
"objectql",
"plugin"
],
"author": "ObjectStack",
"license": "MIT",
"peerDependencies": {
"@objectstack/protocol": "*",
"@objectstack/ql": "*",
"react": "^18.0.0"
},
"dependencies": {
"better-auth": "^1.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/react": "^18.0.0",
"typescript": "^5.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}
143 changes: 143 additions & 0 deletions src/schema/auth.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Authentication Schema for ObjectStack
# Compatible with Better-Auth requirements

"""
User entity representing authenticated users in the system.
Maps to Better-Auth's User table structure.
"""
type User @entity {
"""Unique identifier for the user"""
id: ID! @primary

"""User's email address"""
email: String! @unique

"""Whether the user's email has been verified"""
emailVerified: Boolean! @default(value: false)

"""User's display name"""
name: String

"""URL to user's profile image"""
image: String

"""Timestamp when the user was created"""
createdAt: DateTime! @default(value: "now")

"""Timestamp when the user was last updated"""
updatedAt: DateTime! @updatedAt

"""Sessions associated with this user"""
sessions: [Session!]! @relation(name: "UserSessions")

"""Accounts associated with this user (OAuth providers)"""
accounts: [Account!]! @relation(name: "UserAccounts")
}

"""
Session entity representing active user sessions.
Maps to Better-Auth's Session table structure.
"""
type Session @entity {
"""Unique identifier for the session"""
id: ID! @primary

"""Reference to the user who owns this session"""
userId: String! @index

"""When this session expires"""
expiresAt: DateTime!

"""Session token for authentication"""
token: String! @unique

"""IP address from which the session was created"""
ipAddress: String

"""User agent string of the client"""
userAgent: String

"""Timestamp when the session was created"""
createdAt: DateTime! @default(value: "now")

"""Timestamp when the session was last updated"""
updatedAt: DateTime! @updatedAt

"""The user who owns this session"""
user: User! @relation(name: "UserSessions", references: [userId])
}

"""
Account entity for OAuth provider accounts linked to users.
Maps to Better-Auth's Account table structure.
"""
type Account @entity {
"""Unique identifier for the account"""
id: ID! @primary

"""Reference to the user who owns this account"""
userId: String! @index

"""OAuth provider name (e.g., 'google', 'github')"""
provider: String!

"""Account ID from the OAuth provider"""
providerAccountId: String!

"""OAuth access token"""
accessToken: String

"""OAuth refresh token"""
refreshToken: String

"""Access token expiration timestamp"""
accessTokenExpiresAt: DateTime

"""Refresh token expiration timestamp"""
refreshTokenExpiresAt: DateTime

"""OAuth scope granted"""
scope: String

"""OAuth ID token"""
idToken: String

"""OAuth token type"""
tokenType: String

"""Timestamp when the account was created"""
createdAt: DateTime! @default(value: "now")

"""Timestamp when the account was last updated"""
updatedAt: DateTime! @updatedAt

"""The user who owns this account"""
user: User! @relation(name: "UserAccounts", references: [userId])

"""Unique constraint on provider and providerAccountId"""
@@unique([provider, providerAccountId])
}

"""
VerificationToken entity for email verification and password reset flows.
Maps to Better-Auth's VerificationToken table structure.
"""
type VerificationToken @entity {
"""Unique identifier for the verification token"""
id: ID! @primary

"""Identifier (typically email or user ID)"""
identifier: String! @index

"""The verification token value"""
token: String! @unique

"""When this token expires"""
expiresAt: DateTime!

"""Timestamp when the token was created"""
createdAt: DateTime! @default(value: "now")

"""Unique constraint on identifier and token"""
@@unique([identifier, token])
}
21 changes: 21 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"jsx": "react-jsx"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}