@memberjunction/credentials 3.4.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -385
- package/dist/CredentialEngine.d.ts +1 -1
- package/dist/CredentialEngine.js +21 -28
- package/dist/CredentialEngine.js.map +1 -1
- package/dist/__tests__/credential-validation.test.js +89 -94
- package/dist/__tests__/credential-validation.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/dist/types.js +1 -2
- package/package.json +14 -12
package/README.md
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
# @memberjunction/credentials
|
|
2
2
|
|
|
3
|
-
Secure credential management engine for MemberJunction. Provides centralized storage, retrieval, and audit logging of credentials with automatic field-level encryption.
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
Secure credential management engine for MemberJunction. Provides centralized storage, retrieval, validation, and audit logging of credentials with automatic field-level encryption and JSON Schema validation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@memberjunction/credentials` package manages the full credential lifecycle: storing encrypted values, resolving credentials by name or ID, validating against JSON Schema constraints, and logging every access for audit compliance.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
A["CredentialEngine<br/>(Singleton)"] --> B["Credential Types<br/>(Schema Definitions)"]
|
|
12
|
+
A --> C["Credentials<br/>(Encrypted Values)"]
|
|
13
|
+
A --> D["Credential Categories<br/>(Organization)"]
|
|
14
|
+
A --> E["Audit Log<br/>(Access Tracking)"]
|
|
15
|
+
A --> F["Ajv Validator<br/>(JSON Schema)"]
|
|
16
|
+
|
|
17
|
+
G["Consumer Code"] --> A
|
|
18
|
+
G -->|"getCredential()"| H["ResolvedCredential<T>"]
|
|
19
|
+
G -->|"storeCredential()"| C
|
|
20
|
+
G -->|"validateCredential()"| I["ValidationResult"]
|
|
21
|
+
|
|
22
|
+
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
23
|
+
style B fill:#7c5295,stroke:#563a6b,color:#fff
|
|
24
|
+
style C fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
25
|
+
style D fill:#b8762f,stroke:#8a5722,color:#fff
|
|
26
|
+
style E fill:#b8762f,stroke:#8a5722,color:#fff
|
|
27
|
+
style F fill:#7c5295,stroke:#563a6b,color:#fff
|
|
28
|
+
style H fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
29
|
+
style I fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
30
|
+
```
|
|
15
31
|
|
|
16
32
|
## Installation
|
|
17
33
|
|
|
@@ -21,411 +37,106 @@ npm install @memberjunction/credentials
|
|
|
21
37
|
|
|
22
38
|
## Quick Start
|
|
23
39
|
|
|
24
|
-
### 1. Initialize the Engine
|
|
25
|
-
|
|
26
40
|
```typescript
|
|
27
|
-
import { CredentialEngine } from '@memberjunction/credentials';
|
|
41
|
+
import { CredentialEngine, APIKeyCredentialValues } from '@memberjunction/credentials';
|
|
28
42
|
|
|
29
43
|
// Initialize at application startup
|
|
30
44
|
await CredentialEngine.Instance.Config(false, contextUser);
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### 2. Retrieve a Credential
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
import { CredentialEngine, APIKeyCredentialValues } from '@memberjunction/credentials';
|
|
37
|
-
|
|
38
|
-
// Get a credential with typed values
|
|
46
|
+
// Retrieve a credential with typed values
|
|
39
47
|
const cred = await CredentialEngine.Instance.getCredential<APIKeyCredentialValues>(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
contextUser,
|
|
43
|
-
subsystem: 'AIService'
|
|
44
|
-
}
|
|
48
|
+
'OpenAI',
|
|
49
|
+
{ contextUser, subsystem: 'AIService' }
|
|
45
50
|
);
|
|
46
51
|
|
|
47
52
|
// Use the decrypted values
|
|
48
|
-
|
|
53
|
+
console.log(cred.values.apiKey); // Strongly typed as string
|
|
49
54
|
```
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
## Credential Resolution
|
|
57
|
+
|
|
58
|
+
```mermaid
|
|
59
|
+
flowchart TD
|
|
60
|
+
A["getCredential(name, options)"] --> B{directValues<br/>provided?}
|
|
61
|
+
B -->|Yes| C["Return direct values<br/>source: request"]
|
|
62
|
+
B -->|No| D{credentialId<br/>provided?}
|
|
63
|
+
D -->|Yes| E["Lookup by ID"]
|
|
64
|
+
D -->|No| F["Lookup by name"]
|
|
65
|
+
E --> G["Parse & return values<br/>source: database"]
|
|
66
|
+
F --> G
|
|
67
|
+
G --> H["Log access to Audit Log"]
|
|
68
|
+
H --> I["Update LastUsedAt"]
|
|
69
|
+
|
|
70
|
+
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
71
|
+
style C fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
72
|
+
style G fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
73
|
+
style H fill:#b8762f,stroke:#8a5722,color:#fff
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
Resolution priority:
|
|
77
|
+
1. **Direct values** -- `directValues` in options (bypasses database, useful for testing)
|
|
78
|
+
2. **By ID** -- `credentialId` in options (specific credential lookup)
|
|
79
|
+
3. **By name** -- The `credentialName` parameter (most common usage)
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|------|--------|----------|
|
|
72
|
-
| API Key | `apiKey` | OpenAI, Anthropic, SendGrid, etc. |
|
|
73
|
-
| API Key with Endpoint | `apiKey`, `endpoint` | Azure OpenAI, custom APIs |
|
|
74
|
-
| OAuth2 Client Credentials | `clientId`, `clientSecret`, `tokenUrl`, `scope` | OAuth integrations |
|
|
75
|
-
| Basic Auth | `username`, `password` | Legacy systems |
|
|
76
|
-
| Azure Service Principal | `tenantId`, `clientId`, `clientSecret` | Microsoft Graph, Azure services |
|
|
77
|
-
| AWS IAM | `accessKeyId`, `secretAccessKey`, `region` | S3, SES, Lambda, etc. |
|
|
78
|
-
| Database Connection | `host`, `port`, `database`, `username`, `password` | External databases |
|
|
79
|
-
| Twilio | `accountSid`, `authToken` | SMS/Voice services |
|
|
81
|
+
## Pre-defined Credential Types
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
| Type | Interface | Fields |
|
|
84
|
+
|------|-----------|--------|
|
|
85
|
+
| API Key | `APIKeyCredentialValues` | `apiKey` |
|
|
86
|
+
| API Key with Endpoint | `APIKeyWithEndpointCredentialValues` | `apiKey`, `endpoint` |
|
|
87
|
+
| OAuth2 Client Credentials | `OAuth2ClientCredentialValues` | `clientId`, `clientSecret`, `tokenUrl`, `scope` |
|
|
88
|
+
| Basic Auth | `BasicAuthCredentialValues` | `username`, `password` |
|
|
89
|
+
| Azure Service Principal | `AzureServicePrincipalCredentialValues` | `tenantId`, `clientId`, `clientSecret` |
|
|
90
|
+
| AWS IAM | `AWSIAMCredentialValues` | `accessKeyId`, `secretAccessKey`, `region` |
|
|
91
|
+
| Database Connection | `DatabaseConnectionCredentialValues` | `host`, `port`, `database`, `username`, `password` |
|
|
92
|
+
| Twilio | `TwilioCredentialValues` | `accountSid`, `authToken` |
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
## Storing Credentials
|
|
84
95
|
|
|
85
96
|
```typescript
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{ contextUser }
|
|
97
|
-
);
|
|
98
|
-
console.log(ai.values.apiKey); // Typed as string
|
|
99
|
-
|
|
100
|
-
// AWS Services
|
|
101
|
-
const aws = await CredentialEngine.Instance.getCredential<AWSIAMCredentialValues>(
|
|
102
|
-
'AWS Production',
|
|
103
|
-
{ contextUser }
|
|
104
|
-
);
|
|
105
|
-
console.log(aws.values.accessKeyId, aws.values.secretAccessKey, aws.values.region);
|
|
106
|
-
|
|
107
|
-
// Azure Services
|
|
108
|
-
const azure = await CredentialEngine.Instance.getCredential<AzureServicePrincipalCredentialValues>(
|
|
109
|
-
'Microsoft Graph',
|
|
110
|
-
{ contextUser }
|
|
97
|
+
const credential = await CredentialEngine.Instance.storeCredential(
|
|
98
|
+
'API Key', // Credential type name
|
|
99
|
+
'OpenAI Production', // Credential name
|
|
100
|
+
{ apiKey: 'sk-...' }, // Values (encrypted on save)
|
|
101
|
+
{
|
|
102
|
+
isDefault: true,
|
|
103
|
+
description: 'Production OpenAI API key',
|
|
104
|
+
expiresAt: new Date('2025-12-31')
|
|
105
|
+
},
|
|
106
|
+
contextUser
|
|
111
107
|
);
|
|
112
|
-
console.log(azure.values.tenantId, azure.values.clientId);
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Resolution Priority
|
|
116
|
-
|
|
117
|
-
When you call `getCredential()`, credentials are resolved in this order:
|
|
118
|
-
|
|
119
|
-
1. **Direct Values** - If `directValues` is provided in options, use those (useful for testing)
|
|
120
|
-
2. **By ID** - If `credentialId` is provided, look up that specific credential
|
|
121
|
-
3. **By Name** - Look up by the credential name passed to `getCredential()`
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// Priority 1: Direct values (bypasses database)
|
|
125
|
-
const cred = await engine.getCredential('OpenAI', {
|
|
126
|
-
directValues: { apiKey: 'test-key' },
|
|
127
|
-
contextUser
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Priority 2: Specific credential by ID
|
|
131
|
-
const cred = await engine.getCredential('OpenAI', {
|
|
132
|
-
credentialId: 'specific-credential-uuid',
|
|
133
|
-
contextUser
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Priority 3: By name (most common)
|
|
137
|
-
const cred = await engine.getCredential('OpenAI', { contextUser });
|
|
138
108
|
```
|
|
139
109
|
|
|
140
|
-
##
|
|
141
|
-
|
|
142
|
-
The CredentialEngine caches API keys for fast hash-based lookup, used by authentication systems:
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
// Look up API key by hash (O(1) from cache)
|
|
146
|
-
const apiKey = CredentialEngine.Instance.getAPIKeyByHash(keyHash);
|
|
110
|
+
## JSON Schema Validation
|
|
147
111
|
|
|
148
|
-
|
|
149
|
-
// Key is valid
|
|
150
|
-
console.log('User ID:', apiKey.UserID);
|
|
151
|
-
}
|
|
152
|
-
```
|
|
112
|
+
The engine validates credential values against the `FieldSchema` defined on each Credential Type using Ajv. Supported constraints include `required`, `const`, `enum`, `format`, `pattern`, `minLength`/`maxLength`, and `minimum`/`maximum`.
|
|
153
113
|
|
|
154
|
-
|
|
114
|
+
Default and const values are auto-populated before validation, and validation errors produce clear, human-readable messages.
|
|
155
115
|
|
|
156
116
|
## Audit Logging
|
|
157
117
|
|
|
158
|
-
Every credential
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
const cred = await engine.getCredential('OpenAI', {
|
|
162
|
-
contextUser,
|
|
163
|
-
subsystem: 'AIService' // Logged for tracking
|
|
164
|
-
});
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Audit log entry includes:
|
|
168
|
-
- User who accessed the credential
|
|
169
|
-
- Operation type (Decrypt, Create, Update, Validate)
|
|
118
|
+
Every credential operation (Decrypt, Create, Update, Validate) is logged to the Audit Logs entity with:
|
|
119
|
+
- User who performed the operation
|
|
170
120
|
- Subsystem that requested access
|
|
171
|
-
- Success
|
|
121
|
+
- Success or failure status
|
|
172
122
|
- Duration in milliseconds
|
|
173
123
|
|
|
174
|
-
##
|
|
175
|
-
|
|
176
|
-
The CredentialEngine uses [Ajv JSON Schema validator](https://ajv.js.org/) to validate credential values against the `FieldSchema` defined in each Credential Type. This ensures data integrity and security by enforcing constraints at save time.
|
|
177
|
-
|
|
178
|
-
### Supported Constraints
|
|
179
|
-
|
|
180
|
-
All JSON Schema Draft 7 validation keywords are supported:
|
|
181
|
-
|
|
182
|
-
| Constraint | Description | Example Use Case |
|
|
183
|
-
|-----------|-------------|------------------|
|
|
184
|
-
| `required` | Mandatory fields | API keys must include `apiKey` field |
|
|
185
|
-
| `const` | Fixed immutable values | OAuth token URLs that must never change |
|
|
186
|
-
| `enum` | Limited set of allowed values | Region selection, account types |
|
|
187
|
-
| `format` | Value format validation | `uri`, `email`, `date`, `uuid` |
|
|
188
|
-
| `pattern` | Regex pattern matching | API key format validation |
|
|
189
|
-
| `minLength`/`maxLength` | String length bounds | Password length requirements |
|
|
190
|
-
| `minimum`/`maximum` | Numeric range validation | Port numbers, timeout values |
|
|
191
|
-
| `default` | Auto-populated values | Default regions, endpoints |
|
|
192
|
-
|
|
193
|
-
### Schema Validation Flow
|
|
194
|
-
|
|
195
|
-
1. **Create/Update**: When `storeCredential()` or `updateCredential()` is called
|
|
196
|
-
2. **Apply Defaults**: Fields with `default` or `const` values are auto-populated
|
|
197
|
-
3. **Validate**: The complete credential values are validated against the schema
|
|
198
|
-
4. **Throw on Error**: If validation fails, a clear error message is thrown listing all violations
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
// This credential type schema
|
|
202
|
-
{
|
|
203
|
-
"type": "object",
|
|
204
|
-
"properties": {
|
|
205
|
-
"apiKey": {
|
|
206
|
-
"type": "string",
|
|
207
|
-
"pattern": "^sk-[a-zA-Z0-9]{32}$",
|
|
208
|
-
"description": "OpenAI API key"
|
|
209
|
-
},
|
|
210
|
-
"endpoint": {
|
|
211
|
-
"type": "string",
|
|
212
|
-
"format": "uri",
|
|
213
|
-
"default": "https://api.openai.com/v1"
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
"required": ["apiKey"]
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Will reject this (invalid pattern)
|
|
220
|
-
await engine.storeCredential('OpenAI', 'Production', {
|
|
221
|
-
apiKey: 'invalid-format' // ❌ Fails pattern validation
|
|
222
|
-
}, {}, contextUser);
|
|
223
|
-
// Error: Field "apiKey" does not match required pattern
|
|
224
|
-
|
|
225
|
-
// Will reject this (invalid URI format)
|
|
226
|
-
await engine.storeCredential('OpenAI', 'Production', {
|
|
227
|
-
apiKey: 'sk-abcdefghijklmnopqrstuvwxyz123456',
|
|
228
|
-
endpoint: 'not-a-url' // ❌ Fails format validation
|
|
229
|
-
}, {}, contextUser);
|
|
230
|
-
// Error: Field "endpoint" must be a valid uri
|
|
231
|
-
|
|
232
|
-
// Will accept and auto-populate default
|
|
233
|
-
await engine.storeCredential('OpenAI', 'Production', {
|
|
234
|
-
apiKey: 'sk-abcdefghijklmnopqrstuvwxyz123456'
|
|
235
|
-
// endpoint auto-populated with default: https://api.openai.com/v1
|
|
236
|
-
}, {}, contextUser);
|
|
237
|
-
// ✅ Success
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Error Messages
|
|
241
|
-
|
|
242
|
-
Validation errors are formatted for clarity:
|
|
243
|
-
|
|
244
|
-
- **Required**: `Missing required field: apiKey`
|
|
245
|
-
- **Const**: `Field "tokenUrl" must be "https://api.box.com/oauth2/token"`
|
|
246
|
-
- **Enum**: `Field "region" must be one of: us-east-1, us-west-2, eu-west-1`
|
|
247
|
-
- **Format**: `Field "endpoint" must be a valid uri`
|
|
248
|
-
- **Pattern**: `Field "apiKey" does not match required pattern`
|
|
249
|
-
- **Length**: `Field "password" must be at least 8 characters`
|
|
250
|
-
- **Range**: `Field "port" must be at least 1024`
|
|
251
|
-
|
|
252
|
-
### Const Fields for Security
|
|
253
|
-
|
|
254
|
-
Use `const` in schemas to enforce fixed values that must never change:
|
|
255
|
-
|
|
256
|
-
```json
|
|
257
|
-
{
|
|
258
|
-
"tokenUrl": {
|
|
259
|
-
"type": "string",
|
|
260
|
-
"const": "https://api.box.com/oauth2/token",
|
|
261
|
-
"description": "Box.com OAuth token endpoint"
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
This prevents:
|
|
267
|
-
- Users from accidentally changing critical endpoints
|
|
268
|
-
- Malicious redirection of credentials to external servers
|
|
269
|
-
- Configuration drift across environments
|
|
270
|
-
|
|
271
|
-
Const values are:
|
|
272
|
-
- **Auto-populated** when creating credentials (users don't need to enter them)
|
|
273
|
-
- **Immutable** - validation rejects any attempt to change them
|
|
274
|
-
- **Visible** in UI as read-only fields
|
|
275
|
-
|
|
276
|
-
### Format Validation
|
|
277
|
-
|
|
278
|
-
The `format` keyword validates common data types:
|
|
279
|
-
|
|
280
|
-
- **uri** / **url** - Valid HTTP/HTTPS URLs
|
|
281
|
-
- **email** - Valid email addresses
|
|
282
|
-
- **date** - ISO 8601 dates
|
|
283
|
-
- **date-time** - ISO 8601 timestamps
|
|
284
|
-
- **uuid** - RFC 4122 UUIDs
|
|
285
|
-
- **ipv4** / **ipv6** - IP addresses
|
|
286
|
-
- **hostname** - Valid DNS hostnames
|
|
287
|
-
|
|
288
|
-
### Default Values
|
|
289
|
-
|
|
290
|
-
Use `default` to pre-populate fields with sensible values:
|
|
291
|
-
|
|
292
|
-
```json
|
|
293
|
-
{
|
|
294
|
-
"region": {
|
|
295
|
-
"type": "string",
|
|
296
|
-
"default": "us-east-1",
|
|
297
|
-
"enum": ["us-east-1", "us-west-2", "eu-west-1"]
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Users can override defaults, but they provide good starting points and reduce configuration errors.
|
|
303
|
-
|
|
304
|
-
## Credential Validation
|
|
305
|
-
|
|
306
|
-
Validate credentials against provider endpoints:
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
const result = await engine.validateCredential(credentialId, contextUser);
|
|
310
|
-
|
|
311
|
-
if (!result.isValid) {
|
|
312
|
-
console.error('Credential validation failed:', result.errors);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Result structure
|
|
316
|
-
interface CredentialValidationResult {
|
|
317
|
-
isValid: boolean;
|
|
318
|
-
errors: string[];
|
|
319
|
-
warnings: string[];
|
|
320
|
-
validatedAt: Date;
|
|
321
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
## Updating Credentials
|
|
325
|
-
|
|
326
|
-
Update credential values (automatically re-encrypted):
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
await engine.updateCredential(
|
|
330
|
-
credentialId,
|
|
331
|
-
{ apiKey: 'new-sk-...' }, // New values
|
|
332
|
-
contextUser
|
|
333
|
-
);
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
## Cached Data Access
|
|
337
|
-
|
|
338
|
-
Access cached credential metadata without database calls:
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
// All credentials
|
|
342
|
-
const credentials = engine.Credentials;
|
|
343
|
-
|
|
344
|
-
// All credential types
|
|
345
|
-
const types = engine.CredentialTypes;
|
|
346
|
-
|
|
347
|
-
// All credential categories
|
|
348
|
-
const categories = engine.CredentialCategories;
|
|
349
|
-
|
|
350
|
-
// Lookup helpers
|
|
351
|
-
const type = engine.getCredentialTypeByName('API Key');
|
|
352
|
-
const defaultCred = engine.getDefaultCredentialForType('API Key');
|
|
353
|
-
const credById = engine.getCredentialById('uuid-here');
|
|
354
|
-
const credByName = engine.getCredentialByName('API Key', 'OpenAI');
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## Database Schema
|
|
358
|
-
|
|
359
|
-
### MJ: Credential Types
|
|
360
|
-
|
|
361
|
-
Defines the shape of credential values:
|
|
362
|
-
|
|
363
|
-
| Column | Description |
|
|
364
|
-
|--------|-------------|
|
|
365
|
-
| `Name` | Type name (e.g., 'API Key') |
|
|
366
|
-
| `Description` | Human-readable description |
|
|
367
|
-
| `FieldSchema` | JSON Schema for validation |
|
|
368
|
-
| `ValidationEndpoint` | Optional URL for validation |
|
|
369
|
-
|
|
370
|
-
### MJ: Credentials
|
|
371
|
-
|
|
372
|
-
Stores credential instances:
|
|
373
|
-
|
|
374
|
-
| Column | Description |
|
|
375
|
-
|--------|-------------|
|
|
376
|
-
| `Name` | Credential name (e.g., 'OpenAI Production') |
|
|
377
|
-
| `CredentialTypeID` | Foreign key to type |
|
|
378
|
-
| `Values` | Encrypted JSON blob |
|
|
379
|
-
| `IsDefault` | Default for this type |
|
|
380
|
-
| `IsActive` | Active status |
|
|
381
|
-
| `ExpiresAt` | Optional expiration |
|
|
382
|
-
| `LastUsedAt` | Updated on each access |
|
|
383
|
-
| `LastValidatedAt` | Updated on validation |
|
|
384
|
-
|
|
385
|
-
### MJ: Credential Categories
|
|
386
|
-
|
|
387
|
-
Optional organization:
|
|
388
|
-
|
|
389
|
-
| Column | Description |
|
|
390
|
-
|--------|-------------|
|
|
391
|
-
| `Name` | Category name |
|
|
392
|
-
| `Description` | Description |
|
|
393
|
-
| `ParentCategoryID` | For hierarchy |
|
|
394
|
-
|
|
395
|
-
## Security Considerations
|
|
396
|
-
|
|
397
|
-
1. **Encryption at Rest**
|
|
398
|
-
- The `Values` field uses MJ field-level encryption
|
|
399
|
-
- Only decrypted when explicitly accessed via `getCredential()`
|
|
400
|
-
|
|
401
|
-
2. **Audit Trail**
|
|
402
|
-
- Every access is logged with user, timestamp, and subsystem
|
|
403
|
-
- Failed access attempts are also logged
|
|
404
|
-
|
|
405
|
-
3. **Access Control**
|
|
406
|
-
- Credentials inherit MJ's entity-level permissions
|
|
407
|
-
- Use `contextUser` to enforce authorization
|
|
408
|
-
|
|
409
|
-
4. **Expiration**
|
|
410
|
-
- Set `ExpiresAt` on credentials to enforce rotation
|
|
411
|
-
- Check `expiresAt` in the resolved credential for warnings
|
|
412
|
-
|
|
413
|
-
5. **Per-Request Override**
|
|
414
|
-
- Use `directValues` for testing without database access
|
|
415
|
-
- Values are marked with `source: 'request'` for audit clarity
|
|
416
|
-
|
|
417
|
-
## Integration with EncryptionEngine
|
|
124
|
+
## Security
|
|
418
125
|
|
|
419
|
-
The
|
|
126
|
+
- **Encryption at rest** -- The `Values` field uses MJ field-level encryption
|
|
127
|
+
- **Audit trail** -- All access logged including failed attempts
|
|
128
|
+
- **Access control** -- Entity-level permissions enforced via `contextUser`
|
|
129
|
+
- **Expiration support** -- `ExpiresAt` field enforces credential rotation
|
|
420
130
|
|
|
421
|
-
|
|
422
|
-
- **EncryptionEngine** - Handles API key generation, validation, and field encryption
|
|
131
|
+
## Dependencies
|
|
423
132
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
133
|
+
| Package | Purpose |
|
|
134
|
+
|---------|---------|
|
|
135
|
+
| `@memberjunction/core` | Base engine, metadata, entity system |
|
|
136
|
+
| `@memberjunction/global` | Global state management |
|
|
137
|
+
| `@memberjunction/core-entities` | Credential entity types |
|
|
138
|
+
| `ajv` | JSON Schema validation |
|
|
139
|
+
| `ajv-formats` | Format validators (uri, email, date) |
|
|
429
140
|
|
|
430
141
|
## License
|
|
431
142
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseEngine, IMetadataProvider, UserInfo } from "@memberjunction/core";
|
|
2
2
|
import { CredentialCategoryEntity, CredentialEntity, CredentialTypeEntity } from "@memberjunction/core-entities";
|
|
3
|
-
import { CredentialResolutionOptions, ResolvedCredential, StoreCredentialOptions, CredentialValidationResult } from "./types";
|
|
3
|
+
import { CredentialResolutionOptions, ResolvedCredential, StoreCredentialOptions, CredentialValidationResult } from "./types.js";
|
|
4
4
|
export declare class CredentialEngine extends BaseEngine<CredentialEngine> {
|
|
5
5
|
private _credentials;
|
|
6
6
|
private _credentialTypes;
|