@sentinel-atl/stepup 0.1.1
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 +56 -0
- package/dist/__tests__/stepup.test.d.ts +2 -0
- package/dist/__tests__/stepup.test.d.ts.map +1 -0
- package/dist/__tests__/stepup.test.js +137 -0
- package/dist/__tests__/stepup.test.js.map +1 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @sentinel-atl/stepup
|
|
2
|
+
|
|
3
|
+
Step-up authentication — re-prompt humans for sensitive agent actions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Policy-based triggers** — define when step-up auth is required (scope, risk, time-based)
|
|
8
|
+
- **Challenge-response** — time-bounded, single-use approval tokens
|
|
9
|
+
- **Multi-factor** — support for TOTP, biometric, and custom challenge types
|
|
10
|
+
- **Audit integration** — all step-up events logged
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @sentinel-atl/stepup
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { StepUpManager } from '@sentinel-atl/stepup';
|
|
22
|
+
|
|
23
|
+
const mgr = new StepUpManager({
|
|
24
|
+
auditLog,
|
|
25
|
+
policies: [
|
|
26
|
+
{ trigger: 'scope', scopes: ['admin:*'], challengeType: 'totp' },
|
|
27
|
+
{ trigger: 'risk_score', threshold: 0.8, challengeType: 'biometric' },
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Check if step-up is needed
|
|
32
|
+
const result = mgr.evaluate({
|
|
33
|
+
agentDid: 'did:key:z6Mk...',
|
|
34
|
+
requestedScopes: ['admin:delete'],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (result.required) {
|
|
38
|
+
// Present challenge to human
|
|
39
|
+
const challenge = mgr.createChallenge(result);
|
|
40
|
+
// ... human responds ...
|
|
41
|
+
const approval = mgr.verifyResponse(challenge, response);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `new StepUpManager(config)` | Create manager with policies |
|
|
50
|
+
| `evaluate(context)` | Check if step-up is required |
|
|
51
|
+
| `createChallenge(result)` | Create a time-bounded challenge |
|
|
52
|
+
| `verifyResponse(challenge, response)` | Verify human response |
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepup.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/stepup.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { StepUpManager } from '../index.js';
|
|
3
|
+
import { InMemoryKeyProvider, publicKeyToDid } from '@sentinel-atl/core';
|
|
4
|
+
async function makeIdentity(kp, name) {
|
|
5
|
+
await kp.generate(name);
|
|
6
|
+
const pubKey = await kp.getPublicKey(name);
|
|
7
|
+
return { keyId: name, did: publicKeyToDid(pubKey) };
|
|
8
|
+
}
|
|
9
|
+
describe('@sentinel-atl/stepup', () => {
|
|
10
|
+
let manager;
|
|
11
|
+
let principalKP;
|
|
12
|
+
let principal;
|
|
13
|
+
let agentKP;
|
|
14
|
+
let agent;
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
principalKP = new InMemoryKeyProvider();
|
|
17
|
+
principal = await makeIdentity(principalKP, 'human');
|
|
18
|
+
agentKP = new InMemoryKeyProvider();
|
|
19
|
+
agent = await makeIdentity(agentKP, 'agent');
|
|
20
|
+
manager = new StepUpManager({
|
|
21
|
+
alwaysRequireActions: ['delete_account', 'transfer_funds'],
|
|
22
|
+
sensitivityLevels: ['high', 'critical'],
|
|
23
|
+
challengeTimeoutMs: 5 * 60_000,
|
|
24
|
+
maxPendingChallenges: 3,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// ─── requiresStepUp ────────────────────────────────────────────
|
|
28
|
+
describe('requiresStepUp', () => {
|
|
29
|
+
it('requires step-up for explicit actions', () => {
|
|
30
|
+
const result = manager.requiresStepUp('delete_account');
|
|
31
|
+
expect(result.required).toBe(true);
|
|
32
|
+
expect(result.trigger).toBe('policy_rule');
|
|
33
|
+
});
|
|
34
|
+
it('requires step-up for high sensitivity', () => {
|
|
35
|
+
const result = manager.requiresStepUp('some_action', 'high');
|
|
36
|
+
expect(result.required).toBe(true);
|
|
37
|
+
expect(result.trigger).toBe('sensitivity_high');
|
|
38
|
+
});
|
|
39
|
+
it('requires step-up for critical sensitivity', () => {
|
|
40
|
+
const result = manager.requiresStepUp('some_action', 'critical');
|
|
41
|
+
expect(result.required).toBe(true);
|
|
42
|
+
expect(result.trigger).toBe('sensitivity_critical');
|
|
43
|
+
});
|
|
44
|
+
it('does not require step-up for low sensitivity', () => {
|
|
45
|
+
const result = manager.requiresStepUp('read_email', 'low');
|
|
46
|
+
expect(result.required).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it('does not require step-up for unknown actions', () => {
|
|
49
|
+
const result = manager.requiresStepUp('read_email');
|
|
50
|
+
expect(result.required).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
// ─── Challenge creation ────────────────────────────────────────
|
|
54
|
+
describe('challenges', () => {
|
|
55
|
+
it('creates a challenge', () => {
|
|
56
|
+
const challenge = manager.createChallenge(agent.did, principal.did, 'delete_account', ['admin:delete'], 'policy_rule', 'Delete user account #12345');
|
|
57
|
+
expect(challenge.challengeId).toMatch(/^stepup-/);
|
|
58
|
+
expect(challenge.agentDid).toBe(agent.did);
|
|
59
|
+
expect(challenge.principalDid).toBe(principal.did);
|
|
60
|
+
expect(challenge.action).toBe('delete_account');
|
|
61
|
+
expect(challenge.nonce).toBeDefined();
|
|
62
|
+
expect(challenge.expiresAt).toBeDefined();
|
|
63
|
+
expect(manager.getPendingCount()).toBe(1);
|
|
64
|
+
});
|
|
65
|
+
it('enforces max pending challenges', () => {
|
|
66
|
+
for (let i = 0; i < 3; i++) {
|
|
67
|
+
manager.createChallenge(agent.did, principal.did, `action_${i}`, [], 'policy_rule', `Action ${i}`);
|
|
68
|
+
}
|
|
69
|
+
expect(() => {
|
|
70
|
+
manager.createChallenge(agent.did, principal.did, 'action_4', [], 'policy_rule', 'Action 4');
|
|
71
|
+
}).toThrow('Max pending challenges');
|
|
72
|
+
});
|
|
73
|
+
it('cancels a challenge', () => {
|
|
74
|
+
const challenge = manager.createChallenge(agent.did, principal.did, 'test', [], 'policy_rule', 'Test');
|
|
75
|
+
expect(manager.getPendingCount()).toBe(1);
|
|
76
|
+
manager.cancelChallenge(challenge.challengeId);
|
|
77
|
+
expect(manager.getPendingCount()).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// ─── Approval flow ─────────────────────────────────────────────
|
|
81
|
+
describe('approval flow', () => {
|
|
82
|
+
let challenge;
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
challenge = manager.createChallenge(agent.did, principal.did, 'transfer_funds', ['payment:transfer'], 'sensitivity_high', 'Transfer $5,000 to vendor');
|
|
85
|
+
});
|
|
86
|
+
it('approves with valid principal signature', async () => {
|
|
87
|
+
const approval = await manager.signApproval(principalKP, principal.keyId, challenge, 'approved');
|
|
88
|
+
const result = await manager.verifyApproval(approval);
|
|
89
|
+
expect(result.approved).toBe(true);
|
|
90
|
+
expect(result.challengeId).toBe(challenge.challengeId);
|
|
91
|
+
});
|
|
92
|
+
it('handles denial', async () => {
|
|
93
|
+
const approval = await manager.signApproval(principalKP, principal.keyId, challenge, 'denied');
|
|
94
|
+
const result = await manager.verifyApproval(approval);
|
|
95
|
+
expect(result.approved).toBe(false);
|
|
96
|
+
expect(result.error).toContain('Denied by principal');
|
|
97
|
+
});
|
|
98
|
+
it('rejects wrong signer', async () => {
|
|
99
|
+
// Agent tries to approve instead of principal
|
|
100
|
+
const fakeApproval = await manager.signApproval(agentKP, agent.keyId, challenge, 'approved');
|
|
101
|
+
// Override the principalDid to match challenge (spoofing attempt)
|
|
102
|
+
// The signature won't match principal's key
|
|
103
|
+
const result = await manager.verifyApproval(fakeApproval);
|
|
104
|
+
expect(result.approved).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('prevents replay (second use of same challenge)', async () => {
|
|
107
|
+
const approval = await manager.signApproval(principalKP, principal.keyId, challenge, 'approved');
|
|
108
|
+
// First use succeeds
|
|
109
|
+
const r1 = await manager.verifyApproval(approval);
|
|
110
|
+
expect(r1.approved).toBe(true);
|
|
111
|
+
// Replay fails (challenge consumed)
|
|
112
|
+
const r2 = await manager.verifyApproval(approval);
|
|
113
|
+
expect(r2.approved).toBe(false);
|
|
114
|
+
expect(r2.error).toContain('not found or already consumed');
|
|
115
|
+
});
|
|
116
|
+
it('rejects expired challenge', async () => {
|
|
117
|
+
// Create manager with 0ms timeout
|
|
118
|
+
const fastManager = new StepUpManager({ challengeTimeoutMs: 0 });
|
|
119
|
+
const expiredChallenge = fastManager.createChallenge(agent.did, principal.did, 'test', [], 'policy_rule', 'Expired test');
|
|
120
|
+
// Wait a tick for expiry
|
|
121
|
+
await new Promise(r => setTimeout(r, 5));
|
|
122
|
+
const approval = await fastManager.signApproval(principalKP, principal.keyId, expiredChallenge, 'approved');
|
|
123
|
+
const result = await fastManager.verifyApproval(approval);
|
|
124
|
+
expect(result.approved).toBe(false);
|
|
125
|
+
expect(result.error).toContain('expired');
|
|
126
|
+
});
|
|
127
|
+
it('rejects principal DID mismatch', async () => {
|
|
128
|
+
const approval = await manager.signApproval(principalKP, principal.keyId, challenge, 'approved');
|
|
129
|
+
// Tamper with the principal DID
|
|
130
|
+
approval.principalDid = agent.did;
|
|
131
|
+
const result = await manager.verifyApproval(approval);
|
|
132
|
+
expect(result.approved).toBe(false);
|
|
133
|
+
expect(result.error).toContain('mismatch');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
//# sourceMappingURL=stepup.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepup.test.js","sourceRoot":"","sources":["../../src/__tests__/stepup.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sentinel-atl/stepup — Step-Up Authentication for Sensitive Actions
|
|
3
|
+
*
|
|
4
|
+
* When an agent is about to do something high-sensitivity — authorize a
|
|
5
|
+
* large payment, delete data, modify permissions — the trust pipeline
|
|
6
|
+
* should PAUSE and ask the human principal to re-confirm.
|
|
7
|
+
*
|
|
8
|
+
* This is the "Are you sure?" for AI agents, but cryptographically backed.
|
|
9
|
+
*
|
|
10
|
+
* How it works:
|
|
11
|
+
*
|
|
12
|
+
* 1. Agent detects a sensitive action (via VC sensitivity level or policy rules)
|
|
13
|
+
* 2. Agent creates a `StepUpChallenge` describing what it wants to do
|
|
14
|
+
* 3. Challenge is sent to the human principal
|
|
15
|
+
* 4. Human signs an `StepUpApproval` (proving they reviewed + approved)
|
|
16
|
+
* 5. Agent proceeds only if the approval signature is valid and timely
|
|
17
|
+
*
|
|
18
|
+
* The challenge is time-bounded and single-use to prevent replay.
|
|
19
|
+
*/
|
|
20
|
+
import { type KeyProvider, type SensitivityLevel } from '@sentinel-atl/core';
|
|
21
|
+
import { AuditLog } from '@sentinel-atl/audit';
|
|
22
|
+
export interface StepUpChallenge {
|
|
23
|
+
/** Unique challenge ID */
|
|
24
|
+
challengeId: string;
|
|
25
|
+
/** The agent requesting approval */
|
|
26
|
+
agentDid: string;
|
|
27
|
+
/** The human principal being asked to approve */
|
|
28
|
+
principalDid: string;
|
|
29
|
+
/** Human-readable description of the action */
|
|
30
|
+
actionDescription: string;
|
|
31
|
+
/** Machine-readable action identifier */
|
|
32
|
+
action: string;
|
|
33
|
+
/** Scope being requested */
|
|
34
|
+
scope: string[];
|
|
35
|
+
/** Why step-up was triggered */
|
|
36
|
+
triggerReason: StepUpTrigger;
|
|
37
|
+
/** When the challenge expires (ISO 8601) */
|
|
38
|
+
expiresAt: string;
|
|
39
|
+
/** One-time nonce */
|
|
40
|
+
nonce: string;
|
|
41
|
+
/** Created timestamp */
|
|
42
|
+
createdAt: string;
|
|
43
|
+
}
|
|
44
|
+
export interface StepUpApproval {
|
|
45
|
+
/** The challenge being approved */
|
|
46
|
+
challengeId: string;
|
|
47
|
+
/** The principal who approved it */
|
|
48
|
+
principalDid: string;
|
|
49
|
+
/** Approval or denial */
|
|
50
|
+
decision: 'approved' | 'denied';
|
|
51
|
+
/** When the decision was made */
|
|
52
|
+
decidedAt: string;
|
|
53
|
+
/** Ed25519 signature over the approval data (base64url) */
|
|
54
|
+
signature: string;
|
|
55
|
+
}
|
|
56
|
+
export type StepUpTrigger = 'sensitivity_high' | 'sensitivity_critical' | 'amount_threshold' | 'scope_escalation' | 'first_use' | 'anomaly_detected' | 'policy_rule';
|
|
57
|
+
export interface StepUpPolicy {
|
|
58
|
+
/** Actions that always require step-up */
|
|
59
|
+
alwaysRequireActions?: string[];
|
|
60
|
+
/** Sensitivity levels that trigger step-up (default: ['high', 'critical']) */
|
|
61
|
+
sensitivityLevels?: SensitivityLevel[];
|
|
62
|
+
/** Challenge timeout in ms (default: 5 minutes) */
|
|
63
|
+
challengeTimeoutMs?: number;
|
|
64
|
+
/** Maximum pending challenges per agent */
|
|
65
|
+
maxPendingChallenges?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface StepUpResult {
|
|
68
|
+
approved: boolean;
|
|
69
|
+
error?: string;
|
|
70
|
+
challengeId?: string;
|
|
71
|
+
}
|
|
72
|
+
export declare class StepUpManager {
|
|
73
|
+
private pendingChallenges;
|
|
74
|
+
private usedNonces;
|
|
75
|
+
private policy;
|
|
76
|
+
private auditLog?;
|
|
77
|
+
constructor(policy?: StepUpPolicy, auditLog?: AuditLog);
|
|
78
|
+
/**
|
|
79
|
+
* Check if an action requires step-up authentication.
|
|
80
|
+
*/
|
|
81
|
+
requiresStepUp(action: string, sensitivityLevel?: SensitivityLevel): {
|
|
82
|
+
required: boolean;
|
|
83
|
+
trigger?: StepUpTrigger;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Create a step-up challenge for the human principal to approve.
|
|
87
|
+
*/
|
|
88
|
+
createChallenge(agentDid: string, principalDid: string, action: string, scope: string[], trigger: StepUpTrigger, actionDescription: string): StepUpChallenge;
|
|
89
|
+
/**
|
|
90
|
+
* Principal signs an approval for a challenge.
|
|
91
|
+
*/
|
|
92
|
+
signApproval(keyProvider: KeyProvider, principalKeyId: string, challenge: StepUpChallenge, decision: 'approved' | 'denied'): Promise<StepUpApproval>;
|
|
93
|
+
/**
|
|
94
|
+
* Verify a step-up approval and consume the challenge.
|
|
95
|
+
*
|
|
96
|
+
* This is the critical gate: if the approval is invalid, expired,
|
|
97
|
+
* or replayed, the action MUST NOT proceed.
|
|
98
|
+
*/
|
|
99
|
+
verifyApproval(approval: StepUpApproval): Promise<StepUpResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Get a pending challenge by ID.
|
|
102
|
+
*/
|
|
103
|
+
getChallenge(challengeId: string): StepUpChallenge | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Get count of pending challenges.
|
|
106
|
+
*/
|
|
107
|
+
getPendingCount(): number;
|
|
108
|
+
/**
|
|
109
|
+
* Cancel a pending challenge.
|
|
110
|
+
*/
|
|
111
|
+
cancelChallenge(challengeId: string): boolean;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sentinel-atl/stepup — Step-Up Authentication for Sensitive Actions
|
|
3
|
+
*
|
|
4
|
+
* When an agent is about to do something high-sensitivity — authorize a
|
|
5
|
+
* large payment, delete data, modify permissions — the trust pipeline
|
|
6
|
+
* should PAUSE and ask the human principal to re-confirm.
|
|
7
|
+
*
|
|
8
|
+
* This is the "Are you sure?" for AI agents, but cryptographically backed.
|
|
9
|
+
*
|
|
10
|
+
* How it works:
|
|
11
|
+
*
|
|
12
|
+
* 1. Agent detects a sensitive action (via VC sensitivity level or policy rules)
|
|
13
|
+
* 2. Agent creates a `StepUpChallenge` describing what it wants to do
|
|
14
|
+
* 3. Challenge is sent to the human principal
|
|
15
|
+
* 4. Human signs an `StepUpApproval` (proving they reviewed + approved)
|
|
16
|
+
* 5. Agent proceeds only if the approval signature is valid and timely
|
|
17
|
+
*
|
|
18
|
+
* The challenge is time-bounded and single-use to prevent replay.
|
|
19
|
+
*/
|
|
20
|
+
import { toBase64Url, fromBase64Url, textToBytes, secureRandom, verify, toHex, didToPublicKey, } from '@sentinel-atl/core';
|
|
21
|
+
// ─── Canonicalization ────────────────────────────────────────────────
|
|
22
|
+
function canonicalizeChallenge(challenge) {
|
|
23
|
+
return textToBytes(`stepup:${challenge.challengeId}:${challenge.agentDid}:${challenge.principalDid}:${challenge.action}:${challenge.nonce}:${challenge.expiresAt}`);
|
|
24
|
+
}
|
|
25
|
+
// ─── Step-Up Manager ─────────────────────────────────────────────────
|
|
26
|
+
export class StepUpManager {
|
|
27
|
+
pendingChallenges = new Map();
|
|
28
|
+
usedNonces = new Set();
|
|
29
|
+
policy;
|
|
30
|
+
auditLog;
|
|
31
|
+
constructor(policy = {}, auditLog) {
|
|
32
|
+
this.policy = {
|
|
33
|
+
alwaysRequireActions: policy.alwaysRequireActions ?? [],
|
|
34
|
+
sensitivityLevels: policy.sensitivityLevels ?? ['high', 'critical'],
|
|
35
|
+
challengeTimeoutMs: policy.challengeTimeoutMs ?? 5 * 60_000,
|
|
36
|
+
maxPendingChallenges: policy.maxPendingChallenges ?? 10,
|
|
37
|
+
};
|
|
38
|
+
this.auditLog = auditLog;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if an action requires step-up authentication.
|
|
42
|
+
*/
|
|
43
|
+
requiresStepUp(action, sensitivityLevel) {
|
|
44
|
+
// Check explicit action list
|
|
45
|
+
if (this.policy.alwaysRequireActions.includes(action)) {
|
|
46
|
+
return { required: true, trigger: 'policy_rule' };
|
|
47
|
+
}
|
|
48
|
+
// Check sensitivity level
|
|
49
|
+
if (sensitivityLevel &&
|
|
50
|
+
this.policy.sensitivityLevels.includes(sensitivityLevel)) {
|
|
51
|
+
const trigger = sensitivityLevel === 'critical' ? 'sensitivity_critical' : 'sensitivity_high';
|
|
52
|
+
return { required: true, trigger };
|
|
53
|
+
}
|
|
54
|
+
return { required: false };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a step-up challenge for the human principal to approve.
|
|
58
|
+
*/
|
|
59
|
+
createChallenge(agentDid, principalDid, action, scope, trigger, actionDescription) {
|
|
60
|
+
// Enforce max pending limit
|
|
61
|
+
const agentPending = Array.from(this.pendingChallenges.values())
|
|
62
|
+
.filter(c => c.agentDid === agentDid).length;
|
|
63
|
+
if (agentPending >= this.policy.maxPendingChallenges) {
|
|
64
|
+
throw new Error(`Max pending challenges (${this.policy.maxPendingChallenges}) reached for agent`);
|
|
65
|
+
}
|
|
66
|
+
const nonce = toHex(secureRandom(16));
|
|
67
|
+
const challengeId = `stepup-${toHex(secureRandom(8))}`;
|
|
68
|
+
const now = new Date();
|
|
69
|
+
const challenge = {
|
|
70
|
+
challengeId,
|
|
71
|
+
agentDid,
|
|
72
|
+
principalDid,
|
|
73
|
+
actionDescription,
|
|
74
|
+
action,
|
|
75
|
+
scope,
|
|
76
|
+
triggerReason: trigger,
|
|
77
|
+
expiresAt: new Date(now.getTime() + this.policy.challengeTimeoutMs).toISOString(),
|
|
78
|
+
nonce,
|
|
79
|
+
createdAt: now.toISOString(),
|
|
80
|
+
};
|
|
81
|
+
this.pendingChallenges.set(challengeId, challenge);
|
|
82
|
+
return challenge;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Principal signs an approval for a challenge.
|
|
86
|
+
*/
|
|
87
|
+
async signApproval(keyProvider, principalKeyId, challenge, decision) {
|
|
88
|
+
const approvalData = textToBytes(`stepup-approval:${challenge.challengeId}:${challenge.principalDid}:${decision}:${challenge.nonce}`);
|
|
89
|
+
const sig = await keyProvider.sign(principalKeyId, approvalData);
|
|
90
|
+
return {
|
|
91
|
+
challengeId: challenge.challengeId,
|
|
92
|
+
principalDid: challenge.principalDid,
|
|
93
|
+
decision,
|
|
94
|
+
decidedAt: new Date().toISOString(),
|
|
95
|
+
signature: toBase64Url(sig),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Verify a step-up approval and consume the challenge.
|
|
100
|
+
*
|
|
101
|
+
* This is the critical gate: if the approval is invalid, expired,
|
|
102
|
+
* or replayed, the action MUST NOT proceed.
|
|
103
|
+
*/
|
|
104
|
+
async verifyApproval(approval) {
|
|
105
|
+
// Find the pending challenge
|
|
106
|
+
const challenge = this.pendingChallenges.get(approval.challengeId);
|
|
107
|
+
if (!challenge) {
|
|
108
|
+
return { approved: false, error: 'Challenge not found or already consumed' };
|
|
109
|
+
}
|
|
110
|
+
// Check nonce replay
|
|
111
|
+
if (this.usedNonces.has(challenge.nonce)) {
|
|
112
|
+
return { approved: false, error: 'Challenge nonce already used (replay)' };
|
|
113
|
+
}
|
|
114
|
+
// Check expiry
|
|
115
|
+
if (new Date(challenge.expiresAt) < new Date()) {
|
|
116
|
+
this.pendingChallenges.delete(approval.challengeId);
|
|
117
|
+
return { approved: false, error: 'Challenge expired' };
|
|
118
|
+
}
|
|
119
|
+
// Check principal matches
|
|
120
|
+
if (approval.principalDid !== challenge.principalDid) {
|
|
121
|
+
return { approved: false, error: 'Principal DID mismatch' };
|
|
122
|
+
}
|
|
123
|
+
// Verify signature
|
|
124
|
+
try {
|
|
125
|
+
const publicKey = didToPublicKey(approval.principalDid);
|
|
126
|
+
const approvalData = textToBytes(`stepup-approval:${challenge.challengeId}:${challenge.principalDid}:${approval.decision}:${challenge.nonce}`);
|
|
127
|
+
const sig = fromBase64Url(approval.signature);
|
|
128
|
+
const valid = await verify(sig, approvalData, publicKey);
|
|
129
|
+
if (!valid) {
|
|
130
|
+
return { approved: false, error: 'Invalid approval signature' };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
return { approved: false, error: `Signature verification failed: ${e.message}` };
|
|
135
|
+
}
|
|
136
|
+
// Consume the challenge (single-use)
|
|
137
|
+
this.pendingChallenges.delete(approval.challengeId);
|
|
138
|
+
this.usedNonces.add(challenge.nonce);
|
|
139
|
+
// Check the decision
|
|
140
|
+
if (approval.decision === 'denied') {
|
|
141
|
+
await this.auditLog?.log({
|
|
142
|
+
eventType: 'intent_rejected',
|
|
143
|
+
actorDid: approval.principalDid,
|
|
144
|
+
targetDid: challenge.agentDid,
|
|
145
|
+
result: 'failure',
|
|
146
|
+
reason: 'Step-up denied by principal',
|
|
147
|
+
metadata: {
|
|
148
|
+
challengeId: approval.challengeId,
|
|
149
|
+
action: challenge.action,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
return { approved: false, error: 'Denied by principal', challengeId: approval.challengeId };
|
|
153
|
+
}
|
|
154
|
+
await this.auditLog?.log({
|
|
155
|
+
eventType: 'intent_validated',
|
|
156
|
+
actorDid: approval.principalDid,
|
|
157
|
+
targetDid: challenge.agentDid,
|
|
158
|
+
result: 'success',
|
|
159
|
+
metadata: {
|
|
160
|
+
type: 'step_up_approval',
|
|
161
|
+
challengeId: approval.challengeId,
|
|
162
|
+
action: challenge.action,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
return { approved: true, challengeId: approval.challengeId };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get a pending challenge by ID.
|
|
169
|
+
*/
|
|
170
|
+
getChallenge(challengeId) {
|
|
171
|
+
return this.pendingChallenges.get(challengeId);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get count of pending challenges.
|
|
175
|
+
*/
|
|
176
|
+
getPendingCount() {
|
|
177
|
+
return this.pendingChallenges.size;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Cancel a pending challenge.
|
|
181
|
+
*/
|
|
182
|
+
cancelChallenge(challengeId) {
|
|
183
|
+
return this.pendingChallenges.delete(challengeId);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sentinel-atl/stepup",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Step-up authentication — re-prompt humans for sensitive agent actions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"lint": "tsc --noEmit",
|
|
18
|
+
"clean": "rm -rf dist"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@sentinel-atl/core": "*",
|
|
22
|
+
"@sentinel-atl/audit": "*"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"vitest": "^3.2.4",
|
|
26
|
+
"typescript": "^5.7.0"
|
|
27
|
+
},
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/sentinel-atl/project-sentinel.git",
|
|
32
|
+
"directory": "packages/stepup"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"ai-agent",
|
|
36
|
+
"trust",
|
|
37
|
+
"identity",
|
|
38
|
+
"did",
|
|
39
|
+
"verifiable-credentials",
|
|
40
|
+
"mcp",
|
|
41
|
+
"security"
|
|
42
|
+
],
|
|
43
|
+
"homepage": "https://github.com/sentinel-atl/project-sentinel#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/sentinel-atl/project-sentinel/issues"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
]
|
|
51
|
+
}
|