# @memberjunction/api-keys Server-side API key management and scope-based authorization engine for MemberJunction. Provides key generation, validation, hierarchical scope evaluation, pattern-based access control, and usage logging. ## Architecture ```mermaid graph TD subgraph "API Key Engine" A[APIKeyEngine] --> B[ScopeEvaluator] A --> C[PatternMatcher] A --> D[UsageLogger] A --> E[APIKeysEngineBase
Metadata Cache] end subgraph "Authorization Flow" F[Incoming Request] --> G{Format Valid?} G -->|Yes| H{Hash Lookup} G -->|No| I[Reject] H -->|Found| J{Status Active?} H -->|Not Found| I J -->|Yes| K{Not Expired?} J -->|No| I K -->|Yes| L{App Binding OK?} K -->|No| I L -->|Yes| M[User Context] L -->|No| I end subgraph "Scope Evaluation" N[AuthorizationRequest] --> O{App Ceiling
Check} O -->|Pass| P{Key Scope
Check} O -->|Fail| Q[Denied] P -->|Pass| R[Allowed] P -->|Fail| Q end style A fill:#2d6a9f,stroke:#1a4971,color:#fff style B fill:#7c5295,stroke:#563a6b,color:#fff style C fill:#7c5295,stroke:#563a6b,color:#fff style D fill:#7c5295,stroke:#563a6b,color:#fff style E fill:#2d8659,stroke:#1a5c3a,color:#fff style R fill:#2d8659,stroke:#1a5c3a,color:#fff style Q fill:#b8762f,stroke:#8a5722,color:#fff style I fill:#b8762f,stroke:#8a5722,color:#fff ``` ## Overview This package implements a comprehensive API key authorization system with: - **Key Generation**: Cryptographically secure keys in `mj_sk_[64 hex]` format - **Key Validation**: Format checking, hash lookup, status/expiry verification, app binding checks - **Two-Level Authorization**: Application ceiling + API key scope evaluation - **Pattern Matching**: Wildcard and multi-value patterns for resource-level permissions - **Usage Logging**: Automatic audit trails for all authorization decisions - **Scope Hierarchy**: Structured permission paths (e.g., `entity:read`, `agent:execute`) ## Installation ```bash npm install @memberjunction/api-keys ``` **Requires Node.js 18+** (uses `crypto` module for key generation and hashing). ## Core Concepts ### API Key Format Keys follow the format `mj_sk_[64 hex characters]`: - `mj_sk_` prefix identifies MemberJunction secret keys - 64 hex characters = 32 bytes of cryptographically secure random data - Keys are stored as SHA-256 hashes, never in plaintext ### Two-Level Scope Evaluation ```mermaid graph LR A["Application Ceiling
(Maximum allowed)"] --> C{Intersection} B["API Key Scopes
(What key grants)"] --> C C --> D["Effective
Permissions"] style A fill:#2d6a9f,stroke:#1a4971,color:#fff style B fill:#7c5295,stroke:#563a6b,color:#fff style C fill:#b8762f,stroke:#8a5722,color:#fff style D fill:#2d8659,stroke:#1a5c3a,color:#fff ``` Authorization succeeds only when BOTH levels permit the operation: 1. **Application Ceiling** defines the maximum scopes an application allows 2. **API Key Scopes** define what scopes are assigned to a specific key ### Common Scopes | Scope | Description | |-------|-------------| | `full_access` | Bypass all scope checks | | `entity:read` | Read entity records | | `entity:create` | Create new records | | `entity:update` | Update existing records | | `entity:delete` | Delete records | | `view:run` | Execute RunView queries | | `agent:execute` | Execute AI agents | | `action:execute` | Execute MJ Actions | | `prompt:execute` | Execute AI prompts | | `query:run` | Execute SQL queries | | `metadata:entities:read` | Read entity metadata | | `communication:send` | Send emails/messages | ## Usage ### Getting the Engine ```typescript import { GetAPIKeyEngine } from '@memberjunction/api-keys'; const engine = GetAPIKeyEngine(); await engine.Config(false, contextUser); ``` ### Creating API Keys ```typescript const result = await engine.CreateAPIKey({ UserId: 'user-guid', Label: 'My Integration', Description: 'Used for CI/CD pipeline', ExpiresAt: new Date('2026-12-31') }, contextUser); if (result.Success) { // IMPORTANT: Show this key once -- it cannot be recovered console.log('Raw API Key:', result.RawKey); console.log('API Key ID:', result.APIKeyId); } ``` ### Validating API Keys ```typescript const result = await engine.ValidateAPIKey({ RawKey: request.headers['x-api-key'], ApplicationName: 'MCPServer', Endpoint: '/graphql', Method: 'POST', IPAddress: request.ip }, systemUser); if (result.IsValid) { // result.User contains the authenticated UserInfo // result.APIKeyHash is available for subsequent Authorize() calls } ``` ### Authorizing Requests ```typescript const authResult = await engine.Authorize( apiKeyHash, 'MJAPI', // Application name 'entity:read', // Scope path 'Users', // Resource name contextUser, { endpoint: '/graphql', method: 'POST', operation: 'GetUsersRecord' } ); if (authResult.Allowed) { // Proceed with the operation } else { console.log('Denied:', authResult.Reason); console.log('Rules evaluated:', authResult.EvaluatedRules); } ``` ### Pattern Matching ```typescript import { PatternMatcher } from '@memberjunction/api-keys'; PatternMatcher.matches('Users', '*'); // true - wildcard PatternMatcher.matches('Users', 'Users'); // true - exact PatternMatcher.matches('Users', 'User*'); // true - prefix PatternMatcher.matches('Users', '*ers'); // true - suffix PatternMatcher.matches('Users', '*ser*'); // true - contains PatternMatcher.matches('Users', 'Users,Roles'); // true - multi-value ``` ### Revoking Keys ```typescript const success = await engine.RevokeAPIKey(apiKeyId, contextUser); ``` ## Components | Component | Description | |-----------|-------------| | `APIKeyEngine` | Main orchestrator for key operations and authorization | | `ScopeEvaluator` | Two-level scope evaluation with priority-based rule matching | | `PatternMatcher` | Wildcard and multi-value pattern matching for resources | | `UsageLogger` | Audit trail logging for all authorization decisions | | `APIKeysEngineBase` | Metadata caching layer (from `@memberjunction/api-keys-base`) | ## Configuration ```typescript import { GetAPIKeyEngine } from '@memberjunction/api-keys'; const engine = GetAPIKeyEngine({ enforcementEnabled: true, // Enable scope checks (default: true) loggingEnabled: true, // Log all requests (default: true) defaultBehaviorNoScopes: 'deny', // Keys with no scopes are denied (default) scopeCacheTTLMs: 60000 // Cache TTL for scope rules }); ``` ## Database Entities | Entity | Description | |--------|-------------| | `MJ: API Keys` | API key hashes, status, expiration, metadata | | `MJ: API Scopes` | Hierarchical scope definitions | | `MJ: API Key Scopes` | Scope rules assigned to specific keys | | `MJ: API Applications` | Registered applications (MJAPI, MCP, A2A) | | `MJ: API Application Scopes` | Application-level scope ceilings | | `MJ: API Key Applications` | Key-to-application bindings | ## Testing ```bash npm test npm run test:coverage ``` Test files: `APIKeyEngine.spec.ts`, `ScopeEvaluator.spec.ts`, `PatternMatcher.spec.ts` ## Security Best Practices 1. **Default Deny**: Keys without scopes have no permissions 2. **Minimal Scopes**: Assign only the scopes needed 3. **Resource Restrictions**: Use specific patterns instead of `*` when possible 4. **Expiration**: Set expiration dates for temporary keys 5. **Monitoring**: Review usage logs for suspicious activity 6. **Rotation**: Regularly rotate keys in production ## Integration Used by `@memberjunction/server` (MJAPI), `@memberjunction/ai-mcp-server` (MCP Server), and `@memberjunction/a2aserver` (A2A Server). ## Dependencies | Package | Purpose | |---------|---------| | `@memberjunction/api-keys-base` | Metadata caching layer | | `@memberjunction/core` | RunView, Metadata, UserInfo | | `@memberjunction/core-entities` | Entity types | | `@memberjunction/global` | Class factory | ## License ISC