@private.me/xbind 1.2.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/AGENTS.md +778 -0
- package/LICENSE.md +27 -0
- package/README.md +400 -0
- package/dist-standalone/_deps/crypto/base64.d.ts +29 -0
- package/dist-standalone/_deps/crypto/base64.js +97 -0
- package/dist-standalone/_deps/crypto/cjs/base64.js +103 -0
- package/dist-standalone/_deps/crypto/cjs/errors.js +119 -0
- package/dist-standalone/_deps/crypto/cjs/hmac.js +71 -0
- package/dist-standalone/_deps/crypto/cjs/index.js +86 -0
- package/dist-standalone/_deps/crypto/cjs/padding.js +57 -0
- package/dist-standalone/_deps/crypto/cjs/share-header.js +68 -0
- package/dist-standalone/_deps/crypto/cjs/shares.js +152 -0
- package/dist-standalone/_deps/crypto/cjs/tlv.js +199 -0
- package/dist-standalone/_deps/crypto/cjs/uuid.js +61 -0
- package/dist-standalone/_deps/crypto/cjs/verify.js +24 -0
- package/dist-standalone/_deps/crypto/cjs/xorida.js +221 -0
- package/dist-standalone/_deps/crypto/errors.d.ts +51 -0
- package/dist-standalone/_deps/crypto/errors.js +109 -0
- package/dist-standalone/_deps/crypto/hmac.d.ts +39 -0
- package/dist-standalone/_deps/crypto/hmac.js +66 -0
- package/dist-standalone/_deps/crypto/index.d.ts +20 -0
- package/dist-standalone/_deps/crypto/index.js +45 -0
- package/dist-standalone/_deps/crypto/padding.d.ts +19 -0
- package/dist-standalone/_deps/crypto/padding.js +53 -0
- package/dist-standalone/_deps/crypto/share-header.d.ts +44 -0
- package/dist-standalone/_deps/crypto/share-header.js +63 -0
- package/dist-standalone/_deps/crypto/shares.d.ts +27 -0
- package/dist-standalone/_deps/crypto/shares.js +148 -0
- package/dist-standalone/_deps/crypto/tlv.d.ts +26 -0
- package/dist-standalone/_deps/crypto/tlv.js +195 -0
- package/dist-standalone/_deps/crypto/uuid.d.ts +22 -0
- package/dist-standalone/_deps/crypto/uuid.js +56 -0
- package/dist-standalone/_deps/crypto/verify.d.ts +15 -0
- package/dist-standalone/_deps/crypto/verify.js +15 -0
- package/dist-standalone/_deps/crypto/xorida.d.ts +44 -0
- package/dist-standalone/_deps/crypto/xorida.js +215 -0
- package/dist-standalone/_deps/mldsa-wasm/LICENSE +24 -0
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -0
- package/dist-standalone/_deps/mldsa-wasm/package.json +46 -0
- package/dist-standalone/_deps/mldsa-wasm/types/mldsa.d.ts +30 -0
- package/dist-standalone/_deps/shared/cjs/errors.js +582 -0
- package/dist-standalone/_deps/shared/cjs/index.js +492 -0
- package/dist-standalone/_deps/shared/cjs/package.json +1 -0
- package/dist-standalone/_deps/shared/cjs/types.js +403 -0
- package/dist-standalone/_deps/shared/errors.d.ts +48 -0
- package/dist-standalone/_deps/shared/errors.d.ts.map +1 -0
- package/dist-standalone/_deps/shared/errors.js +192 -0
- package/dist-standalone/_deps/shared/errors.js.map +1 -0
- package/dist-standalone/_deps/shared/index.d.ts +4 -0
- package/dist-standalone/_deps/shared/index.d.ts.map +1 -0
- package/dist-standalone/_deps/shared/index.js +78 -0
- package/dist-standalone/_deps/shared/index.js.map +1 -0
- package/dist-standalone/_deps/shared/types.d.ts +1097 -0
- package/dist-standalone/_deps/shared/types.d.ts.map +1 -0
- package/dist-standalone/_deps/shared/types.js +89 -0
- package/dist-standalone/_deps/shared/types.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts +115 -0
- package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts +13 -0
- package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/package.json +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts +39 -0
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +83 -0
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts +99 -0
- package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +143 -0
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts +32 -0
- package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +119 -0
- package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts +109 -0
- package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +8 -0
- package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/errors.d.ts +115 -0
- package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/errors.js +253 -0
- package/dist-standalone/_deps/ux-helpers/errors.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/index.d.ts +13 -0
- package/dist-standalone/_deps/ux-helpers/index.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/index.js +16 -0
- package/dist-standalone/_deps/ux-helpers/index.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/pagination.d.ts +39 -0
- package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/pagination.js +79 -0
- package/dist-standalone/_deps/ux-helpers/pagination.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/progress.d.ts +99 -0
- package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/progress.js +138 -0
- package/dist-standalone/_deps/ux-helpers/progress.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/search.d.ts +32 -0
- package/dist-standalone/_deps/ux-helpers/search.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/search.js +116 -0
- package/dist-standalone/_deps/ux-helpers/search.js.map +1 -0
- package/dist-standalone/_deps/ux-helpers/types.d.ts +109 -0
- package/dist-standalone/_deps/ux-helpers/types.d.ts.map +1 -0
- package/dist-standalone/_deps/ux-helpers/types.js +7 -0
- package/dist-standalone/_deps/ux-helpers/types.js.map +1 -0
- package/dist-standalone/_deps/xchange/auto-accept.d.ts +127 -0
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/package.json +1 -0
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -0
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -0
- package/dist-standalone/_deps/xchange/errors.d.ts +69 -0
- package/dist-standalone/_deps/xchange/errors.js +1 -0
- package/dist-standalone/_deps/xchange/index.d.ts +15 -0
- package/dist-standalone/_deps/xchange/index.js +1 -0
- package/dist-standalone/_deps/xchange/invite-client.d.ts +178 -0
- package/dist-standalone/_deps/xchange/invite-client.js +1 -0
- package/dist-standalone/_deps/xchange/lazy-init.d.ts +176 -0
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -0
- package/dist-standalone/_deps/xchange/trust-integration.d.ts +102 -0
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -0
- package/dist-standalone/_deps/xchange/xchange.d.ts +60 -0
- package/dist-standalone/_deps/xchange/xchange.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/package.json +1 -0
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -0
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -0
- package/dist-standalone/_deps/xregistry/discovery.d.ts +126 -0
- package/dist-standalone/_deps/xregistry/discovery.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/discovery.js +1 -0
- package/dist-standalone/_deps/xregistry/discovery.js.map +1 -0
- package/dist-standalone/_deps/xregistry/errors.d.ts +41 -0
- package/dist-standalone/_deps/xregistry/errors.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/errors.js +1 -0
- package/dist-standalone/_deps/xregistry/errors.js.map +1 -0
- package/dist-standalone/_deps/xregistry/index.d.ts +8 -0
- package/dist-standalone/_deps/xregistry/index.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/index.js +1 -0
- package/dist-standalone/_deps/xregistry/index.js.map +1 -0
- package/dist-standalone/_deps/xregistry/registry.d.ts +85 -0
- package/dist-standalone/_deps/xregistry/registry.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/registry.js +1 -0
- package/dist-standalone/_deps/xregistry/registry.js.map +1 -0
- package/dist-standalone/_deps/xregistry/schema.d.ts +81 -0
- package/dist-standalone/_deps/xregistry/schema.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/schema.js +1 -0
- package/dist-standalone/_deps/xregistry/schema.js.map +1 -0
- package/dist-standalone/_deps/xregistry/types.d.ts +95 -0
- package/dist-standalone/_deps/xregistry/types.d.ts.map +1 -0
- package/dist-standalone/_deps/xregistry/types.js +1 -0
- package/dist-standalone/_deps/xregistry/types.js.map +1 -0
- package/dist-standalone/agent-call.d.ts +286 -0
- package/dist-standalone/agent-call.js +642 -0
- package/dist-standalone/agent-sdk.d.ts +207 -0
- package/dist-standalone/agent-sdk.js +328 -0
- package/dist-standalone/agent.d.ts +670 -0
- package/dist-standalone/agent.js +1529 -0
- package/dist-standalone/approval.d.ts +145 -0
- package/dist-standalone/approval.js +193 -0
- package/dist-standalone/auth.d.ts +75 -0
- package/dist-standalone/auth.js +219 -0
- package/dist-standalone/auto-accept.d.ts +102 -0
- package/dist-standalone/auto-accept.js +229 -0
- package/dist-standalone/backup-config.d.ts +150 -0
- package/dist-standalone/backup-config.js +201 -0
- package/dist-standalone/checkpoint.d.ts +125 -0
- package/dist-standalone/checkpoint.js +186 -0
- package/dist-standalone/cjs/agent-call.js +651 -0
- package/dist-standalone/cjs/agent-sdk.js +332 -0
- package/dist-standalone/cjs/agent.js +1566 -0
- package/dist-standalone/cjs/approval.js +199 -0
- package/dist-standalone/cjs/auth.js +225 -0
- package/dist-standalone/cjs/auto-accept.js +233 -0
- package/dist-standalone/cjs/backup-config.js +207 -0
- package/dist-standalone/cjs/checkpoint.js +193 -0
- package/dist-standalone/cjs/cli/init.js +487 -0
- package/dist-standalone/cjs/connect.js +312 -0
- package/dist-standalone/cjs/did-document.js +101 -0
- package/dist-standalone/cjs/did-privateme.js +130 -0
- package/dist-standalone/cjs/did-web.js +201 -0
- package/dist-standalone/cjs/discovery.js +462 -0
- package/dist-standalone/cjs/dual-mode.js +251 -0
- package/dist-standalone/cjs/email-templates.js +313 -0
- package/dist-standalone/cjs/email-transport.js +239 -0
- package/dist-standalone/cjs/envelope.js +510 -0
- package/dist-standalone/cjs/errors.js +562 -0
- package/dist-standalone/cjs/gateway-state.js +55 -0
- package/dist-standalone/cjs/gateway-transport.js +120 -0
- package/dist-standalone/cjs/guardrails.js +223 -0
- package/dist-standalone/cjs/http-compat.js +272 -0
- package/dist-standalone/cjs/identity.js +541 -0
- package/dist-standalone/cjs/index.js +224 -0
- package/dist-standalone/cjs/invitation.js +421 -0
- package/dist-standalone/cjs/invite.js +328 -0
- package/dist-standalone/cjs/key-agreement.js +246 -0
- package/dist-standalone/cjs/lazy-init.js +300 -0
- package/dist-standalone/cjs/mdns-discovery.js +202 -0
- package/dist-standalone/cjs/nonce-store.js +66 -0
- package/dist-standalone/cjs/package.json +3 -0
- package/dist-standalone/cjs/pairing-manager.js +223 -0
- package/dist-standalone/cjs/policy.js +320 -0
- package/dist-standalone/cjs/redis-nonce-store.js +76 -0
- package/dist-standalone/cjs/registry-middleware.js +50 -0
- package/dist-standalone/cjs/retry-transport.js +102 -0
- package/dist-standalone/cjs/security-policy.js +204 -0
- package/dist-standalone/cjs/split-channel.js +177 -0
- package/dist-standalone/cjs/subscription-proof.js +230 -0
- package/dist-standalone/cjs/succession.js +148 -0
- package/dist-standalone/cjs/transport.js +63 -0
- package/dist-standalone/cjs/trust-registry.js +742 -0
- package/dist-standalone/cjs/verify.js +25 -0
- package/dist-standalone/cjs/xfetch.js +252 -0
- package/dist-standalone/cli/init.d.ts +63 -0
- package/dist-standalone/cli/init.js +450 -0
- package/dist-standalone/connect.d.ts +143 -0
- package/dist-standalone/connect.js +274 -0
- package/dist-standalone/did-document.d.ts +65 -0
- package/dist-standalone/did-document.js +96 -0
- package/dist-standalone/did-privateme.d.ts +70 -0
- package/dist-standalone/did-privateme.js +121 -0
- package/dist-standalone/did-web.d.ts +73 -0
- package/dist-standalone/did-web.js +196 -0
- package/dist-standalone/discovery.d.ts +176 -0
- package/dist-standalone/discovery.js +458 -0
- package/dist-standalone/dual-mode.d.ts +145 -0
- package/dist-standalone/dual-mode.js +247 -0
- package/dist-standalone/email-templates.d.ts +41 -0
- package/dist-standalone/email-templates.js +309 -0
- package/dist-standalone/email-transport.d.ts +139 -0
- package/dist-standalone/email-transport.js +232 -0
- package/dist-standalone/envelope.d.ts +288 -0
- package/dist-standalone/envelope.js +497 -0
- package/dist-standalone/errors.d.ts +74 -0
- package/dist-standalone/errors.js +548 -0
- package/dist-standalone/gateway-state.d.ts +32 -0
- package/dist-standalone/gateway-state.js +51 -0
- package/dist-standalone/gateway-transport.d.ts +59 -0
- package/dist-standalone/gateway-transport.js +116 -0
- package/dist-standalone/guardrails.d.ts +136 -0
- package/dist-standalone/guardrails.js +216 -0
- package/dist-standalone/http-compat.d.ts +150 -0
- package/dist-standalone/http-compat.js +267 -0
- package/dist-standalone/identity.d.ts +176 -0
- package/dist-standalone/identity.js +516 -0
- package/dist-standalone/index.d.ts +83 -0
- package/dist-standalone/index.js +51 -0
- package/dist-standalone/invitation.d.ts +211 -0
- package/dist-standalone/invitation.js +415 -0
- package/dist-standalone/invite.d.ts +192 -0
- package/dist-standalone/invite.js +324 -0
- package/dist-standalone/key-agreement.d.ts +122 -0
- package/dist-standalone/key-agreement.js +236 -0
- package/dist-standalone/lazy-init.d.ts +167 -0
- package/dist-standalone/lazy-init.js +295 -0
- package/dist-standalone/mdns-discovery.d.ts +117 -0
- package/dist-standalone/mdns-discovery.js +195 -0
- package/dist-standalone/nonce-store.d.ts +39 -0
- package/dist-standalone/nonce-store.js +62 -0
- package/dist-standalone/package.json +11 -0
- package/dist-standalone/pairing-manager.d.ts +147 -0
- package/dist-standalone/pairing-manager.js +219 -0
- package/dist-standalone/policy.d.ts +150 -0
- package/dist-standalone/policy.js +315 -0
- package/dist-standalone/redis-nonce-store.d.ts +93 -0
- package/dist-standalone/redis-nonce-store.js +72 -0
- package/dist-standalone/registry-middleware.d.ts +38 -0
- package/dist-standalone/registry-middleware.js +47 -0
- package/dist-standalone/retry-transport.d.ts +76 -0
- package/dist-standalone/retry-transport.js +98 -0
- package/dist-standalone/security-policy.d.ts +146 -0
- package/dist-standalone/security-policy.js +198 -0
- package/dist-standalone/split-channel.d.ts +69 -0
- package/dist-standalone/split-channel.js +171 -0
- package/dist-standalone/subscription-proof.d.ts +103 -0
- package/dist-standalone/subscription-proof.js +224 -0
- package/dist-standalone/succession.d.ts +57 -0
- package/dist-standalone/succession.js +142 -0
- package/dist-standalone/transport.d.ts +50 -0
- package/dist-standalone/transport.js +59 -0
- package/dist-standalone/trust-registry.d.ts +286 -0
- package/dist-standalone/trust-registry.js +702 -0
- package/dist-standalone/verify.d.ts +16 -0
- package/dist-standalone/verify.js +16 -0
- package/dist-standalone/xfetch.d.ts +129 -0
- package/dist-standalone/xfetch.js +247 -0
- package/llms.txt +800 -0
- package/package.json +79 -0
- package/share1.dat +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module approval
|
|
3
|
+
* OAuth-style approval flow for agents
|
|
4
|
+
*
|
|
5
|
+
* Enterprise agents require explicit user consent before performing
|
|
6
|
+
* sensitive operations. This module implements OAuth-style consent
|
|
7
|
+
* screens and approval tokens.
|
|
8
|
+
*/
|
|
9
|
+
import type { Result } from '@private.me/shared';
|
|
10
|
+
import type { PolicyConstraints } from './agent-call.js';
|
|
11
|
+
/**
|
|
12
|
+
* Approval request options
|
|
13
|
+
*/
|
|
14
|
+
export interface ApprovalOptions {
|
|
15
|
+
/** Agent DID requesting approval */
|
|
16
|
+
readonly agentDid: string;
|
|
17
|
+
/** Scopes being requested (e.g., ["payments:createCharge", "invoices:create"]) */
|
|
18
|
+
readonly scopes: string[];
|
|
19
|
+
/** Policy limits to enforce */
|
|
20
|
+
readonly limits: PolicyConstraints['limits'];
|
|
21
|
+
/** Duration of approval (ISO 8601 duration or ms) */
|
|
22
|
+
readonly duration: string | number;
|
|
23
|
+
/** Human-readable description of what the agent will do */
|
|
24
|
+
readonly description?: string;
|
|
25
|
+
/** Metadata for audit logging */
|
|
26
|
+
readonly metadata?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Approval token (returned after user consent)
|
|
30
|
+
*/
|
|
31
|
+
export interface ApprovalToken {
|
|
32
|
+
/** Unique approval ID */
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** Agent DID this approval is for */
|
|
35
|
+
readonly agentDid: string;
|
|
36
|
+
/** Granted scopes */
|
|
37
|
+
readonly scopes: string[];
|
|
38
|
+
/** Granted limits */
|
|
39
|
+
readonly limits: PolicyConstraints['limits'];
|
|
40
|
+
/** Expiration timestamp (ms since epoch) */
|
|
41
|
+
readonly expiresAt: number;
|
|
42
|
+
/** When this approval was created */
|
|
43
|
+
readonly createdAt: number;
|
|
44
|
+
/** Whether this approval is still valid */
|
|
45
|
+
readonly valid: boolean;
|
|
46
|
+
/** JWT-style signature (Ed25519) */
|
|
47
|
+
readonly signature: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Approval result
|
|
51
|
+
*/
|
|
52
|
+
export interface ApprovalResult {
|
|
53
|
+
/** Whether user approved */
|
|
54
|
+
readonly approved: boolean;
|
|
55
|
+
/** Approval token (if approved) */
|
|
56
|
+
readonly token?: ApprovalToken;
|
|
57
|
+
/** Reason for denial (if not approved) */
|
|
58
|
+
readonly reason?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Approval error codes
|
|
62
|
+
*/
|
|
63
|
+
export declare enum ApprovalErrorCode {
|
|
64
|
+
USER_DENIED = "APPROVAL_USER_DENIED",
|
|
65
|
+
TIMEOUT = "APPROVAL_TIMEOUT",
|
|
66
|
+
INVALID_DURATION = "APPROVAL_INVALID_DURATION",
|
|
67
|
+
SIGNATURE_FAILED = "APPROVAL_SIGNATURE_FAILED",
|
|
68
|
+
TOKEN_EXPIRED = "APPROVAL_TOKEN_EXPIRED",
|
|
69
|
+
TOKEN_REVOKED = "APPROVAL_TOKEN_REVOKED",
|
|
70
|
+
INVALID_TOKEN = "APPROVAL_INVALID_TOKEN"
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Approval error
|
|
74
|
+
*/
|
|
75
|
+
export declare class ApprovalError extends Error {
|
|
76
|
+
readonly code: ApprovalErrorCode;
|
|
77
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
78
|
+
constructor(code: ApprovalErrorCode, message: string, details?: Record<string, unknown> | undefined);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Approval presenter (UI abstraction)
|
|
82
|
+
*
|
|
83
|
+
* Implementations display consent screen to user and return their decision.
|
|
84
|
+
* This allows different UIs (CLI, web, mobile) to plug in.
|
|
85
|
+
*/
|
|
86
|
+
export interface ApprovalPresenter {
|
|
87
|
+
/**
|
|
88
|
+
* Present approval request to user
|
|
89
|
+
*
|
|
90
|
+
* @param options - Approval options
|
|
91
|
+
* @returns User's decision
|
|
92
|
+
*/
|
|
93
|
+
present(options: ApprovalOptions): Promise<Result<ApprovalResult, ApprovalError>>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* CLI approval presenter (prints to console, reads stdin)
|
|
97
|
+
* This is a legitimate CLI interface - console output is intentional
|
|
98
|
+
*/
|
|
99
|
+
export declare class CLIApprovalPresenter implements ApprovalPresenter {
|
|
100
|
+
present(options: ApprovalOptions): Promise<Result<ApprovalResult, ApprovalError>>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Approval flow coordinator
|
|
104
|
+
*
|
|
105
|
+
* Orchestrates the consent flow: present to user → sign token → track expiry
|
|
106
|
+
*/
|
|
107
|
+
export declare class ApprovalFlow {
|
|
108
|
+
private readonly presenter;
|
|
109
|
+
private readonly tokens;
|
|
110
|
+
constructor(options?: {
|
|
111
|
+
readonly presenter?: ApprovalPresenter;
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Request approval from user
|
|
115
|
+
*
|
|
116
|
+
* @param options - Approval request options
|
|
117
|
+
* @returns Approval result with token (if approved)
|
|
118
|
+
*/
|
|
119
|
+
requestApproval(options: ApprovalOptions): Promise<Result<ApprovalResult, ApprovalError>>;
|
|
120
|
+
/**
|
|
121
|
+
* Verify an approval token
|
|
122
|
+
*
|
|
123
|
+
* @param tokenId - Token ID to verify
|
|
124
|
+
* @returns Token if valid, error otherwise
|
|
125
|
+
*/
|
|
126
|
+
verifyToken(tokenId: string): Result<ApprovalToken, ApprovalError>;
|
|
127
|
+
/**
|
|
128
|
+
* Revoke an approval token
|
|
129
|
+
*
|
|
130
|
+
* @param tokenId - Token ID to revoke
|
|
131
|
+
*/
|
|
132
|
+
revokeToken(tokenId: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Parse duration string to milliseconds
|
|
135
|
+
*/
|
|
136
|
+
private parseDuration;
|
|
137
|
+
/**
|
|
138
|
+
* Generate a unique token ID
|
|
139
|
+
*/
|
|
140
|
+
private generateTokenId;
|
|
141
|
+
/**
|
|
142
|
+
* Sign an approval token (stub - production would use Ed25519)
|
|
143
|
+
*/
|
|
144
|
+
private signToken;
|
|
145
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module approval
|
|
3
|
+
* OAuth-style approval flow for agents
|
|
4
|
+
*
|
|
5
|
+
* Enterprise agents require explicit user consent before performing
|
|
6
|
+
* sensitive operations. This module implements OAuth-style consent
|
|
7
|
+
* screens and approval tokens.
|
|
8
|
+
*/
|
|
9
|
+
import { ok, err } from"./_deps/shared/index.js";
|
|
10
|
+
/**
|
|
11
|
+
* Approval error codes
|
|
12
|
+
*/
|
|
13
|
+
export var ApprovalErrorCode;
|
|
14
|
+
(function (ApprovalErrorCode) {
|
|
15
|
+
ApprovalErrorCode["USER_DENIED"] = "APPROVAL_USER_DENIED";
|
|
16
|
+
ApprovalErrorCode["TIMEOUT"] = "APPROVAL_TIMEOUT";
|
|
17
|
+
ApprovalErrorCode["INVALID_DURATION"] = "APPROVAL_INVALID_DURATION";
|
|
18
|
+
ApprovalErrorCode["SIGNATURE_FAILED"] = "APPROVAL_SIGNATURE_FAILED";
|
|
19
|
+
ApprovalErrorCode["TOKEN_EXPIRED"] = "APPROVAL_TOKEN_EXPIRED";
|
|
20
|
+
ApprovalErrorCode["TOKEN_REVOKED"] = "APPROVAL_TOKEN_REVOKED";
|
|
21
|
+
ApprovalErrorCode["INVALID_TOKEN"] = "APPROVAL_INVALID_TOKEN";
|
|
22
|
+
})(ApprovalErrorCode || (ApprovalErrorCode = {}));
|
|
23
|
+
/**
|
|
24
|
+
* Approval error
|
|
25
|
+
*/
|
|
26
|
+
export class ApprovalError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
details;
|
|
29
|
+
constructor(code, message, details) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.details = details;
|
|
33
|
+
this.name = 'ApprovalError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* CLI approval presenter (prints to console, reads stdin)
|
|
38
|
+
* This is a legitimate CLI interface - console output is intentional
|
|
39
|
+
*/
|
|
40
|
+
export class CLIApprovalPresenter {
|
|
41
|
+
/* eslint-disable no-console */
|
|
42
|
+
async present(options) {
|
|
43
|
+
console.log('\n=== AGENT APPROVAL REQUEST ===');
|
|
44
|
+
console.log(`Agent: ${options.agentDid}`);
|
|
45
|
+
console.log(`Duration: ${options.duration}`);
|
|
46
|
+
console.log('\nRequested Scopes:');
|
|
47
|
+
for (const scope of options.scopes) {
|
|
48
|
+
console.log(` - ${scope}`);
|
|
49
|
+
}
|
|
50
|
+
if (options.limits) {
|
|
51
|
+
console.log('\nPolicy Limits:');
|
|
52
|
+
if (options.limits.amountPerTxn) {
|
|
53
|
+
console.log(` - Max per transaction: $${options.limits.amountPerTxn.toLocaleString()}`);
|
|
54
|
+
}
|
|
55
|
+
if (options.limits.dailyAmount) {
|
|
56
|
+
console.log(` - Daily limit: $${options.limits.dailyAmount.toLocaleString()}`);
|
|
57
|
+
}
|
|
58
|
+
if (options.limits.callsPerMinute) {
|
|
59
|
+
console.log(` - Rate limit: ${options.limits.callsPerMinute} calls/minute`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (options.description) {
|
|
63
|
+
console.log(`\nDescription: ${options.description}`);
|
|
64
|
+
}
|
|
65
|
+
console.log('\n[This is a mock presenter - auto-approving for development]');
|
|
66
|
+
console.log('In production, this would prompt for user input.\n');
|
|
67
|
+
// Auto-approve for development (production would prompt for y/n)
|
|
68
|
+
return ok({
|
|
69
|
+
approved: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Approval flow coordinator
|
|
75
|
+
*
|
|
76
|
+
* Orchestrates the consent flow: present to user → sign token → track expiry
|
|
77
|
+
*/
|
|
78
|
+
export class ApprovalFlow {
|
|
79
|
+
presenter;
|
|
80
|
+
tokens = new Map();
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
this.presenter = options.presenter ?? new CLIApprovalPresenter();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Request approval from user
|
|
86
|
+
*
|
|
87
|
+
* @param options - Approval request options
|
|
88
|
+
* @returns Approval result with token (if approved)
|
|
89
|
+
*/
|
|
90
|
+
async requestApproval(options) {
|
|
91
|
+
// Validate duration
|
|
92
|
+
const durationMs = this.parseDuration(options.duration);
|
|
93
|
+
if (durationMs <= 0) {
|
|
94
|
+
return err(new ApprovalError(ApprovalErrorCode.INVALID_DURATION, `Invalid duration: ${options.duration}`));
|
|
95
|
+
}
|
|
96
|
+
// Present to user
|
|
97
|
+
const result = await this.presenter.present(options);
|
|
98
|
+
if (!result.ok)
|
|
99
|
+
return result;
|
|
100
|
+
if (!result.value.approved) {
|
|
101
|
+
return ok({
|
|
102
|
+
approved: false,
|
|
103
|
+
reason: result.value.reason ?? 'User denied approval',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Generate approval token
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
const token = {
|
|
109
|
+
id: this.generateTokenId(),
|
|
110
|
+
agentDid: options.agentDid,
|
|
111
|
+
scopes: options.scopes,
|
|
112
|
+
limits: options.limits,
|
|
113
|
+
expiresAt: now + durationMs,
|
|
114
|
+
createdAt: now,
|
|
115
|
+
valid: true,
|
|
116
|
+
signature: await this.signToken(options.agentDid, options.scopes, now + durationMs),
|
|
117
|
+
};
|
|
118
|
+
// Store token
|
|
119
|
+
this.tokens.set(token.id, token);
|
|
120
|
+
return ok({
|
|
121
|
+
approved: true,
|
|
122
|
+
token,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Verify an approval token
|
|
127
|
+
*
|
|
128
|
+
* @param tokenId - Token ID to verify
|
|
129
|
+
* @returns Token if valid, error otherwise
|
|
130
|
+
*/
|
|
131
|
+
verifyToken(tokenId) {
|
|
132
|
+
const token = this.tokens.get(tokenId);
|
|
133
|
+
if (!token) {
|
|
134
|
+
return err(new ApprovalError(ApprovalErrorCode.INVALID_TOKEN, `Token ${tokenId} not found`));
|
|
135
|
+
}
|
|
136
|
+
if (!token.valid) {
|
|
137
|
+
return err(new ApprovalError(ApprovalErrorCode.TOKEN_REVOKED, `Token ${tokenId} has been revoked`));
|
|
138
|
+
}
|
|
139
|
+
if (Date.now() > token.expiresAt) {
|
|
140
|
+
return err(new ApprovalError(ApprovalErrorCode.TOKEN_EXPIRED, `Token ${tokenId} expired at ${new Date(token.expiresAt).toISOString()}`));
|
|
141
|
+
}
|
|
142
|
+
return ok(token);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Revoke an approval token
|
|
146
|
+
*
|
|
147
|
+
* @param tokenId - Token ID to revoke
|
|
148
|
+
*/
|
|
149
|
+
revokeToken(tokenId) {
|
|
150
|
+
const token = this.tokens.get(tokenId);
|
|
151
|
+
if (token) {
|
|
152
|
+
this.tokens.set(tokenId, { ...token, valid: false });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Parse duration string to milliseconds
|
|
157
|
+
*/
|
|
158
|
+
parseDuration(duration) {
|
|
159
|
+
if (typeof duration === 'number')
|
|
160
|
+
return duration;
|
|
161
|
+
// Parse ISO 8601 duration or simple format
|
|
162
|
+
const matches = duration.match(/^(\d+)([smhd])$/);
|
|
163
|
+
if (!matches)
|
|
164
|
+
return -1;
|
|
165
|
+
const value = parseInt(matches[1] ?? '0', 10);
|
|
166
|
+
const unit = matches[2];
|
|
167
|
+
switch (unit) {
|
|
168
|
+
case 's': return value * 1000;
|
|
169
|
+
case 'm': return value * 60 * 1000;
|
|
170
|
+
case 'h': return value * 60 * 60 * 1000;
|
|
171
|
+
case 'd': return value * 24 * 60 * 60 * 1000;
|
|
172
|
+
default: return -1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Generate a unique token ID
|
|
177
|
+
*/
|
|
178
|
+
generateTokenId() {
|
|
179
|
+
const randomBytes = new Uint8Array(12);
|
|
180
|
+
crypto.getRandomValues(randomBytes);
|
|
181
|
+
const randomString = Array.from(randomBytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
182
|
+
return `appr_${Date.now()}_${randomString}`;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Sign an approval token (stub - production would use Ed25519)
|
|
186
|
+
*/
|
|
187
|
+
async signToken(agentDid, scopes, expiresAt) {
|
|
188
|
+
// Production would use Ed25519 signature over JSON.stringify({ agentDid, scopes, expiresAt })
|
|
189
|
+
// For now, return a mock signature
|
|
190
|
+
const payload = JSON.stringify({ agentDid, scopes, expiresAt });
|
|
191
|
+
return btoa(payload).substring(0, 32);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xlock auth challenge module for Agent SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides requestAuth(), respondToChallenge(), and onChallenge()
|
|
5
|
+
* functions that work with an Agent instance and the XBind gateway.
|
|
6
|
+
*
|
|
7
|
+
* These functions are also re-exported as thin methods on the Agent class.
|
|
8
|
+
*/
|
|
9
|
+
import type { Result, AuthRequest, AuthResult, IncomingAuthChallenge, AuthChallengeErrorCode } from '@private.me/shared';
|
|
10
|
+
import type { Agent } from './agent.js';
|
|
11
|
+
/** Options for the gateway connection used by auth functions. */
|
|
12
|
+
export interface AuthGatewayOptions {
|
|
13
|
+
/** Gateway base URL (e.g. 'https://api.xail.io'). */
|
|
14
|
+
readonly gatewayUrl: string;
|
|
15
|
+
/** Bearer token for status polling (HMAC-SHA256 of sender DID). */
|
|
16
|
+
readonly accessToken: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create an auth challenge and poll until the user responds.
|
|
20
|
+
*
|
|
21
|
+
* Sends a challenge via the XBind gateway, then polls the status
|
|
22
|
+
* endpoint until the challenge is approved, denied, or expires.
|
|
23
|
+
*
|
|
24
|
+
* @param agent - The Agent requesting authorization.
|
|
25
|
+
* @param request - Auth request details (recipient DID, action, metadata).
|
|
26
|
+
* @param gateway - Gateway connection options.
|
|
27
|
+
* @returns Auth result (approved/denied) or error.
|
|
28
|
+
*/
|
|
29
|
+
export declare function requestAuth(agent: Agent, request: AuthRequest, gateway: AuthGatewayOptions): Promise<Result<AuthResult, AuthChallengeErrorCode>>;
|
|
30
|
+
/**
|
|
31
|
+
* Respond to an incoming auth challenge (approve or deny).
|
|
32
|
+
*
|
|
33
|
+
* Sends a signed response envelope to the gateway.
|
|
34
|
+
*
|
|
35
|
+
* @param agent - The Agent responding to the challenge.
|
|
36
|
+
* @param challengeId - The challenge ID to respond to.
|
|
37
|
+
* @param approved - Whether the user approved the challenge.
|
|
38
|
+
* @param gateway - Gateway connection options.
|
|
39
|
+
* @returns Success or error.
|
|
40
|
+
*/
|
|
41
|
+
export declare function respondToChallenge(agent: Agent, challengeId: string, approved: boolean, gateway: AuthGatewayOptions): Promise<Result<void, AuthChallengeErrorCode>>;
|
|
42
|
+
/**
|
|
43
|
+
* Register a callback for incoming auth challenges via WebSocket.
|
|
44
|
+
*
|
|
45
|
+
* The callback fires when an `auth:challenge` message arrives
|
|
46
|
+
* over the gateway WebSocket connection.
|
|
47
|
+
*
|
|
48
|
+
* @param ws - The WebSocket connection to the gateway.
|
|
49
|
+
* @param callback - Handler for incoming challenges.
|
|
50
|
+
* @returns Cleanup function to unregister the listener.
|
|
51
|
+
*/
|
|
52
|
+
export declare function onChallenge(ws: {
|
|
53
|
+
addEventListener: (type: string, handler: (event: {
|
|
54
|
+
data: string;
|
|
55
|
+
}) => void) => void;
|
|
56
|
+
removeEventListener: (type: string, handler: (event: {
|
|
57
|
+
data: string;
|
|
58
|
+
}) => void) => void;
|
|
59
|
+
}, callback: (challenge: IncomingAuthChallenge) => void): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Generate a registration QR code URI for TOTP migration.
|
|
62
|
+
*
|
|
63
|
+
* Creates a `xlock://register` URI that replaces `otpauth://` for
|
|
64
|
+
* asymmetric DID-based registration.
|
|
65
|
+
*
|
|
66
|
+
* @param options - Registration options.
|
|
67
|
+
* @returns The registration URI string.
|
|
68
|
+
*/
|
|
69
|
+
export declare function generateRegistrationQR(options: {
|
|
70
|
+
readonly appName: string;
|
|
71
|
+
readonly appDid: string;
|
|
72
|
+
readonly registryUrl: string;
|
|
73
|
+
readonly callbackUrl: string;
|
|
74
|
+
readonly appIcon?: string;
|
|
75
|
+
}): string;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xlock auth challenge module for Agent SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides requestAuth(), respondToChallenge(), and onChallenge()
|
|
5
|
+
* functions that work with an Agent instance and the XBind gateway.
|
|
6
|
+
*
|
|
7
|
+
* These functions are also re-exported as thin methods on the Agent class.
|
|
8
|
+
*/
|
|
9
|
+
import { ok, err } from"./_deps/shared/index.js";
|
|
10
|
+
import { createSignedEnvelope } from './envelope.js';
|
|
11
|
+
/** Default poll interval for challenge status. */
|
|
12
|
+
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
13
|
+
/** Default TTL for challenges (5 minutes). */
|
|
14
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
15
|
+
/** Max poll iterations (TTL / pollInterval + safety margin). */
|
|
16
|
+
const MAX_POLL_ITERATIONS = 200;
|
|
17
|
+
/**
|
|
18
|
+
* Create an auth challenge and poll until the user responds.
|
|
19
|
+
*
|
|
20
|
+
* Sends a challenge via the XBind gateway, then polls the status
|
|
21
|
+
* endpoint until the challenge is approved, denied, or expires.
|
|
22
|
+
*
|
|
23
|
+
* @param agent - The Agent requesting authorization.
|
|
24
|
+
* @param request - Auth request details (recipient DID, action, metadata).
|
|
25
|
+
* @param gateway - Gateway connection options.
|
|
26
|
+
* @returns Auth result (approved/denied) or error.
|
|
27
|
+
*/
|
|
28
|
+
export async function requestAuth(agent, request, gateway) {
|
|
29
|
+
const ttlMs = request.ttlMs ?? DEFAULT_TTL_MS;
|
|
30
|
+
const pollIntervalMs = request.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
31
|
+
// Build the challenge payload
|
|
32
|
+
const payload = {
|
|
33
|
+
recipientDid: request.to,
|
|
34
|
+
action: request.action,
|
|
35
|
+
metadata: request.metadata ?? {},
|
|
36
|
+
ttlMs,
|
|
37
|
+
};
|
|
38
|
+
// Create a signed envelope wrapping the challenge request
|
|
39
|
+
const envelopeResult = await createSignedEnvelope({
|
|
40
|
+
senderDid: agent.did,
|
|
41
|
+
recipientDid: request.to,
|
|
42
|
+
scope: 'xlock:challenge',
|
|
43
|
+
plaintext: new TextEncoder().encode(JSON.stringify(payload)),
|
|
44
|
+
privateKey: agent.identity.privateKey,
|
|
45
|
+
});
|
|
46
|
+
if (!envelopeResult.ok) {
|
|
47
|
+
return err('INVALID_REQUEST');
|
|
48
|
+
}
|
|
49
|
+
// POST to gateway
|
|
50
|
+
let challengeId;
|
|
51
|
+
let expiresAt;
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`${gateway.gatewayUrl}/gateway/auth/challenge`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(envelopeResult.value),
|
|
57
|
+
});
|
|
58
|
+
if (response.status === 429)
|
|
59
|
+
return err('RATE_LIMITED');
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const body = await response.json().catch(() => null);
|
|
62
|
+
const code = body
|
|
63
|
+
?.error?.code;
|
|
64
|
+
return err(code ?? 'INVALID_REQUEST');
|
|
65
|
+
}
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
challengeId = data.challengeId;
|
|
68
|
+
expiresAt = data.expiresAt;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return err('INVALID_REQUEST');
|
|
72
|
+
}
|
|
73
|
+
// Poll for status
|
|
74
|
+
return pollChallengeStatus(challengeId, expiresAt, pollIntervalMs, gateway);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Respond to an incoming auth challenge (approve or deny).
|
|
78
|
+
*
|
|
79
|
+
* Sends a signed response envelope to the gateway.
|
|
80
|
+
*
|
|
81
|
+
* @param agent - The Agent responding to the challenge.
|
|
82
|
+
* @param challengeId - The challenge ID to respond to.
|
|
83
|
+
* @param approved - Whether the user approved the challenge.
|
|
84
|
+
* @param gateway - Gateway connection options.
|
|
85
|
+
* @returns Success or error.
|
|
86
|
+
*/
|
|
87
|
+
export async function respondToChallenge(agent, challengeId, approved, gateway) {
|
|
88
|
+
const payload = {
|
|
89
|
+
challengeId,
|
|
90
|
+
approved,
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
// Create a signed envelope for the response
|
|
94
|
+
const envelopeResult = await createSignedEnvelope({
|
|
95
|
+
senderDid: agent.did,
|
|
96
|
+
recipientDid: agent.did, // Self-addressed (gateway verifies recipient match)
|
|
97
|
+
scope: 'xlock:respond',
|
|
98
|
+
plaintext: new TextEncoder().encode(JSON.stringify(payload)),
|
|
99
|
+
privateKey: agent.identity.privateKey,
|
|
100
|
+
});
|
|
101
|
+
if (!envelopeResult.ok) {
|
|
102
|
+
return err('INVALID_REQUEST');
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(`${gateway.gatewayUrl}/gateway/auth/respond`, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: { 'Content-Type': 'application/json' },
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
...envelopeResult.value,
|
|
110
|
+
// Include response fields at top level for the server
|
|
111
|
+
challengeId,
|
|
112
|
+
approved,
|
|
113
|
+
responseEnvelope: envelopeResult.value,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const body = await response.json().catch(() => null);
|
|
118
|
+
const code = body
|
|
119
|
+
?.error?.code;
|
|
120
|
+
return err(code ?? 'INVALID_REQUEST');
|
|
121
|
+
}
|
|
122
|
+
return ok(undefined);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return err('INVALID_REQUEST');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Register a callback for incoming auth challenges via WebSocket.
|
|
130
|
+
*
|
|
131
|
+
* The callback fires when an `auth:challenge` message arrives
|
|
132
|
+
* over the gateway WebSocket connection.
|
|
133
|
+
*
|
|
134
|
+
* @param ws - The WebSocket connection to the gateway.
|
|
135
|
+
* @param callback - Handler for incoming challenges.
|
|
136
|
+
* @returns Cleanup function to unregister the listener.
|
|
137
|
+
*/
|
|
138
|
+
export function onChallenge(ws, callback) {
|
|
139
|
+
const handler = (event) => {
|
|
140
|
+
try {
|
|
141
|
+
const msg = JSON.parse(event.data);
|
|
142
|
+
if (msg.type === 'auth:challenge' && msg.data) {
|
|
143
|
+
callback(msg.data);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Skip non-JSON messages
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
ws.addEventListener('message', handler);
|
|
151
|
+
return () => ws.removeEventListener('message', handler);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Generate a registration QR code URI for TOTP migration.
|
|
155
|
+
*
|
|
156
|
+
* Creates a `xlock://register` URI that replaces `otpauth://` for
|
|
157
|
+
* asymmetric DID-based registration.
|
|
158
|
+
*
|
|
159
|
+
* @param options - Registration options.
|
|
160
|
+
* @returns The registration URI string.
|
|
161
|
+
*/
|
|
162
|
+
export function generateRegistrationQR(options) {
|
|
163
|
+
const params = new URLSearchParams({
|
|
164
|
+
app: options.appName,
|
|
165
|
+
did: options.appDid,
|
|
166
|
+
registry: options.registryUrl,
|
|
167
|
+
callback: options.callbackUrl,
|
|
168
|
+
});
|
|
169
|
+
if (options.appIcon)
|
|
170
|
+
params.set('icon', options.appIcon);
|
|
171
|
+
return `xlock://register?${params.toString()}`;
|
|
172
|
+
}
|
|
173
|
+
/** Poll the gateway for challenge status until resolved or expired. */
|
|
174
|
+
async function pollChallengeStatus(challengeId, expiresAt, pollIntervalMs, gateway) {
|
|
175
|
+
for (let i = 0; i < MAX_POLL_ITERATIONS; i++) {
|
|
176
|
+
if (Date.now() > expiresAt) {
|
|
177
|
+
return err('CHALLENGE_EXPIRED');
|
|
178
|
+
}
|
|
179
|
+
await sleep(pollIntervalMs);
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(`${gateway.gatewayUrl}/gateway/auth/status/${challengeId}`, {
|
|
182
|
+
headers: { Authorization: `Bearer ${gateway.accessToken}` },
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
if (response.status === 404)
|
|
186
|
+
return err('CHALLENGE_NOT_FOUND');
|
|
187
|
+
continue; // Retry on transient errors
|
|
188
|
+
}
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
if (data.status === 'approved') {
|
|
191
|
+
return ok({
|
|
192
|
+
challengeId: data.challengeId,
|
|
193
|
+
approved: true,
|
|
194
|
+
respondedAt: data.respondedAt,
|
|
195
|
+
envelope: data.envelope,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (data.status === 'denied') {
|
|
199
|
+
return ok({
|
|
200
|
+
challengeId: data.challengeId,
|
|
201
|
+
approved: false,
|
|
202
|
+
respondedAt: data.respondedAt,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (data.status === 'expired') {
|
|
206
|
+
return err('CHALLENGE_EXPIRED');
|
|
207
|
+
}
|
|
208
|
+
// Still pending — continue polling
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Network error — continue polling
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return err('CHALLENGE_TIMEOUT');
|
|
215
|
+
}
|
|
216
|
+
/** Promise-based sleep. */
|
|
217
|
+
function sleep(ms) {
|
|
218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
219
|
+
}
|