@poppykit/server 0.4.0 → 0.5.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/API.md +10 -3
- package/AUTH.md +120 -124
- package/CHANGELOG.md +13 -1
- package/CONFORMANCE.md +73 -0
- package/CONTRIBUTING.md +12 -11
- package/DELIVERY.md +2 -0
- package/INTEGRATIONS.md +74 -81
- package/OPERATIONS.md +142 -0
- package/README.md +11 -20
- package/RELEASING.md +2 -0
- package/SECURITY.md +2 -0
- package/STORAGE.md +56 -17
- package/dist/adapter-contract.d.ts +18 -0
- package/dist/adapter-contract.js +79 -0
- package/dist/auth/authenticator.d.ts +3 -1
- package/dist/auth/authenticator.js +34 -29
- package/dist/auth/authority.d.ts +8 -2
- package/dist/auth/authority.js +7 -2
- package/dist/auth/session.d.ts +47 -0
- package/dist/auth/session.js +94 -0
- package/dist/browser.d.ts +15 -16
- package/dist/browser.js +65 -33
- package/dist/conversation/service.d.ts +11 -1
- package/dist/conversation/service.js +39 -9
- package/dist/feedback.d.ts +1 -1
- package/dist/feedback.js +5 -5
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +8 -4
- package/dist/network/discovery.d.ts +31 -13
- package/dist/network/discovery.js +103 -66
- package/dist/operations.d.ts +115 -0
- package/dist/operations.js +212 -0
- package/dist/postgres.d.ts +6 -0
- package/dist/postgres.js +72 -4
- package/dist/protocol/operations.d.ts +51 -0
- package/dist/protocol/operations.js +98 -0
- package/dist/protocol/session.d.ts +28 -0
- package/dist/protocol/session.js +93 -0
- package/dist/protocol/sse.d.ts +1 -0
- package/dist/protocol/sse.js +1 -1
- package/dist/protocol/wire.d.ts +8 -4
- package/dist/protocol/wire.js +37 -15
- package/dist/reference-auth/index.d.ts +10 -16
- package/dist/reference-auth/index.js +225 -430
- package/dist/sqlite.d.ts +11 -0
- package/dist/sqlite.js +84 -4
- package/examples/operations.mjs +18 -0
- package/examples/quickstart.mjs +8 -5
- package/migrations/RETIREMENT.md +77 -0
- package/package.json +5 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { calculateJwkThumbprint, compactVerify, createLocalJWKSet, decodeProtectedHeader, errors, importJWK } from 'jose';
|
|
3
|
+
import { id, object, parseJson, string, ValidationError } from './wire.js';
|
|
4
|
+
import { validateClientId } from '../network/discovery.js';
|
|
5
|
+
import { secureUrl } from '../network/transport.js';
|
|
6
|
+
const COMPACT = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
7
|
+
const PRIVATE = ['d', 'p', 'q', 'dp', 'dq', 'qi', 'oth', 'k'];
|
|
8
|
+
const hash = (value) => createHash('sha256').update(value).digest('base64url');
|
|
9
|
+
function unique(jwt) {
|
|
10
|
+
if (!COMPACT.test(jwt))
|
|
11
|
+
throw new ValidationError('session.jwt');
|
|
12
|
+
for (const segment of jwt.split('.').slice(0, 2))
|
|
13
|
+
parseJson(Buffer.from(segment, 'base64url').toString('utf8'));
|
|
14
|
+
}
|
|
15
|
+
function timestamp(value) {
|
|
16
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
17
|
+
throw new ValidationError('session.time');
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function bounded(value, field) {
|
|
21
|
+
const result = string(value, field);
|
|
22
|
+
if (result.length > 256)
|
|
23
|
+
throw new ValidationError(field);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
export function sessionRequestUrl(value) {
|
|
27
|
+
const url = secureUrl(value);
|
|
28
|
+
return `${url.origin}${url.pathname}`;
|
|
29
|
+
}
|
|
30
|
+
export function parseSessionClaims(value) {
|
|
31
|
+
const claims = object(value, 'session');
|
|
32
|
+
validateClientId(string(claims.iss, 'session.iss'));
|
|
33
|
+
secureUrl(string(claims.aud, 'session.aud'));
|
|
34
|
+
id(claims.sid, 'session.sid');
|
|
35
|
+
bounded(claims.sub, 'session.sub');
|
|
36
|
+
if ('agent_id' in claims)
|
|
37
|
+
bounded(claims.agent_id, 'session.agent_id');
|
|
38
|
+
string(claims.htm, 'session.htm');
|
|
39
|
+
const htu = string(claims.htu, 'session.htu');
|
|
40
|
+
if (htu.includes('?') || htu.includes('#'))
|
|
41
|
+
throw new ValidationError('session.htu');
|
|
42
|
+
secureUrl(htu);
|
|
43
|
+
timestamp(claims.iat);
|
|
44
|
+
const jti = bounded(claims.jti, 'session.jti');
|
|
45
|
+
if (jti.length < 16)
|
|
46
|
+
throw new ValidationError('session.jti');
|
|
47
|
+
if ('ath' in claims)
|
|
48
|
+
string(claims.ath, 'session.ath');
|
|
49
|
+
return claims;
|
|
50
|
+
}
|
|
51
|
+
/** Verifies identity only. The resource must separately admit/continue the client_id + sid under its store fence. */
|
|
52
|
+
export async function verifySessionJwt(jwt, expected) {
|
|
53
|
+
if (!jwt || jwt.length > 32768)
|
|
54
|
+
throw new ValidationError('session.jwt');
|
|
55
|
+
unique(jwt);
|
|
56
|
+
const header = decodeProtectedHeader(jwt);
|
|
57
|
+
if (header.typ !== 'poppy-session+jwt' || header.alg !== 'ES256' || typeof header.kid !== 'string' || typeof header.pap_dlg !== 'string')
|
|
58
|
+
throw new ValidationError('session.header');
|
|
59
|
+
const claims = parseSessionClaims(parseJson(Buffer.from(jwt.split('.')[1], 'base64url').toString('utf8')));
|
|
60
|
+
const delegation = header.pap_dlg;
|
|
61
|
+
unique(delegation);
|
|
62
|
+
const delegatedHeader = decodeProtectedHeader(delegation);
|
|
63
|
+
if (delegatedHeader.typ !== 'pap-delegation+jwt' || typeof delegatedHeader.alg !== 'string' || !['ES256', 'PS384'].includes(delegatedHeader.alg) || typeof delegatedHeader.kid !== 'string')
|
|
64
|
+
throw new ValidationError('delegation.header');
|
|
65
|
+
const client = await expected.metadata.resolveClient(claims.iss);
|
|
66
|
+
let jwks = await expected.metadata.resolveJwks(client.jwks_uri);
|
|
67
|
+
let verified;
|
|
68
|
+
try {
|
|
69
|
+
verified = await compactVerify(delegation, createLocalJWKSet(jwks), { algorithms: ['ES256', 'PS384'] });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!(error instanceof errors.JWKSNoMatchingKey))
|
|
73
|
+
throw error;
|
|
74
|
+
jwks = await expected.metadata.resolveJwks(client.jwks_uri, { refresh: true });
|
|
75
|
+
verified = await compactVerify(delegation, createLocalJWKSet(jwks), { algorithms: ['ES256', 'PS384'] });
|
|
76
|
+
}
|
|
77
|
+
const delegated = object(parseJson(new TextDecoder().decode(verified.payload)), 'delegation');
|
|
78
|
+
const issued = timestamp(delegated.iat), expires = timestamp(delegated.exp), at = expected.nowMs / 1000;
|
|
79
|
+
bounded(delegated.jti, 'delegation.jti');
|
|
80
|
+
if (delegated.jti.length < 16 || delegated.iss !== claims.iss || expires <= issued || expires - issued > 86400 || expires <= at || issued > at + 30)
|
|
81
|
+
throw new ValidationError('delegation.claims');
|
|
82
|
+
const key = object(object(delegated.cnf, 'delegation.cnf').jwk, 'delegation.key');
|
|
83
|
+
if (key.kty !== 'EC' || key.crv !== 'P-256' || PRIVATE.some(name => name in key) || typeof key.x !== 'string' || typeof key.y !== 'string')
|
|
84
|
+
throw new ValidationError('delegation.key');
|
|
85
|
+
if (header.kid !== await calculateJwkThumbprint(key) || claims.iat > expires)
|
|
86
|
+
throw new ValidationError('session.key');
|
|
87
|
+
await compactVerify(jwt, await importJWK(key, 'ES256'), { algorithms: ['ES256'] });
|
|
88
|
+
if (claims.aud !== expected.companyId || claims.htm !== expected.method || sessionRequestUrl(claims.htu) !== sessionRequestUrl(expected.url) || claims.iat < at - 60 || claims.iat > at + 30)
|
|
89
|
+
throw new ValidationError('session.binding');
|
|
90
|
+
if (expected.accessToken === undefined ? 'ath' in claims : claims.ath !== hash(expected.accessToken) || expected.tokenClientId !== claims.iss)
|
|
91
|
+
throw new ValidationError('session.token');
|
|
92
|
+
return { ...claims, delegationExpiresAt: expires * 1000 };
|
|
93
|
+
}
|
package/dist/protocol/sse.d.ts
CHANGED
package/dist/protocol/sse.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function* parseEventStream(body, options = {}) {
|
|
|
18
18
|
const delta = object(value);
|
|
19
19
|
return { kind: 'delta', messageId: id(delta.message_id, 'message_id'), text: string(delta.text, 'text', false) };
|
|
20
20
|
}
|
|
21
|
-
const event = parseEvent(value);
|
|
21
|
+
const event = parseEvent(value, options.operations);
|
|
22
22
|
if (eventId !== event.id)
|
|
23
23
|
throw new ValidationError('sse.id');
|
|
24
24
|
return { kind: 'event', event };
|
package/dist/protocol/wire.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BusinessOperation, OperationApproval } from './operations.js';
|
|
1
2
|
export type Json = null | boolean | number | string | Json[] | {
|
|
2
3
|
[key: string]: Json;
|
|
3
4
|
};
|
|
@@ -19,6 +20,8 @@ export interface Message {
|
|
|
19
20
|
text?: string;
|
|
20
21
|
data?: JsonObject;
|
|
21
22
|
context?: JsonObject;
|
|
23
|
+
operation?: BusinessOperation;
|
|
24
|
+
approval?: OperationApproval;
|
|
22
25
|
[key: string]: unknown;
|
|
23
26
|
}
|
|
24
27
|
export interface RecordedMessage extends Message {
|
|
@@ -67,7 +70,7 @@ export interface EventPage extends ConversationState {
|
|
|
67
70
|
export interface TokenResponse {
|
|
68
71
|
access_token: string;
|
|
69
72
|
token_type: 'DPoP' | 'Bearer';
|
|
70
|
-
expires_in
|
|
73
|
+
expires_in?: number;
|
|
71
74
|
scope?: string;
|
|
72
75
|
refresh_token?: string;
|
|
73
76
|
refresh_token_expires_in?: number;
|
|
@@ -93,11 +96,12 @@ export declare function id(value: unknown, field: string): string;
|
|
|
93
96
|
export declare function parseMessage(value: unknown, options?: {
|
|
94
97
|
recorded?: boolean;
|
|
95
98
|
company?: boolean;
|
|
99
|
+
operations?: boolean;
|
|
96
100
|
}): Message | RecordedMessage;
|
|
97
|
-
export declare function parseConversationRequest(value: unknown): ConversationRequest;
|
|
101
|
+
export declare function parseConversationRequest(value: unknown, operations?: boolean): ConversationRequest;
|
|
98
102
|
export declare function parseConversationState(value: unknown): ConversationState;
|
|
99
|
-
export declare function parseEvent(value: unknown): ConversationEvent;
|
|
100
|
-
export declare function parseEventPage(value: unknown, requestCursor?: string): EventPage;
|
|
103
|
+
export declare function parseEvent(value: unknown, operations?: boolean): ConversationEvent;
|
|
104
|
+
export declare function parseEventPage(value: unknown, requestCursor?: string, operations?: boolean): EventPage;
|
|
101
105
|
export declare function parseTokenResponse(value: unknown): TokenResponse;
|
|
102
106
|
export declare function parseProtocolError(value: unknown): ProtocolErrorBody;
|
|
103
107
|
export declare function parseReadOptions(url: URL, maxWaitSeconds: number): {
|
package/dist/protocol/wire.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseBusinessOperation, parseOperationApproval } from './operations.js';
|
|
1
2
|
export class ValidationError extends Error {
|
|
2
3
|
field;
|
|
3
4
|
constructor(field) {
|
|
@@ -70,7 +71,18 @@ export function parseMessage(value, options = {}) {
|
|
|
70
71
|
throw new ValidationError('message.author.image');
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
const company = options.company || (options.recorded && message.role === 'company');
|
|
75
|
+
if ('operation' in message) {
|
|
76
|
+
if (!company || !options.operations)
|
|
77
|
+
throw new ValidationError('message.operation');
|
|
78
|
+
message.operation = parseBusinessOperation(message.operation);
|
|
79
|
+
}
|
|
80
|
+
if ('approval' in message) {
|
|
81
|
+
if (company || !options.operations)
|
|
82
|
+
throw new ValidationError('message.approval');
|
|
83
|
+
message.approval = parseOperationApproval(message.approval);
|
|
84
|
+
}
|
|
85
|
+
if (!['text', 'data', 'context', ...(options.operations ? [company ? 'operation' : 'approval'] : [])].some(key => key in message))
|
|
74
86
|
throw new ValidationError('message.content');
|
|
75
87
|
if ('text' in message)
|
|
76
88
|
string(message.text, 'message.text', false);
|
|
@@ -109,20 +121,24 @@ export function parseMessage(value, options = {}) {
|
|
|
109
121
|
}
|
|
110
122
|
return message;
|
|
111
123
|
}
|
|
112
|
-
export function parseConversationRequest(value) {
|
|
124
|
+
export function parseConversationRequest(value, operations = false) {
|
|
113
125
|
const request = object(value);
|
|
114
126
|
if ('parent_conversation_id' in request)
|
|
115
127
|
id(request.parent_conversation_id, 'parent_conversation_id');
|
|
116
|
-
return { ...request, message: parseMessage(request.message) };
|
|
128
|
+
return { ...request, message: parseMessage(request.message, { operations }) };
|
|
117
129
|
}
|
|
118
130
|
export function parseConversationState(value) {
|
|
119
131
|
const state = object(value);
|
|
120
132
|
id(state.conversation_id, 'conversation_id');
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
string(state.status, 'status');
|
|
134
|
+
string(state.responder, 'responder');
|
|
135
|
+
if (!['working', 'idle', 'queued', 'closed'].includes(state.status))
|
|
136
|
+
state.status = 'working';
|
|
137
|
+
if (!['agent', 'human'].includes(state.responder))
|
|
138
|
+
state.responder = 'human';
|
|
123
139
|
return state;
|
|
124
140
|
}
|
|
125
|
-
export function parseEvent(value) {
|
|
141
|
+
export function parseEvent(value, operations = false) {
|
|
126
142
|
const event = object(value, 'event');
|
|
127
143
|
id(event.id, 'event.id');
|
|
128
144
|
string(event.type, 'event.type');
|
|
@@ -138,12 +154,14 @@ export function parseEvent(value) {
|
|
|
138
154
|
throw new ValidationError('event.created_at');
|
|
139
155
|
switch (event.type) {
|
|
140
156
|
case 'message':
|
|
141
|
-
parseMessage(event.message, { recorded: true });
|
|
157
|
+
parseMessage(event.message, { recorded: true, operations });
|
|
142
158
|
break;
|
|
143
|
-
case 'state':
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
case 'state': {
|
|
160
|
+
const state = parseConversationState({ conversation_id: 'state', status: event.status, responder: event.responder });
|
|
161
|
+
event.status = state.status;
|
|
162
|
+
event.responder = state.responder;
|
|
146
163
|
break;
|
|
164
|
+
}
|
|
147
165
|
case 'authorization':
|
|
148
166
|
member(event.error, ['insufficient_scope'], 'event.error');
|
|
149
167
|
if (!/^[\x21\x23-\x5B\x5D-\x7E]+(?: [\x21\x23-\x5B\x5D-\x7E]+)*$/.test(string(event.scope, 'event.scope')))
|
|
@@ -159,11 +177,11 @@ export function parseEvent(value) {
|
|
|
159
177
|
}
|
|
160
178
|
return event;
|
|
161
179
|
}
|
|
162
|
-
export function parseEventPage(value, requestCursor) {
|
|
180
|
+
export function parseEventPage(value, requestCursor, operations = false) {
|
|
163
181
|
const page = parseConversationState(value);
|
|
164
182
|
if (!Array.isArray(page.events))
|
|
165
183
|
throw new ValidationError('events');
|
|
166
|
-
page.events.forEach(parseEvent);
|
|
184
|
+
page.events.forEach(event => parseEvent(event, operations));
|
|
167
185
|
member(page.has_more, [true, false], 'has_more');
|
|
168
186
|
if (page.cursor === null) {
|
|
169
187
|
if (requestCursor !== undefined || page.events.length || page.has_more)
|
|
@@ -172,7 +190,7 @@ export function parseEventPage(value, requestCursor) {
|
|
|
172
190
|
else {
|
|
173
191
|
string(page.cursor, 'cursor');
|
|
174
192
|
const expected = page.events.length ? page.events.at(-1).id : requestCursor;
|
|
175
|
-
if (expected
|
|
193
|
+
if (expected === undefined || page.cursor !== expected)
|
|
176
194
|
throw new ValidationError('cursor');
|
|
177
195
|
}
|
|
178
196
|
return page;
|
|
@@ -185,8 +203,12 @@ export function parseTokenResponse(value) {
|
|
|
185
203
|
if (scope !== '' && (!/^[\x21\x23-\x5B\x5D-\x7E]+(?: [\x21\x23-\x5B\x5D-\x7E]+)*$/.test(scope) || new Set(scope.split(' ')).size !== scope.split(' ').length))
|
|
186
204
|
throw new ValidationError('scope');
|
|
187
205
|
}
|
|
188
|
-
|
|
189
|
-
|
|
206
|
+
const type = string(token.token_type, 'token_type').toLowerCase();
|
|
207
|
+
if (type !== 'bearer' && type !== 'dpop')
|
|
208
|
+
throw new ValidationError('token_type');
|
|
209
|
+
token.token_type = type === 'bearer' ? 'Bearer' : 'DPoP';
|
|
210
|
+
if ('expires_in' in token && (!Number.isSafeInteger(token.expires_in) || token.expires_in < 0))
|
|
211
|
+
throw new ValidationError('expires_in');
|
|
190
212
|
if ('refresh_token' in token)
|
|
191
213
|
string(token.refresh_token, 'refresh_token');
|
|
192
214
|
if ('refresh_token_expires_in' in token)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { MetadataResolver } from '../network/discovery.js';
|
|
2
2
|
import type { IssuerStore, IssuerSelection } from './store.js';
|
|
3
3
|
import type { AuthorityCoordinator, ExternalAuthorityAdapter } from '../auth/authority.js';
|
|
4
|
-
import type { BrowserSession, BrowserSessionAuthority } from '../browser.js';
|
|
5
4
|
export type { IssuerStore, IssuerSelection };
|
|
6
5
|
export { createMetadataResolver } from '../network/discovery.js';
|
|
7
6
|
export { createNodeTransport } from '../network/transport.js';
|
|
@@ -26,6 +25,8 @@ interface GrantRecord {
|
|
|
26
25
|
previousHash: string;
|
|
27
26
|
}
|
|
28
27
|
interface AccessTokenRecord {
|
|
28
|
+
clientId?: string;
|
|
29
|
+
accountId?: string;
|
|
29
30
|
sessionId: string;
|
|
30
31
|
grantId: string | null;
|
|
31
32
|
scopes: string[];
|
|
@@ -34,7 +35,7 @@ interface AccessTokenRecord {
|
|
|
34
35
|
expiresAt: number;
|
|
35
36
|
}
|
|
36
37
|
interface AuthorizationRecord {
|
|
37
|
-
sessionKey: string;
|
|
38
|
+
sessionKey: string | null;
|
|
38
39
|
clientId: string;
|
|
39
40
|
redirectUri: string;
|
|
40
41
|
state: string;
|
|
@@ -49,7 +50,7 @@ interface AuthorizationRecord {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
interface CodeRecord {
|
|
52
|
-
sessionKey: string;
|
|
53
|
+
sessionKey: string | null;
|
|
53
54
|
clientId: string;
|
|
54
55
|
redirectUri: string;
|
|
55
56
|
codeChallenge: string;
|
|
@@ -74,9 +75,11 @@ export type SignInRecord = {
|
|
|
74
75
|
accountId?: string;
|
|
75
76
|
} | {
|
|
76
77
|
kind: 'mediated';
|
|
78
|
+
resource?: string | null;
|
|
79
|
+
grantId?: string;
|
|
77
80
|
clientId: string;
|
|
78
|
-
userId
|
|
79
|
-
sessionId
|
|
81
|
+
userId?: string;
|
|
82
|
+
sessionId?: string;
|
|
80
83
|
scopes: string[];
|
|
81
84
|
expiresAt: number;
|
|
82
85
|
status: 'verifying' | 'pending' | 'failed' | 'expired' | 'complete';
|
|
@@ -101,12 +104,10 @@ export type SignInRecord = {
|
|
|
101
104
|
};
|
|
102
105
|
export interface MediatedContext {
|
|
103
106
|
clientId: string;
|
|
104
|
-
userId: string;
|
|
105
|
-
sessionId: string;
|
|
106
107
|
issuer: string;
|
|
107
108
|
}
|
|
108
109
|
export interface MediatedOptions {
|
|
109
|
-
endpoint
|
|
110
|
+
endpoint?: string;
|
|
110
111
|
scopes: string[];
|
|
111
112
|
fields: {
|
|
112
113
|
name: string;
|
|
@@ -136,7 +137,7 @@ export interface IssuerState {
|
|
|
136
137
|
pending: number;
|
|
137
138
|
pendingForClient: number;
|
|
138
139
|
};
|
|
139
|
-
version: 3 | 4;
|
|
140
|
+
version: 3 | 4 | 5;
|
|
140
141
|
generation: string;
|
|
141
142
|
sessions: Record<string, SessionRecord>;
|
|
142
143
|
sessionKeys: Record<string, string>;
|
|
@@ -200,12 +201,6 @@ export type AuthorizationDecision = {
|
|
|
200
201
|
denied: true;
|
|
201
202
|
};
|
|
202
203
|
export interface ReferenceIssuer {
|
|
203
|
-
browserSessions: BrowserSessionAuthority;
|
|
204
|
-
sessionForKey(binding: {
|
|
205
|
-
clientId: string;
|
|
206
|
-
userId: string;
|
|
207
|
-
sessionKey: string;
|
|
208
|
-
}): Promise<BrowserSession>;
|
|
209
204
|
token(request: Request): Promise<Response>;
|
|
210
205
|
revoke(request: Request): Promise<Response>;
|
|
211
206
|
beginAuthorization(request: Request, browserBinding: string): Promise<AuthorizationView>;
|
|
@@ -213,7 +208,6 @@ export interface ReferenceIssuer {
|
|
|
213
208
|
deviceAuthorization(request: Request): Promise<Response>;
|
|
214
209
|
beginDeviceAuthorization(userCode: string, browserBinding: string): Promise<AuthorizationView>;
|
|
215
210
|
completeDeviceAuthorization(userCode: string, browserBinding: string, decision: AuthorizationDecision): Promise<void>;
|
|
216
|
-
mediatedSignIn(request: Request): Promise<Response>;
|
|
217
211
|
authorizationMetadata(): Record<string, unknown>;
|
|
218
212
|
authority: ExternalAuthorityAdapter;
|
|
219
213
|
disconnect(clientId: string, accountId: string): Promise<void>;
|