Files
wiki-security-composable/Server Changes Proposal.md
2025-08-02 02:48:51 -05:00

288 lines
10 KiB
Markdown

# Federated Wiki Security Architecture Enhancement
## Overview
This document proposes an enhancement to Federated Wiki's security architecture that separates **authentication** (who you are) from **authorization enhancement** (what you can do). This change would enable complementary security features like API tokens, rate limiting, and fine-grained permissions to work alongside any authentication method without conflicts.
## Current Architecture Limitations
### How Federated Wiki Security Works Today
Federated Wiki currently uses a single "security plugin" architecture where each plugin handles:
- **Authentication** - Proving user identity (OAuth, shared secrets, etc.)
- **Session management** - Storing user state
- **Authorization** - Determining what users can do
- **Route handling** - Login/logout endpoints
- **UI components** - Login dialogs and forms
### The Problem
This monolithic approach creates several issues:
1. **Mutually Exclusive Plugins**: You can only use one security plugin at a time
2. **Limited Extensibility**: Adding new authentication methods requires replacing the entire security system
3. **Feature Conflicts**: Plugins that want to enhance security (like API tokens) must either:
- Duplicate all existing authentication logic, or
- Use hacky workarounds to simulate existing authentication
### Real-World Impact
Consider these common scenarios that are difficult or impossible today:
```bash
# Impossible: OAuth login + API tokens
wiki --security_type passportjs --enable_api_tokens
# Impossible: Friends auth + rate limiting
wiki --security_type friends --enable_rate_limiting
# Impossible: Any auth + audit logging
wiki --security_type passportjs --enable_audit_log
```
Each security enhancement requires creating a completely new security plugin or forking existing ones.
## Proposed Solution: Composable Authentication Architecture
### Core Concept
Separate authentication from authorization enhancement:
- **Authentication Component**: Handles core identity verification (one plugin)
- **Authorization Enhancement Components**: Add complementary security features (multiple plugins)
### New Command Line Interface
*Proposed future syntax if server adopts this architecture natively:*
```bash
# Authentication (mutually exclusive)
wiki --auth_type friends
wiki --auth_type passportjs
wiki --auth_type ldap
# Authorization enhancements (complementary)
wiki --auth_type friends --authz_enhancers wiki-plugin-useraccesstokens
wiki --auth_type passportjs --authz_enhancers wiki-plugin-useraccesstokens,wiki-plugin-ratelimit
wiki --auth_type ldap --authz_enhancers wiki-plugin-useraccesstokens,wiki-plugin-permissions,wiki-plugin-audit
```
*Current implementation using `wiki-security-composable`:*
```bash
wiki --security_type composable --auth_provider wiki-security-friends --authz_enhancers wiki-plugin-useraccesstokens
```
### Plugin Types
**Authentication Plugins** (`wiki-auth-*`):
- Manage core user identity
- Handle owner/admin concepts
- Provide login/logout flows
- Store identity in consistent format
**Authorization Enhancement Plugins** (`wiki-plugin-*` with `securityEnhancer` export):
- Add supplementary authentication methods (API tokens, webhooks)
- Implement security policies (rate limiting, permissions)
- Provide monitoring/audit capabilities
- Work with any authentication plugin
## Technical Implementation
### Architecture Overview
```javascript
// Current: Single security handler
app.securityhandler = require(argv.security_type)(log, loga, argv)
// Proposed: Server-native composable architecture
const authHandler = require(argv.auth_provider)(log, loga, argv)
const enhancers = loadAuthzEnhancers(argv.authz_enhancers, authHandler)
app.securityhandler = composeSecurityHandler(authHandler, enhancers)
```
### Interface Contracts
**Authentication Plugin Interface**:
```javascript
{
retrieveOwner: (callback) => void,
getOwner: () => string,
setOwner: (identity, callback) => void,
getUser: (request) => object,
isAuthorized: (request) => boolean,
isAdmin: (request) => boolean,
defineRoutes: (app, cors, updateOwner) => void
}
```
**Authorization Enhancement Plugin Interface** (exported as `securityEnhancer` function):
```javascript
{
getUser?: (request) => object, // Optional: detect alternative auth
isAuthorized?: (request) => boolean, // Optional: additional auth checks
isAdmin?: (request) => boolean, // Optional: additional admin checks
defineRoutes?: (app, cors) => void // Optional: add routes
}
```
### Backward Compatibility
The current `--security_type` parameter continues to work:
```bash
# Old way (still works)
wiki --security_type friends
# Automatically translates to:
wiki --auth_type friends --authz_enhancers ""
```
Existing security plugins can be migrated incrementally without breaking changes.
### Migration Path
1. Extend `wiki-server` to support new parameter format
2. Implement composite security handler (as shown with `wiki-security-composable`)
3. Maintain full backward compatibility
## Current Implementation Status
This enhancement has a **working proof-of-concept implementation** in `wiki-security-composable` that demonstrates the architecture without requiring server changes. The implementation uses the existing plugin system where authorization enhancers are regular `wiki-plugin-*` packages that export a `securityEnhancer` function.
**Current Working Components:**
- `wiki-security-composable` - Foundation plugin that composes authentication with authorization enhancers
- `wiki-plugin-useraccesstokens` - Authorization enhancer for API tokens
- `wiki-plugin-ratelimit` - Authorization enhancer for request rate limiting
Additional authorization enhancers can be created following the established pattern
**How It Works Today:**
```bash
# Current implementation syntax
wiki --security_type composable --auth_provider wiki-security-friends --authz_enhancers wiki-plugin-useraccesstokens
```
The `wiki-security-composable` plugin acts as a wrapper that:
1. Loads the specified authentication provider (e.g., `wiki-security-friends`)
2. Loads authorization enhancer plugins that export `securityEnhancer` functions
3. Composes them using a function composition pattern
4. Presents a unified security interface to the wiki server
### Client asset management challenges with `wiki-security-composable`
When using `wiki-security-composable`, the server expects security assets to be in one specific location (e.g.,`wiki-security-composable/client/` when using the `--security_type composable` parameter). However, the actual authentication assets are in the base authentication provider's directory (e.g., `wiki-security-friends/client/`).
```javascript
// wiki-server/lib/server.js:284
app.use('/security', express.static(path.join(argv.packageDir, argv.security_type, 'client'), staticPathOptions))
```
This creates a hardcoded expectation that security assets are at `{packageDir}/{security_type}/client/`, but when using composable security:
- `security_type = "wiki-security-composable"`
- Actual authentication assets are in `wiki-security-friends/client/` or `wiki-security-passportjs/client/`
**Current Workaround**: `wiki-security-composable` must copy client assets from the base authentication provider to its own directory during initialization:
```javascript
// Detect base provider assets
const baseProviderClientPath = path.join(
path.dirname(require.resolve(`${authProvider}/package.json`)),
'client'
);
// Copy to our expected location
const ourClientPath = path.join(__dirname, '..', 'client');
fs.rmSync(ourClientPath, { recursive: true, force: true });
copyFiles(baseProviderClientPath, ourClientPath);
```
## Server Enhancement Proposal
The server could adopt this composable pattern natively to eliminate these workarounds and provide a cleaner developer and user experience:
### Native Configuration Support
Instead of the current workaround syntax, the server could support:
```bash
# Proposed native server support
wiki --auth_provider wiki-security-friends --authz_enhancers wiki-plugin-useraccesstokens,wiki-plugin-ratelimit
```
With backward compatibility:
```bash
# Current syntax continues to work
wiki --security_type friends # translates to --auth_provider wiki-security-friends
```
### Dynamic Asset Resolution
**Server Enhancement**: The server could dynamically resolve static routes based on the actual authentication provider:
```javascript
// Proposed server enhancement
const authProvider = argv.auth_provider;
const authAssetPath = path.join(path.dirname(require.resolve(`${authProvider}/package.json`)), 'client');
app.use('/auth', express.static(authAssetPath, staticPathOptions));
// Authorization enhancers continue using existing /plugins/ routes
```
### Built-in Composition Logic
**Server Enhancement**: The server could handle security composition natively:
```javascript
// Proposed server enhancement in defaultargs.js
if (argv.authz_enhancers) {
if (typeof argv.authz_enhancers === 'string') {
argv.authz_enhancers = argv.authz_enhancers.split(',').map(name => name.trim());
}
} else {
argv.authz_enhancers = [];
}
// Backward compatibility
if (argv.security_type && !argv.auth_provider) {
argv.auth_provider = argv.security_type;
}
```
```javascript
// Proposed server enhancement in server.js
const authHandler = require(argv.auth_provider)(log, loga, argv);
const authzHandlers = [];
if (argv.authz_enhancers) {
argv.authz_enhancers.forEach(pluginName => {
const plugin = require(pluginName);
if (plugin.securityEnhancer && typeof plugin.securityEnhancer === 'function') {
const handler = plugin.securityEnhancer(log, loga, argv, authHandler);
authzHandlers.push(handler);
}
});
}
// Create composite security handler
app.securityhandler = composeSecurityHandler(authHandler, authzHandlers);
```
### Authorization Enhancer Interface
The current plugin interface would remain unchanged:
```javascript
// wiki-plugin-useraccesstokens/index.js
export const securityEnhancer = (log, loga, argv, authHandler) => {
return {
getUser: (req, baseGetUser) => { /* enhanced user detection */ },
isAuthorized: (req, baseIsAuthorized) => { /* enhanced authorization */ },
isAdmin: (req, baseIsAdmin) => { /* enhanced admin checks */ },
middleware: (req, res, next) => { /* token validation, etc. */ },
defineRoutes: (app, cors, updateOwner) => { /* additional routes */ }
};
};
```