@poppykit/client 0.0.0-stage → 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/API.md +25 -0
- package/AUTH.md +51 -0
- package/CHANGELOG.md +18 -0
- package/CONTRIBUTING.md +38 -0
- package/DELIVERY.md +94 -0
- package/INTEGRATIONS.md +87 -0
- package/LICENSE +21 -0
- package/PROVIDER.md +99 -0
- package/README.md +49 -2
- package/RELEASING.md +91 -0
- package/SECURITY.md +13 -0
- package/STORAGE.md +96 -0
- package/dist/auth/challenge.d.ts +19 -0
- package/dist/auth/challenge.js +68 -0
- package/dist/auth/client-auth.d.ts +297 -0
- package/dist/auth/client-auth.js +1583 -0
- package/dist/auth/ownership.d.ts +9 -0
- package/dist/auth/ownership.js +23 -0
- package/dist/auth/sqlite-store.d.ts +14 -0
- package/dist/auth/sqlite-store.js +123 -0
- package/dist/auth/store.d.ts +19 -0
- package/dist/auth/store.js +1 -0
- package/dist/conversation-store.d.ts +15 -0
- package/dist/conversation-store.js +1 -0
- package/dist/conversation.d.ts +118 -0
- package/dist/conversation.js +404 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +7 -0
- package/dist/integrations.d.ts +30 -0
- package/dist/integrations.js +58 -0
- package/dist/memory.d.ts +15 -0
- package/dist/memory.js +32 -0
- package/dist/network/discovery.d.ts +101 -0
- package/dist/network/discovery.js +276 -0
- package/dist/network/transport.d.ts +31 -0
- package/dist/network/transport.js +305 -0
- package/dist/protocol/sse.d.ts +15 -0
- package/dist/protocol/sse.js +90 -0
- package/dist/protocol/wire.d.ts +128 -0
- package/dist/protocol/wire.js +278 -0
- package/dist/provider-sqlite.d.ts +8 -0
- package/dist/provider-sqlite.js +30 -0
- package/dist/provider.d.ts +121 -0
- package/dist/provider.js +342 -0
- package/dist/sqlite-init.d.ts +2 -0
- package/dist/sqlite-init.js +17 -0
- package/dist/sqlite.d.ts +20 -0
- package/dist/sqlite.js +80 -0
- package/examples/quickstart.mjs +19 -0
- package/package.json +72 -4
package/STORAGE.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Persistence and recovery
|
|
2
|
+
|
|
3
|
+
Use targeted storage contracts. Node 24.12.0
|
|
4
|
+
or later is required. The optional `@poppykit/client/sqlite` entry point exports
|
|
5
|
+
`SqliteAuthStore` and `SqliteConversationStore`; importing the client core does not
|
|
6
|
+
load SQLite. `node:sqlite` is experimental in the tested Node 24.12.0 runtime.
|
|
7
|
+
Consumers can implement `AuthStore` and `ConversationStore` using their own database.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { SqliteAuthStore, SqliteConversationStore } from '@poppykit/client/sqlite';
|
|
11
|
+
const authStore = new SqliteAuthStore('/private/path/auth.db');
|
|
12
|
+
const conversationStore = new SqliteConversationStore('/private/path/messages.db');
|
|
13
|
+
// Pass authStore to createClientAuth and conversationStore to createConversationClient.
|
|
14
|
+
// Close both after all operations have finished.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use one auth database per namespace/local user/client identity. The owner tuple is
|
|
18
|
+
checked on every SDK auth read and mutation. Identities are keyed by the exact full
|
|
19
|
+
issuer, connections by immutable IDs, operations by ID and callback state hash,
|
|
20
|
+
cleanup by connection, and token slots by session/resource. Secret values are never
|
|
21
|
+
stored in auth records: immutable references point to the host's `SecretStore`.
|
|
22
|
+
Session Tokens remain in process memory. Conversation databases contain message
|
|
23
|
+
bodies; operators must provide private storage and their required encryption.
|
|
24
|
+
|
|
25
|
+
Auth selections load an identity or a connection aggregate (its grants, operations,
|
|
26
|
+
and cleanup), never unrelated connections. Operation/callback selectors resolve the
|
|
27
|
+
owning connection through indexes. `list` explicitly enumerates connections.
|
|
28
|
+
Recovery pages nonterminal operation IDs in batches of 100, then rechecks each
|
|
29
|
+
operation and its owner inside its transaction. `cleanup(before, limit)` removes
|
|
30
|
+
only terminal, non-uncertain auth history; the limit is at most 1000. It does not
|
|
31
|
+
erase grants, pending exchanges, uncertain results, or secrets.
|
|
32
|
+
|
|
33
|
+
Transactions must serialize conflicting domain updates across processes, roll back
|
|
34
|
+
on exceptions, reject asynchronous mutation callbacks and non-JSON persisted values,
|
|
35
|
+
enforce unique keys, and return detached values. SQLite uses WAL, FULL synchronous
|
|
36
|
+
commits and a bounded busy wait. It serializes writers; parallel writers are not a
|
|
37
|
+
promise. The database file must be on a local filesystem that supports SQLite's
|
|
38
|
+
locking; this is not a distributed database. Create adapters before starting work,
|
|
39
|
+
and keep network/vault calls outside transaction callbacks.
|
|
40
|
+
|
|
41
|
+
Conversation storage has separate prepare/get/settle operations and checkpoint
|
|
42
|
+
compare-and-set. An outbound ID is unique per connection/full issuer, and also
|
|
43
|
+
records its endpoint. Retry rejects an endpoint change before sending. Preparation
|
|
44
|
+
returns the existing record instead of replacing content; terminal outcomes cannot
|
|
45
|
+
be overwritten by a late sender. Checkpoints include endpoint, conversation and
|
|
46
|
+
consumer. Receipts remain available for retries: the old `keepCompleted` deletion
|
|
47
|
+
behavior is removed. Retention preserves message identity and bounds payload cleanup; no automatic
|
|
48
|
+
age-based deletion is performed.
|
|
49
|
+
|
|
50
|
+
`MemoryConversationStore` from `@poppykit/client/memory` is non-durable and confined
|
|
51
|
+
to one process. The SQLite adapters also accept `:memory:` for non-durable tests;
|
|
52
|
+
each such adapter has its own database. Neither mode demonstrates process recovery.
|
|
53
|
+
|
|
54
|
+
## Operation ownership
|
|
55
|
+
|
|
56
|
+
Each active rotation or code exchange records a random execution owner, host name,
|
|
57
|
+
and PID before dispatch. The default liveness check keeps other running local
|
|
58
|
+
processes live; within this process it tracks actual active executions. Unknown
|
|
59
|
+
remote hosts and permission errors are conservatively live. A new invocation cannot
|
|
60
|
+
invalidate an active exchange just by starting recovery. A reused PID can delay
|
|
61
|
+
recovery; it cannot authorize replay. Multi-host adapters must supply a trustworthy
|
|
62
|
+
`ownerAlive` hook backed by their deployment's ownership/fencing mechanism. Returning
|
|
63
|
+
false is an assertion of abandonment, not a request to cancel an active operation.
|
|
64
|
+
|
|
65
|
+
Recovery rechecks phase/generation and liveness under the database transaction. A
|
|
66
|
+
prepared abandoned rotation can release its claim; a dispatched exchange without a
|
|
67
|
+
durably staged response becomes uncertain. A staged rotation can activate locally
|
|
68
|
+
only if its connection/grant generations still match. Cancellation, disconnection,
|
|
69
|
+
or another generation wins over late activation. No remote token exchange is replayed
|
|
70
|
+
by recovery, and lease expiry would not prove that a request was never sent.
|
|
71
|
+
|
|
72
|
+
The vault and remote issuer are outside the database transaction. Stage immutable
|
|
73
|
+
secrets first, conditionally activate references, then remove superseded secrets.
|
|
74
|
+
Vault failure or process death can leave an unreferenced staged secret; the host
|
|
75
|
+
needs an orphan-secret sweep that respects active owners and a grace period. A lost
|
|
76
|
+
remote rotation response remains uncertain. This adapter does not promise automatic
|
|
77
|
+
recovery of a token that the issuer rotated but never delivered.
|
|
78
|
+
|
|
79
|
+
## Development reset
|
|
80
|
+
|
|
81
|
+
There is no automatic migration from old whole-state development stores. Stop all
|
|
82
|
+
users of a disposable store, resolve uncertain remote outcomes, and explicitly move
|
|
83
|
+
aside its database and WAL/SHM files and the matching host-managed secret namespace
|
|
84
|
+
before creating a fresh store. Never reset live credentials by deleting a database
|
|
85
|
+
behind a running process. Old stores are not silently deleted or reinterpreted.
|
|
86
|
+
|
|
87
|
+
Contract tests cover real child-process contention, crash recovery, uncertain
|
|
88
|
+
external effects, staged secrets and revocation races; see [contributing](CONTRIBUTING.md).
|
|
89
|
+
|
|
90
|
+
## Conversation delivery lifetime
|
|
91
|
+
|
|
92
|
+
See [DELIVERY.md](DELIVERY.md) for the retention table. Outbound records and
|
|
93
|
+
checkpoints are retained until explicit local deletion; pending/uncertain retries
|
|
94
|
+
must first be reconciled. Checkpoint acknowledgements use compare-and-set and do
|
|
95
|
+
not acknowledge merely because a page or stream frame was read. Local deletion
|
|
96
|
+
never deletes company history, revokes access or cancels accepted business work.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function schemeChallenge(header: string | null, scheme: 'DPoP' | 'Bearer'): {
|
|
2
|
+
error?: string;
|
|
3
|
+
scope?: string;
|
|
4
|
+
extension?: string;
|
|
5
|
+
conversation_id?: string;
|
|
6
|
+
} | undefined;
|
|
7
|
+
export declare const dpopChallenge: (header: string | null) => {
|
|
8
|
+
error?: string;
|
|
9
|
+
scope?: string;
|
|
10
|
+
extension?: string;
|
|
11
|
+
conversation_id?: string;
|
|
12
|
+
} | undefined;
|
|
13
|
+
export interface AuthChallenge {
|
|
14
|
+
error?: string;
|
|
15
|
+
requiredScopes: string[];
|
|
16
|
+
extensions: string[];
|
|
17
|
+
conversationId?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function authChallenge(header: string | null, body?: unknown): AuthChallenge;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Reads the DPoP challenge from a WWW-Authenticate header (RFC 9110 11.6.1, spec 6); other schemes' params are ignored.
|
|
2
|
+
const TOKEN = /[A-Za-z0-9!#$%&'*+.^_`|~-]+/y;
|
|
3
|
+
export function schemeChallenge(header, scheme) {
|
|
4
|
+
if (!header)
|
|
5
|
+
return undefined;
|
|
6
|
+
let i = 0;
|
|
7
|
+
let current;
|
|
8
|
+
let found;
|
|
9
|
+
const skip = (chars) => { while (i < header.length && chars.test(header[i]))
|
|
10
|
+
i++; };
|
|
11
|
+
while (i < header.length) {
|
|
12
|
+
skip(/[\s,]/);
|
|
13
|
+
TOKEN.lastIndex = i;
|
|
14
|
+
const match = TOKEN.exec(header);
|
|
15
|
+
if (!match) {
|
|
16
|
+
i++;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
i += match[0].length;
|
|
20
|
+
const afterToken = i;
|
|
21
|
+
skip(/\s/);
|
|
22
|
+
if (header[i] !== '=' || !current) {
|
|
23
|
+
if (header[i] === '=') {
|
|
24
|
+
i = afterToken;
|
|
25
|
+
skip(/[=\s]/);
|
|
26
|
+
continue;
|
|
27
|
+
} // token68 or stray param before any scheme
|
|
28
|
+
current = { scheme: match[0].toLowerCase(), params: {} };
|
|
29
|
+
if (current.scheme === scheme.toLowerCase())
|
|
30
|
+
found = current.params;
|
|
31
|
+
i = afterToken;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
i++;
|
|
35
|
+
skip(/\s/);
|
|
36
|
+
let value = '';
|
|
37
|
+
if (header[i] === '"') {
|
|
38
|
+
i++;
|
|
39
|
+
while (i < header.length && header[i] !== '"') {
|
|
40
|
+
if (header[i] === '\\')
|
|
41
|
+
i++;
|
|
42
|
+
value += header[i] ?? '';
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
TOKEN.lastIndex = i;
|
|
49
|
+
value = TOKEN.exec(header)?.[0] ?? '';
|
|
50
|
+
i += value.length;
|
|
51
|
+
}
|
|
52
|
+
const name = match[0].toLowerCase();
|
|
53
|
+
if (current.scheme === scheme.toLowerCase() && ['error', 'scope', 'extension', 'conversation_id'].includes(name))
|
|
54
|
+
current.params[name] = value;
|
|
55
|
+
}
|
|
56
|
+
return found;
|
|
57
|
+
}
|
|
58
|
+
export const dpopChallenge = (header) => schemeChallenge(header, 'DPoP');
|
|
59
|
+
// Combine independent detail channels: a sparse JSON error must not erase header scopes/extensions.
|
|
60
|
+
export function authChallenge(header, body) {
|
|
61
|
+
const challenge = dpopChallenge(header), json = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
|
|
62
|
+
const scopes = [challenge?.scope, json.scope].flatMap(value => typeof value === 'string' ? value.split(' ').filter(scope => /^[\x21\x23-\x5B\x5D-\x7E]+$/.test(scope)) : []);
|
|
63
|
+
const extensions = [challenge?.extension, json.extension].filter((value) => typeof value === 'string' && value.length <= 512 && /^(?:[A-Za-z0-9._-]+|(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*)$/.test(value));
|
|
64
|
+
const safe = new Set(['sign_in_required', 'insufficient_scope', 'invalid_token', 'invalid_dpop_proof', 'use_dpop_nonce', 'extension_required', 'rate_limited', 'invalid_scope']);
|
|
65
|
+
const error = [challenge?.error, json.error].find((value) => typeof value === 'string' && safe.has(value));
|
|
66
|
+
const conversationId = [json.conversation_id, challenge?.conversation_id].find((value) => typeof value === 'string' && value.length > 0 && value.length <= 256);
|
|
67
|
+
return { ...(error ? { error } : {}), requiredScopes: [...new Set(scopes)], extensions: [...new Set(extensions)], ...(conversationId ? { conversationId } : {}) };
|
|
68
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import type { AuthSelection, AuthStore } from './store.js';
|
|
2
|
+
import type { OperationOwner } from './ownership.js';
|
|
3
|
+
import type { JsonObject } from '../protocol/wire.js';
|
|
4
|
+
import type { CompanyMetadata, MetadataResolver } from '../network/discovery.js';
|
|
5
|
+
import type { HttpTransport } from '../network/transport.js';
|
|
6
|
+
import type { ApiTarget, AgentBrowserController, FeedbackReport } from '../integrations.js';
|
|
7
|
+
export type { AuthStore, AuthSelection };
|
|
8
|
+
export interface SecretStore {
|
|
9
|
+
stage(secret: string): Promise<string>;
|
|
10
|
+
read(reference: string): Promise<string | null>;
|
|
11
|
+
remove(reference: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export interface IdentitySigner {
|
|
14
|
+
readonly kid: string;
|
|
15
|
+
publicJwk(): Promise<JsonObject>;
|
|
16
|
+
sign(input: Uint8Array): Promise<Uint8Array>;
|
|
17
|
+
}
|
|
18
|
+
export type AssertionRequest = {
|
|
19
|
+
operationId: string;
|
|
20
|
+
issuer: string;
|
|
21
|
+
} & ({
|
|
22
|
+
purpose: 'client-authentication';
|
|
23
|
+
} | {
|
|
24
|
+
purpose: 'session';
|
|
25
|
+
} | {
|
|
26
|
+
purpose: 'browser';
|
|
27
|
+
sessionId: string;
|
|
28
|
+
returnTo: string;
|
|
29
|
+
});
|
|
30
|
+
export interface AssertionProvider {
|
|
31
|
+
assert(request: AssertionRequest): Promise<{
|
|
32
|
+
assertion: string;
|
|
33
|
+
expiresAt: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export interface ProviderIdentity {
|
|
37
|
+
identity(issuer: string): Promise<{
|
|
38
|
+
clientId: string;
|
|
39
|
+
userId: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export type SignInMethod = 'direct' | 'device' | 'mediated';
|
|
43
|
+
export interface AuthorizationRequirement {
|
|
44
|
+
requiredScopes: string[];
|
|
45
|
+
grantedScopes: string[];
|
|
46
|
+
missingScopes: string[];
|
|
47
|
+
sufficient: boolean;
|
|
48
|
+
methods: {
|
|
49
|
+
method: SignInMethod;
|
|
50
|
+
scopes: string[];
|
|
51
|
+
}[];
|
|
52
|
+
organization: {
|
|
53
|
+
name: string;
|
|
54
|
+
domain: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface TrustedInput {
|
|
58
|
+
collect(request: {
|
|
59
|
+
operationId: string;
|
|
60
|
+
issuer: string;
|
|
61
|
+
organization: {
|
|
62
|
+
name: string;
|
|
63
|
+
domain: string;
|
|
64
|
+
};
|
|
65
|
+
fields: {
|
|
66
|
+
name: string;
|
|
67
|
+
label: string;
|
|
68
|
+
secret: boolean;
|
|
69
|
+
}[];
|
|
70
|
+
challenge?: {
|
|
71
|
+
id: string;
|
|
72
|
+
sentTo: string;
|
|
73
|
+
expiresAt: number;
|
|
74
|
+
};
|
|
75
|
+
}): Promise<Record<string, string> | null>;
|
|
76
|
+
}
|
|
77
|
+
export interface SignInInput {
|
|
78
|
+
method?: SignInMethod;
|
|
79
|
+
scopes: string[];
|
|
80
|
+
redirectUri?: string;
|
|
81
|
+
browserBinding?: string;
|
|
82
|
+
force?: boolean;
|
|
83
|
+
requiredExistingScopes?: string[];
|
|
84
|
+
}
|
|
85
|
+
export interface SignInStart {
|
|
86
|
+
outcome: AuthOutcome;
|
|
87
|
+
authorizationUrl?: string;
|
|
88
|
+
device?: {
|
|
89
|
+
verificationUri: string;
|
|
90
|
+
verificationUriComplete?: string;
|
|
91
|
+
userCode: string;
|
|
92
|
+
organization: {
|
|
93
|
+
name: string;
|
|
94
|
+
domain: string;
|
|
95
|
+
};
|
|
96
|
+
nextPollAt: number;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export interface ClientAuthOptions {
|
|
100
|
+
namespace: string;
|
|
101
|
+
localUserId: string;
|
|
102
|
+
clientId: string;
|
|
103
|
+
redirectUris: readonly string[];
|
|
104
|
+
identity?: IdentitySigner;
|
|
105
|
+
assertions?: AssertionProvider;
|
|
106
|
+
providerIdentity?: ProviderIdentity;
|
|
107
|
+
trustedInput?: TrustedInput;
|
|
108
|
+
store: AuthStore;
|
|
109
|
+
secrets: SecretStore;
|
|
110
|
+
transport: HttpTransport;
|
|
111
|
+
metadata: MetadataResolver;
|
|
112
|
+
now?: () => number;
|
|
113
|
+
ownerAlive?: (owner: OperationOwner) => boolean;
|
|
114
|
+
}
|
|
115
|
+
export type ConnectionStatus = 'signed_out' | 'signed_in' | 'disconnected' | 'uncertain';
|
|
116
|
+
export interface Connection {
|
|
117
|
+
id: string;
|
|
118
|
+
domain: string;
|
|
119
|
+
issuer: string;
|
|
120
|
+
organization: {
|
|
121
|
+
name: string;
|
|
122
|
+
domain: string;
|
|
123
|
+
};
|
|
124
|
+
sessionId: string | null;
|
|
125
|
+
generation: number;
|
|
126
|
+
status: ConnectionStatus;
|
|
127
|
+
scopes: string[];
|
|
128
|
+
}
|
|
129
|
+
export type OperationStatus = 'pending' | 'completed' | 'denied' | 'expired' | 'cancelled' | 'failed' | 'uncertain';
|
|
130
|
+
export interface AuthOutcome {
|
|
131
|
+
operationId: string;
|
|
132
|
+
status: OperationStatus;
|
|
133
|
+
connection: Connection;
|
|
134
|
+
error?: string;
|
|
135
|
+
grantedScopes?: string[];
|
|
136
|
+
missingScopes?: string[];
|
|
137
|
+
retryAfterMs?: number;
|
|
138
|
+
nextAction?: 'wait' | 'sign_in' | 'retry_cleanup';
|
|
139
|
+
}
|
|
140
|
+
export interface SignOutOutcome {
|
|
141
|
+
connection: Connection;
|
|
142
|
+
cleanup: 'complete' | 'pending' | 'uncertain';
|
|
143
|
+
}
|
|
144
|
+
export interface ConversationTarget {
|
|
145
|
+
issuer: string;
|
|
146
|
+
endpoint: string;
|
|
147
|
+
resource?: string;
|
|
148
|
+
}
|
|
149
|
+
export interface ClientAuth {
|
|
150
|
+
start(domain: string, input?: {
|
|
151
|
+
anonymous?: boolean;
|
|
152
|
+
}): Promise<AuthOutcome>;
|
|
153
|
+
get(connectionId: string): Promise<Connection | null>;
|
|
154
|
+
list(): Promise<Connection[]>;
|
|
155
|
+
target(connectionId: string): Promise<ConversationTarget>;
|
|
156
|
+
authorization(connectionId: string, requiredScopes: readonly string[]): Promise<AuthorizationRequirement>;
|
|
157
|
+
beginSignIn(connectionId: string, input: SignInInput): Promise<SignInStart>;
|
|
158
|
+
continueSignIn(operationId: string): Promise<AuthOutcome>;
|
|
159
|
+
completeSignIn(input: {
|
|
160
|
+
callbackUrl: string;
|
|
161
|
+
browserBinding: string;
|
|
162
|
+
}): Promise<AuthOutcome>;
|
|
163
|
+
cancelSignIn(operationId: string): Promise<AuthOutcome>;
|
|
164
|
+
operation(operationId: string): Promise<AuthOutcome | null>;
|
|
165
|
+
renew(connectionId: string, input?: {
|
|
166
|
+
resource?: string;
|
|
167
|
+
scopes?: string[];
|
|
168
|
+
}): Promise<AuthOutcome>;
|
|
169
|
+
signOut(connectionId: string): Promise<SignOutOutcome>;
|
|
170
|
+
retryCleanup(connectionId: string): Promise<SignOutOutcome>;
|
|
171
|
+
recover(): Promise<AuthOutcome[]>;
|
|
172
|
+
authorizedFetch(connectionId: string, request: Request, target: ConversationTarget, requiredScopes?: readonly string[]): Promise<Response>;
|
|
173
|
+
apiTarget(connectionId: string, apiUrl: string, destination?: string): Promise<ApiTarget>;
|
|
174
|
+
apiFetch(connectionId: string, request: Request, target: ApiTarget, requiredScopes?: readonly string[]): Promise<Response>;
|
|
175
|
+
joinBrowser(connectionId: string, returnTo: string, controller: AgentBrowserController): Promise<void>;
|
|
176
|
+
feedback(connectionId: string, report: FeedbackReport): Promise<void>;
|
|
177
|
+
}
|
|
178
|
+
export interface CredentialRecord {
|
|
179
|
+
secretRef: string;
|
|
180
|
+
tokenType: 'DPoP' | 'Bearer';
|
|
181
|
+
dpopKeyRef: string;
|
|
182
|
+
scopes: string[];
|
|
183
|
+
expiresAt: number;
|
|
184
|
+
generation: number;
|
|
185
|
+
}
|
|
186
|
+
export interface ConnectionRecord {
|
|
187
|
+
id: string;
|
|
188
|
+
domain: string;
|
|
189
|
+
issuer: string;
|
|
190
|
+
metadata: CompanyMetadata;
|
|
191
|
+
userId: string;
|
|
192
|
+
dpopKeyRef: string;
|
|
193
|
+
sessionId: string | null;
|
|
194
|
+
generation: number;
|
|
195
|
+
status: ConnectionStatus;
|
|
196
|
+
grantId: string | null;
|
|
197
|
+
slots: Record<string, CredentialRecord>;
|
|
198
|
+
anonymous?: boolean;
|
|
199
|
+
notBefore?: string;
|
|
200
|
+
}
|
|
201
|
+
export interface GrantRecord {
|
|
202
|
+
id: string;
|
|
203
|
+
issuer: string;
|
|
204
|
+
userId: string;
|
|
205
|
+
secretRef: string;
|
|
206
|
+
scopes: string[];
|
|
207
|
+
expiresAt: number | null;
|
|
208
|
+
generation: number;
|
|
209
|
+
status: 'active' | 'uncertain' | 'revoking' | 'revoked';
|
|
210
|
+
operationId: string | null;
|
|
211
|
+
}
|
|
212
|
+
export interface DirectRecord {
|
|
213
|
+
stateHash: string;
|
|
214
|
+
verifierRef: string;
|
|
215
|
+
browserHash: string;
|
|
216
|
+
redirectUri: string;
|
|
217
|
+
issuer: string;
|
|
218
|
+
clientId: string;
|
|
219
|
+
sessionId: string;
|
|
220
|
+
}
|
|
221
|
+
export interface OperationRecord {
|
|
222
|
+
owner?: OperationOwner;
|
|
223
|
+
epoch: string;
|
|
224
|
+
id: string;
|
|
225
|
+
kind: 'start' | 'renew' | 'sign_in';
|
|
226
|
+
connectionId: string;
|
|
227
|
+
connectionGeneration: number;
|
|
228
|
+
grantId: string | null;
|
|
229
|
+
grantGeneration: number | null;
|
|
230
|
+
phase: 'prepared' | 'dispatched' | 'staged' | 'terminal';
|
|
231
|
+
status: OperationStatus;
|
|
232
|
+
createdAt: number;
|
|
233
|
+
expiresAt: number;
|
|
234
|
+
resource: string | null;
|
|
235
|
+
scopes: string[];
|
|
236
|
+
direct: DirectRecord | null;
|
|
237
|
+
staged: StagedRefresh | null;
|
|
238
|
+
result: AuthOutcome | null;
|
|
239
|
+
method?: SignInMethod;
|
|
240
|
+
requiredExistingScopes?: string[];
|
|
241
|
+
signInSessionId?: string;
|
|
242
|
+
device?: {
|
|
243
|
+
codeRef: string;
|
|
244
|
+
intervalMs: number;
|
|
245
|
+
nextPollAt: number;
|
|
246
|
+
};
|
|
247
|
+
mediated?: {
|
|
248
|
+
challengeId: string;
|
|
249
|
+
sentTo: string;
|
|
250
|
+
};
|
|
251
|
+
cancelRequested?: boolean;
|
|
252
|
+
}
|
|
253
|
+
export interface StagedRefresh {
|
|
254
|
+
tokenType?: 'DPoP' | 'Bearer';
|
|
255
|
+
accessRef: string;
|
|
256
|
+
refreshRef: string | null;
|
|
257
|
+
sessionId: string;
|
|
258
|
+
scopes: string[];
|
|
259
|
+
signedIn: boolean;
|
|
260
|
+
expiresAt: number;
|
|
261
|
+
refreshExpiresAt: number | null;
|
|
262
|
+
}
|
|
263
|
+
export interface CleanupRecord {
|
|
264
|
+
id: string;
|
|
265
|
+
connectionId: string;
|
|
266
|
+
issuer: string;
|
|
267
|
+
revocationEndpoint: string;
|
|
268
|
+
secretRef: string;
|
|
269
|
+
status: 'pending' | 'uncertain';
|
|
270
|
+
notBefore?: string;
|
|
271
|
+
}
|
|
272
|
+
export interface ClientAuthState {
|
|
273
|
+
version: 1;
|
|
274
|
+
epoch: string;
|
|
275
|
+
owner: {
|
|
276
|
+
namespace: string;
|
|
277
|
+
clientId: string;
|
|
278
|
+
localUserId: string;
|
|
279
|
+
} | null;
|
|
280
|
+
identities: Record<string, {
|
|
281
|
+
userId: string;
|
|
282
|
+
dpopKeyRef: string;
|
|
283
|
+
}>;
|
|
284
|
+
connections: Record<string, ConnectionRecord>;
|
|
285
|
+
grants: Record<string, GrantRecord>;
|
|
286
|
+
operations: Record<string, OperationRecord>;
|
|
287
|
+
cleanup: Record<string, CleanupRecord>;
|
|
288
|
+
}
|
|
289
|
+
export declare class AuthFailure extends Error {
|
|
290
|
+
readonly code: string;
|
|
291
|
+
readonly connectionId?: string | undefined;
|
|
292
|
+
readonly retryAfterMs?: number | undefined;
|
|
293
|
+
readonly retryAt?: string | undefined;
|
|
294
|
+
constructor(code: string, connectionId?: string | undefined, retryAfterMs?: number | undefined, retryAt?: string | undefined);
|
|
295
|
+
}
|
|
296
|
+
export declare function initialClientAuthState(): ClientAuthState;
|
|
297
|
+
export declare function createClientAuth(options: ClientAuthOptions): ClientAuth;
|