@getcirrus/oauth-provider 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +284 -0
- package/dist/index.d.ts +591 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1395 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import { JWK } from "jose";
|
|
2
|
+
import { OAuthClientMetadata, OAuthParResponse, OAuthTokenResponse } from "@atproto/oauth-types";
|
|
3
|
+
|
|
4
|
+
//#region src/storage.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OAuth storage interface and types
|
|
8
|
+
* Defines the storage abstraction for auth codes, tokens, clients, etc.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Data stored with an authorization code
|
|
12
|
+
*/
|
|
13
|
+
interface AuthCodeData {
|
|
14
|
+
/** Client DID that requested the code */
|
|
15
|
+
clientId: string;
|
|
16
|
+
/** Redirect URI used in the authorization request */
|
|
17
|
+
redirectUri: string;
|
|
18
|
+
/** PKCE code challenge */
|
|
19
|
+
codeChallenge: string;
|
|
20
|
+
/** PKCE challenge method (always S256 for AT Protocol) */
|
|
21
|
+
codeChallengeMethod: "S256";
|
|
22
|
+
/** Authorized scope */
|
|
23
|
+
scope: string;
|
|
24
|
+
/** User DID that authorized the request */
|
|
25
|
+
sub: string;
|
|
26
|
+
/** Expiration timestamp (Unix ms) */
|
|
27
|
+
expiresAt: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Data stored with access and refresh tokens
|
|
31
|
+
*/
|
|
32
|
+
interface TokenData {
|
|
33
|
+
/** Opaque access token */
|
|
34
|
+
accessToken: string;
|
|
35
|
+
/** Opaque refresh token */
|
|
36
|
+
refreshToken: string;
|
|
37
|
+
/** Client DID that received the token */
|
|
38
|
+
clientId: string;
|
|
39
|
+
/** User DID the token is for */
|
|
40
|
+
sub: string;
|
|
41
|
+
/** Authorized scope */
|
|
42
|
+
scope: string;
|
|
43
|
+
/** DPoP key thumbprint (for token binding) */
|
|
44
|
+
dpopJkt?: string;
|
|
45
|
+
/** Issuance timestamp (Unix ms) */
|
|
46
|
+
issuedAt: number;
|
|
47
|
+
/** Expiration timestamp (Unix ms) */
|
|
48
|
+
expiresAt: number;
|
|
49
|
+
/** Whether the token has been revoked */
|
|
50
|
+
revoked?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* OAuth client metadata (discovered from DID document)
|
|
54
|
+
*/
|
|
55
|
+
interface ClientMetadata {
|
|
56
|
+
/** Client DID */
|
|
57
|
+
clientId: string;
|
|
58
|
+
/** Human-readable client name */
|
|
59
|
+
clientName: string;
|
|
60
|
+
/** Allowed redirect URIs */
|
|
61
|
+
redirectUris: string[];
|
|
62
|
+
/** Client logo URI (optional) */
|
|
63
|
+
logoUri?: string;
|
|
64
|
+
/** Client homepage URI (optional) */
|
|
65
|
+
clientUri?: string;
|
|
66
|
+
/** When the metadata was cached (Unix ms) */
|
|
67
|
+
cachedAt?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Data stored for Pushed Authorization Requests (PAR)
|
|
71
|
+
*/
|
|
72
|
+
interface PARData {
|
|
73
|
+
/** Client DID that pushed the request */
|
|
74
|
+
clientId: string;
|
|
75
|
+
/** All OAuth parameters from the push request */
|
|
76
|
+
params: Record<string, string>;
|
|
77
|
+
/** Expiration timestamp (Unix ms) */
|
|
78
|
+
expiresAt: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Storage interface for OAuth data
|
|
82
|
+
* Implementations should handle TTL-based expiration
|
|
83
|
+
*/
|
|
84
|
+
interface OAuthStorage {
|
|
85
|
+
/**
|
|
86
|
+
* Save an authorization code
|
|
87
|
+
* @param code The authorization code
|
|
88
|
+
* @param data Associated data
|
|
89
|
+
*/
|
|
90
|
+
saveAuthCode(code: string, data: AuthCodeData): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Get authorization code data
|
|
93
|
+
* @param code The authorization code
|
|
94
|
+
* @returns The data or null if not found/expired
|
|
95
|
+
*/
|
|
96
|
+
getAuthCode(code: string): Promise<AuthCodeData | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Delete an authorization code (after use)
|
|
99
|
+
* @param code The authorization code
|
|
100
|
+
*/
|
|
101
|
+
deleteAuthCode(code: string): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Save token data
|
|
104
|
+
* @param data The token data
|
|
105
|
+
*/
|
|
106
|
+
saveTokens(data: TokenData): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Get token data by access token
|
|
109
|
+
* @param accessToken The access token
|
|
110
|
+
* @returns The data or null if not found/expired/revoked
|
|
111
|
+
*/
|
|
112
|
+
getTokenByAccess(accessToken: string): Promise<TokenData | null>;
|
|
113
|
+
/**
|
|
114
|
+
* Get token data by refresh token
|
|
115
|
+
* @param refreshToken The refresh token
|
|
116
|
+
* @returns The data or null if not found/expired/revoked
|
|
117
|
+
*/
|
|
118
|
+
getTokenByRefresh(refreshToken: string): Promise<TokenData | null>;
|
|
119
|
+
/**
|
|
120
|
+
* Revoke a token by access token
|
|
121
|
+
* @param accessToken The access token to revoke
|
|
122
|
+
*/
|
|
123
|
+
revokeToken(accessToken: string): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Revoke all tokens for a user (for logout)
|
|
126
|
+
* @param sub The user DID
|
|
127
|
+
*/
|
|
128
|
+
revokeAllTokens?(sub: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Save client metadata (cached from DID document)
|
|
131
|
+
* @param clientId The client DID
|
|
132
|
+
* @param metadata The client metadata
|
|
133
|
+
*/
|
|
134
|
+
saveClient(clientId: string, metadata: ClientMetadata): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Get cached client metadata
|
|
137
|
+
* @param clientId The client DID
|
|
138
|
+
* @returns The metadata or null if not cached
|
|
139
|
+
*/
|
|
140
|
+
getClient(clientId: string): Promise<ClientMetadata | null>;
|
|
141
|
+
/**
|
|
142
|
+
* Save PAR request data
|
|
143
|
+
* @param requestUri The unique request URI
|
|
144
|
+
* @param data The PAR data
|
|
145
|
+
*/
|
|
146
|
+
savePAR(requestUri: string, data: PARData): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Get PAR request data
|
|
149
|
+
* @param requestUri The request URI
|
|
150
|
+
* @returns The data or null if not found/expired
|
|
151
|
+
*/
|
|
152
|
+
getPAR(requestUri: string): Promise<PARData | null>;
|
|
153
|
+
/**
|
|
154
|
+
* Delete PAR request (after use - one-time use)
|
|
155
|
+
* @param requestUri The request URI
|
|
156
|
+
*/
|
|
157
|
+
deletePAR(requestUri: string): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Check if a nonce has been used and save it if not
|
|
160
|
+
* Used for DPoP replay prevention
|
|
161
|
+
* @param nonce The nonce to check
|
|
162
|
+
* @returns true if the nonce is new (valid), false if already used
|
|
163
|
+
*/
|
|
164
|
+
checkAndSaveNonce(nonce: string): Promise<boolean>;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* In-memory storage implementation for testing
|
|
168
|
+
*/
|
|
169
|
+
declare class InMemoryOAuthStorage implements OAuthStorage {
|
|
170
|
+
private authCodes;
|
|
171
|
+
private tokens;
|
|
172
|
+
private refreshTokenIndex;
|
|
173
|
+
private clients;
|
|
174
|
+
private parRequests;
|
|
175
|
+
private nonces;
|
|
176
|
+
saveAuthCode(code: string, data: AuthCodeData): Promise<void>;
|
|
177
|
+
getAuthCode(code: string): Promise<AuthCodeData | null>;
|
|
178
|
+
deleteAuthCode(code: string): Promise<void>;
|
|
179
|
+
saveTokens(data: TokenData): Promise<void>;
|
|
180
|
+
getTokenByAccess(accessToken: string): Promise<TokenData | null>;
|
|
181
|
+
getTokenByRefresh(refreshToken: string): Promise<TokenData | null>;
|
|
182
|
+
revokeToken(accessToken: string): Promise<void>;
|
|
183
|
+
revokeAllTokens(sub: string): Promise<void>;
|
|
184
|
+
saveClient(clientId: string, metadata: ClientMetadata): Promise<void>;
|
|
185
|
+
getClient(clientId: string): Promise<ClientMetadata | null>;
|
|
186
|
+
savePAR(requestUri: string, data: PARData): Promise<void>;
|
|
187
|
+
getPAR(requestUri: string): Promise<PARData | null>;
|
|
188
|
+
deletePAR(requestUri: string): Promise<void>;
|
|
189
|
+
checkAndSaveNonce(nonce: string): Promise<boolean>;
|
|
190
|
+
/** Clear all stored data (for testing) */
|
|
191
|
+
clear(): void;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/client-resolver.d.ts
|
|
195
|
+
/**
|
|
196
|
+
* Client resolution error
|
|
197
|
+
*/
|
|
198
|
+
declare class ClientResolutionError extends Error {
|
|
199
|
+
readonly code: string;
|
|
200
|
+
constructor(message: string, code: string);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Options for client resolution
|
|
204
|
+
*/
|
|
205
|
+
interface ClientResolverOptions {
|
|
206
|
+
/** Storage for caching client metadata */
|
|
207
|
+
storage?: OAuthStorage;
|
|
208
|
+
/** Cache TTL in milliseconds (default: 1 hour) */
|
|
209
|
+
cacheTtl?: number;
|
|
210
|
+
/** Fetch function for making HTTP requests (for testing) */
|
|
211
|
+
fetch?: typeof globalThis.fetch;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Resolve client metadata from a DID
|
|
215
|
+
*/
|
|
216
|
+
declare class ClientResolver {
|
|
217
|
+
private storage?;
|
|
218
|
+
private cacheTtl;
|
|
219
|
+
private fetchFn;
|
|
220
|
+
constructor(options?: ClientResolverOptions);
|
|
221
|
+
/**
|
|
222
|
+
* Resolve client metadata from a client ID (URL or DID)
|
|
223
|
+
* @param clientId The client ID (HTTPS URL or DID)
|
|
224
|
+
* @returns The client metadata
|
|
225
|
+
* @throws ClientResolutionError if resolution fails
|
|
226
|
+
*/
|
|
227
|
+
resolveClient(clientId: string): Promise<ClientMetadata>;
|
|
228
|
+
/**
|
|
229
|
+
* Validate that a redirect URI is allowed for a client
|
|
230
|
+
* @param clientId The client DID
|
|
231
|
+
* @param redirectUri The redirect URI to validate
|
|
232
|
+
* @returns true if the redirect URI is allowed
|
|
233
|
+
*/
|
|
234
|
+
validateRedirectUri(clientId: string, redirectUri: string): Promise<boolean>;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Create a client resolver with optional caching
|
|
238
|
+
*/
|
|
239
|
+
declare function createClientResolver(options?: ClientResolverOptions): ClientResolver;
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/provider.d.ts
|
|
242
|
+
/**
|
|
243
|
+
* OAuth provider configuration
|
|
244
|
+
*/
|
|
245
|
+
interface OAuthProviderConfig {
|
|
246
|
+
/** OAuth storage implementation */
|
|
247
|
+
storage: OAuthStorage;
|
|
248
|
+
/** The OAuth issuer URL (e.g., https://your-pds.com) */
|
|
249
|
+
issuer: string;
|
|
250
|
+
/** Whether DPoP is required for all tokens (default: true for AT Protocol) */
|
|
251
|
+
dpopRequired?: boolean;
|
|
252
|
+
/** Whether PAR is enabled (default: true) */
|
|
253
|
+
enablePAR?: boolean;
|
|
254
|
+
/** Client resolver for DID-based discovery */
|
|
255
|
+
clientResolver?: ClientResolver;
|
|
256
|
+
/** Callback to verify user credentials */
|
|
257
|
+
verifyUser?: (password: string) => Promise<{
|
|
258
|
+
sub: string;
|
|
259
|
+
handle: string;
|
|
260
|
+
} | null>;
|
|
261
|
+
/** Get the current user (if already authenticated) */
|
|
262
|
+
getCurrentUser?: () => Promise<{
|
|
263
|
+
sub: string;
|
|
264
|
+
handle: string;
|
|
265
|
+
} | null>;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Error thrown when request body parsing fails
|
|
269
|
+
*/
|
|
270
|
+
declare class RequestBodyError extends Error {
|
|
271
|
+
constructor(message: string);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Parse request body from JSON or form-urlencoded
|
|
275
|
+
* @throws RequestBodyError if content type is unsupported or parsing fails
|
|
276
|
+
*/
|
|
277
|
+
declare function parseRequestBody(request: Request): Promise<Record<string, string>>;
|
|
278
|
+
/**
|
|
279
|
+
* AT Protocol OAuth 2.1 Provider
|
|
280
|
+
*/
|
|
281
|
+
declare class ATProtoOAuthProvider {
|
|
282
|
+
private storage;
|
|
283
|
+
private issuer;
|
|
284
|
+
private dpopRequired;
|
|
285
|
+
private enablePAR;
|
|
286
|
+
private parHandler;
|
|
287
|
+
private clientResolver;
|
|
288
|
+
private verifyUser?;
|
|
289
|
+
private getCurrentUser?;
|
|
290
|
+
constructor(config: OAuthProviderConfig);
|
|
291
|
+
/**
|
|
292
|
+
* Handle authorization request (GET/POST /oauth/authorize)
|
|
293
|
+
*/
|
|
294
|
+
handleAuthorize(request: Request): Promise<Response>;
|
|
295
|
+
/**
|
|
296
|
+
* Handle authorization form POST
|
|
297
|
+
*/
|
|
298
|
+
private handleAuthorizePost;
|
|
299
|
+
/**
|
|
300
|
+
* Handle token request (POST /oauth/token)
|
|
301
|
+
*/
|
|
302
|
+
handleToken(request: Request): Promise<Response>;
|
|
303
|
+
/**
|
|
304
|
+
* Handle authorization code grant
|
|
305
|
+
*/
|
|
306
|
+
private handleAuthorizationCodeGrant;
|
|
307
|
+
/**
|
|
308
|
+
* Handle refresh token grant
|
|
309
|
+
*/
|
|
310
|
+
private handleRefreshTokenGrant;
|
|
311
|
+
/**
|
|
312
|
+
* Handle PAR request (POST /oauth/par)
|
|
313
|
+
*/
|
|
314
|
+
handlePAR(request: Request): Promise<Response>;
|
|
315
|
+
/**
|
|
316
|
+
* Handle metadata request (GET /.well-known/oauth-authorization-server)
|
|
317
|
+
*/
|
|
318
|
+
handleMetadata(): Response;
|
|
319
|
+
/**
|
|
320
|
+
* Verify an access token from a request
|
|
321
|
+
* @param request The HTTP request
|
|
322
|
+
* @param requiredScope Optional scope to require
|
|
323
|
+
* @returns Token data if valid
|
|
324
|
+
*/
|
|
325
|
+
verifyAccessToken(request: Request, requiredScope?: string): Promise<TokenData | null>;
|
|
326
|
+
/**
|
|
327
|
+
* Render an error page
|
|
328
|
+
*/
|
|
329
|
+
private renderError;
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/pkce.d.ts
|
|
333
|
+
/**
|
|
334
|
+
* PKCE (Proof Key for Code Exchange) verification
|
|
335
|
+
* Implements RFC 7636 with S256 challenge method
|
|
336
|
+
*/
|
|
337
|
+
/**
|
|
338
|
+
* Verify a PKCE code challenge against a verifier
|
|
339
|
+
* @param verifier The code verifier from the token request
|
|
340
|
+
* @param challenge The code challenge from the authorization request
|
|
341
|
+
* @param method The challenge method (only S256 supported for AT Protocol)
|
|
342
|
+
* @returns true if the verifier matches the challenge
|
|
343
|
+
*/
|
|
344
|
+
declare function verifyPkceChallenge(verifier: string, challenge: string, method: "S256"): Promise<boolean>;
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/dpop.d.ts
|
|
347
|
+
/**
|
|
348
|
+
* Verified DPoP proof data
|
|
349
|
+
*/
|
|
350
|
+
interface DpopProof {
|
|
351
|
+
/** HTTP method from the proof */
|
|
352
|
+
htm: string;
|
|
353
|
+
/** HTTP URI from the proof (without query/fragment) */
|
|
354
|
+
htu: string;
|
|
355
|
+
/** Unique proof identifier (for replay prevention) */
|
|
356
|
+
jti: string;
|
|
357
|
+
/** Access token hash (if present) */
|
|
358
|
+
ath?: string;
|
|
359
|
+
/** Key thumbprint (JWK thumbprint of the proof key) */
|
|
360
|
+
jkt: string;
|
|
361
|
+
/** The public JWK from the proof */
|
|
362
|
+
jwk: JWK;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* DPoP verification options
|
|
366
|
+
*/
|
|
367
|
+
interface DpopVerifyOptions {
|
|
368
|
+
/** Access token to verify ath claim against (optional) */
|
|
369
|
+
accessToken?: string;
|
|
370
|
+
/** Allowed signature algorithms (default: ['ES256']) */
|
|
371
|
+
allowedAlgorithms?: string[];
|
|
372
|
+
/** Expected nonce value (optional, for nonce binding) */
|
|
373
|
+
expectedNonce?: string;
|
|
374
|
+
/** Max token age in seconds (default: 60) */
|
|
375
|
+
maxTokenAge?: number;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* DPoP verification error
|
|
379
|
+
*/
|
|
380
|
+
declare class DpopError extends Error {
|
|
381
|
+
readonly code: string;
|
|
382
|
+
constructor(message: string, code: string, options?: ErrorOptions);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Verify a DPoP proof from a request
|
|
386
|
+
* Uses jose library for JWT verification
|
|
387
|
+
* @param request The HTTP request containing the DPoP header
|
|
388
|
+
* @param options Verification options
|
|
389
|
+
* @returns The verified proof data
|
|
390
|
+
* @throws DpopError if verification fails
|
|
391
|
+
*/
|
|
392
|
+
declare function verifyDpopProof(request: Request, options?: DpopVerifyOptions): Promise<DpopProof>;
|
|
393
|
+
/**
|
|
394
|
+
* Generate a random DPoP nonce
|
|
395
|
+
* @returns A base64url-encoded random nonce (16 bytes)
|
|
396
|
+
*/
|
|
397
|
+
declare function generateDpopNonce(): string;
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/par.d.ts
|
|
400
|
+
/**
|
|
401
|
+
* OAuth error response
|
|
402
|
+
*/
|
|
403
|
+
interface OAuthErrorResponse {
|
|
404
|
+
error: string;
|
|
405
|
+
error_description?: string;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Handler for Pushed Authorization Requests (PAR)
|
|
409
|
+
*/
|
|
410
|
+
declare class PARHandler {
|
|
411
|
+
private storage;
|
|
412
|
+
private issuer;
|
|
413
|
+
private expiresIn;
|
|
414
|
+
/**
|
|
415
|
+
* Create a PAR handler
|
|
416
|
+
* @param storage OAuth storage implementation
|
|
417
|
+
* @param issuer The OAuth issuer URL
|
|
418
|
+
* @param expiresIn PAR expiration time in seconds (default: 90)
|
|
419
|
+
*/
|
|
420
|
+
constructor(storage: OAuthStorage, issuer: string, expiresIn?: number);
|
|
421
|
+
/**
|
|
422
|
+
* Handle a PAR push request
|
|
423
|
+
* POST /oauth/par
|
|
424
|
+
* @param request The HTTP request
|
|
425
|
+
* @returns Response with request_uri or error
|
|
426
|
+
*/
|
|
427
|
+
handlePushRequest(request: Request): Promise<Response>;
|
|
428
|
+
/**
|
|
429
|
+
* Retrieve and consume PAR parameters
|
|
430
|
+
* Called during authorization request handling
|
|
431
|
+
* @param requestUri The request URI from the authorization request
|
|
432
|
+
* @param clientId The client_id from the authorization request (for verification)
|
|
433
|
+
* @returns The stored parameters or null if not found/expired
|
|
434
|
+
*/
|
|
435
|
+
retrieveParams(requestUri: string, clientId: string): Promise<Record<string, string> | null>;
|
|
436
|
+
/**
|
|
437
|
+
* Check if a request_uri is valid format
|
|
438
|
+
*/
|
|
439
|
+
static isRequestUri(value: string): boolean;
|
|
440
|
+
/**
|
|
441
|
+
* Create an OAuth error response
|
|
442
|
+
*/
|
|
443
|
+
private errorResponse;
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/tokens.d.ts
|
|
447
|
+
/** Default access token TTL: 1 hour */
|
|
448
|
+
declare const ACCESS_TOKEN_TTL: number;
|
|
449
|
+
/** Default refresh token TTL: 90 days */
|
|
450
|
+
declare const REFRESH_TOKEN_TTL: number;
|
|
451
|
+
/** Authorization code TTL: 5 minutes */
|
|
452
|
+
declare const AUTH_CODE_TTL: number;
|
|
453
|
+
/**
|
|
454
|
+
* Generate a cryptographically random token
|
|
455
|
+
* @param bytes Number of random bytes (default: 32)
|
|
456
|
+
* @returns Base64URL-encoded token
|
|
457
|
+
*/
|
|
458
|
+
declare function generateRandomToken(bytes?: number): string;
|
|
459
|
+
/**
|
|
460
|
+
* Generate an authorization code
|
|
461
|
+
* @returns A random authorization code
|
|
462
|
+
*/
|
|
463
|
+
declare function generateAuthCode(): string;
|
|
464
|
+
/**
|
|
465
|
+
* Token generation result
|
|
466
|
+
*/
|
|
467
|
+
interface GeneratedTokens {
|
|
468
|
+
/** Opaque access token */
|
|
469
|
+
accessToken: string;
|
|
470
|
+
/** Opaque refresh token */
|
|
471
|
+
refreshToken: string;
|
|
472
|
+
/** Access token type (Bearer or DPoP) */
|
|
473
|
+
tokenType: "Bearer" | "DPoP";
|
|
474
|
+
/** Access token expiration in seconds */
|
|
475
|
+
expiresIn: number;
|
|
476
|
+
/** Scope granted */
|
|
477
|
+
scope: string;
|
|
478
|
+
/** Subject (user DID) */
|
|
479
|
+
sub: string;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Options for token generation
|
|
483
|
+
*/
|
|
484
|
+
interface GenerateTokensOptions {
|
|
485
|
+
/** User DID */
|
|
486
|
+
sub: string;
|
|
487
|
+
/** Client DID */
|
|
488
|
+
clientId: string;
|
|
489
|
+
/** Scope granted */
|
|
490
|
+
scope: string;
|
|
491
|
+
/** DPoP key thumbprint (if using DPoP) */
|
|
492
|
+
dpopJkt?: string;
|
|
493
|
+
/** Custom access token TTL in ms (default: 1 hour) */
|
|
494
|
+
accessTokenTtl?: number;
|
|
495
|
+
/** Custom refresh token TTL in ms (default: 90 days) */
|
|
496
|
+
refreshTokenTtl?: number;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Generate access and refresh tokens
|
|
500
|
+
* Tokens are opaque - their meaning comes from the database entry
|
|
501
|
+
* @param options Token generation options
|
|
502
|
+
* @returns Generated tokens and metadata
|
|
503
|
+
*/
|
|
504
|
+
declare function generateTokens(options: GenerateTokensOptions): {
|
|
505
|
+
tokens: GeneratedTokens;
|
|
506
|
+
tokenData: TokenData;
|
|
507
|
+
};
|
|
508
|
+
/**
|
|
509
|
+
* Refresh tokens - generates new access token, optionally rotates refresh token
|
|
510
|
+
* @param existingData The existing token data
|
|
511
|
+
* @param rotateRefreshToken Whether to generate a new refresh token
|
|
512
|
+
* @param accessTokenTtl Custom access token TTL in ms
|
|
513
|
+
* @returns Updated tokens and token data
|
|
514
|
+
*/
|
|
515
|
+
declare function refreshTokens(existingData: TokenData, rotateRefreshToken?: boolean, accessTokenTtl?: number): {
|
|
516
|
+
tokens: GeneratedTokens;
|
|
517
|
+
tokenData: TokenData;
|
|
518
|
+
};
|
|
519
|
+
/**
|
|
520
|
+
* Build token response for OAuth token endpoint
|
|
521
|
+
* @param tokens The generated tokens
|
|
522
|
+
* @returns JSON-serializable token response
|
|
523
|
+
*/
|
|
524
|
+
declare function buildTokenResponse(tokens: GeneratedTokens): OAuthTokenResponse;
|
|
525
|
+
/**
|
|
526
|
+
* Extract access token from Authorization header
|
|
527
|
+
* Supports both Bearer and DPoP token types
|
|
528
|
+
* @param request The HTTP request
|
|
529
|
+
* @returns The access token and type, or null if not found
|
|
530
|
+
*/
|
|
531
|
+
declare function extractAccessToken(request: Request): {
|
|
532
|
+
token: string;
|
|
533
|
+
type: "Bearer" | "DPoP";
|
|
534
|
+
} | null;
|
|
535
|
+
/**
|
|
536
|
+
* Validate that a token is not expired or revoked
|
|
537
|
+
* @param tokenData The token data from storage
|
|
538
|
+
* @returns true if the token is valid
|
|
539
|
+
*/
|
|
540
|
+
declare function isTokenValid(tokenData: TokenData): boolean;
|
|
541
|
+
//#endregion
|
|
542
|
+
//#region src/ui.d.ts
|
|
543
|
+
/**
|
|
544
|
+
* Content Security Policy for the consent UI
|
|
545
|
+
*
|
|
546
|
+
* - default-src 'none': Deny all by default
|
|
547
|
+
* - style-src 'unsafe-inline': Allow inline styles (our CSS is inline)
|
|
548
|
+
* - img-src https: data:: Allow images from HTTPS URLs (client logos) and data URIs
|
|
549
|
+
* - form-action 'self': Form can only POST to same origin
|
|
550
|
+
* - frame-ancestors 'none': Prevent clickjacking by disallowing framing
|
|
551
|
+
* - base-uri 'none': Prevent base tag injection
|
|
552
|
+
*/
|
|
553
|
+
declare const CONSENT_UI_CSP = "default-src 'none'; style-src 'unsafe-inline'; img-src https: data:; form-action 'self'; frame-ancestors 'none'; base-uri 'none'";
|
|
554
|
+
/**
|
|
555
|
+
* Options for rendering the consent UI
|
|
556
|
+
*/
|
|
557
|
+
interface ConsentUIOptions {
|
|
558
|
+
/** The OAuth client metadata */
|
|
559
|
+
client: ClientMetadata;
|
|
560
|
+
/** The requested scope */
|
|
561
|
+
scope: string;
|
|
562
|
+
/** URL to POST the consent form to */
|
|
563
|
+
authorizeUrl: string;
|
|
564
|
+
/** State parameter to include in the form */
|
|
565
|
+
state: string;
|
|
566
|
+
/** OAuth parameters to include as hidden fields */
|
|
567
|
+
oauthParams: Record<string, string>;
|
|
568
|
+
/** User's handle (for display) */
|
|
569
|
+
userHandle?: string;
|
|
570
|
+
/** Whether to show a login form instead of consent */
|
|
571
|
+
showLogin?: boolean;
|
|
572
|
+
/** Error message to display */
|
|
573
|
+
error?: string;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Render the consent UI HTML
|
|
577
|
+
* @param options Consent UI options
|
|
578
|
+
* @returns HTML string
|
|
579
|
+
*/
|
|
580
|
+
declare function renderConsentUI(options: ConsentUIOptions): string;
|
|
581
|
+
/**
|
|
582
|
+
* Render an error page
|
|
583
|
+
* @param error Error code
|
|
584
|
+
* @param description Error description
|
|
585
|
+
* @param redirectUri Optional redirect URI for the error
|
|
586
|
+
* @returns HTML string
|
|
587
|
+
*/
|
|
588
|
+
declare function renderErrorPage(error: string, description: string, redirectUri?: string): string;
|
|
589
|
+
//#endregion
|
|
590
|
+
export { ACCESS_TOKEN_TTL, ATProtoOAuthProvider, AUTH_CODE_TTL, type AuthCodeData, CONSENT_UI_CSP, type ClientMetadata, ClientResolutionError, ClientResolver, type ClientResolverOptions, type ConsentUIOptions, DpopError, type DpopProof, type DpopVerifyOptions, type GenerateTokensOptions, type GeneratedTokens, InMemoryOAuthStorage, type OAuthClientMetadata, type OAuthErrorResponse, type OAuthParResponse, type OAuthProviderConfig, type OAuthStorage, type PARData, PARHandler, REFRESH_TOKEN_TTL, RequestBodyError, type TokenData, buildTokenResponse, createClientResolver, extractAccessToken, generateAuthCode, generateDpopNonce, generateRandomToken, generateTokens, isTokenValid, parseRequestBody, refreshTokens, renderConsentUI, renderErrorPage, verifyDpopProof, verifyPkceChallenge };
|
|
591
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/storage.ts","../src/client-resolver.ts","../src/provider.ts","../src/pkce.ts","../src/dpop.ts","../src/par.ts","../src/tokens.ts","../src/ui.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAQA;AAoBA;AAwBA;AAkBiB,UA9DA,YAAA,CAkER;EASQ;EAUiB,QAAA,EAAA,MAAA;EAAe;EAOb,WAAA,EAAA,MAAA;EAAR;EAMG,aAAA,EAAA,MAAA;EAUb;EAAY,mBAAA,EAAA,MAAA;EAOkB;EAAR,KAAA,EAAA,MAAA;EAOU;EAAR,GAAA,EAAA,MAAA;EAMP;EAMH,SAAA,EAAA,MAAA;;;;;AA6BG,UA/IlB,SAAA,CA+IkB;EAAU;EAOR,WAAA,EAAA,MAAA;EAAR;EAMG,YAAA,EAAA,MAAA;EAYG;EAAO,QAAA,EAAA,MAAA;EAM7B;EAQ2B,GAAA,EAAA,MAAA;EAAe;EAIb,KAAA,EAAA,MAAA;EAAR;EAUG,OAAA,CAAA,EAAA,MAAA;EAIb;EAAY,QAAA,EAAA,MAAA;EAKkB;EAAR,SAAA,EAAA,MAAA;EASU;EAAR,OAAA,CAAA,EAAA,OAAA;;;;;AA6BJ,UA3N3B,cAAA,CA2N2B;EAAR;EAIK,QAAA,EAAA,MAAA;EAAU;EAIR,UAAA,EAAA,MAAA;EAAR;EAUG,YAAA,EAAA,MAAA,EAAA;EAIG;EA3FI,OAAA,CAAA,EAAA,MAAA;EAAY;;;;ACzLzD;AAaA;AA6DA;;AAiBgD,UDtC/B,OAAA,CCsC+B;EAAR;EAoF2B,QAAA,EAAA,MAAA;EAAO;EAa1D,MAAA,EDnIP,MCmIO,CAAA,MAAA,EAAoB,MAAA,CAAA;;;;ACpLpC;;;;AAcwB,UF4CP,YAAA,CE5CO;EAAO;AAyB/B;AAWA;;;EAA0D,YAAA,CAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EFkBxB,YElBwB,CAAA,EFkBT,OElBS,CAAA,IAAA,CAAA;EAAO;AA4BjE;;;;EAwB0C,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EF3Bd,OE2Bc,CF3BN,YE2BM,GAAA,IAAA,CAAA;EAiNd;;;;EAsNgB,cAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EF5bb,OE4ba,CAAA,IAAA,CAAA;EAAR;;;;EAqDhC,UAAA,CAAA,IAAA,EFvec,SEued,CAAA,EFve0B,OEue1B,CAAA,IAAA,CAAA;EAAO;;;;AClkBX;yCHkGwC,QAAQ;;;AI7GhD;AAkBA;AAcA;EA+CsB,iBAAA,CAAe,YAAA,EAAA,MAAA,CAAA,EJqCK,OIrCL,CJqCa,SIrCb,GAAA,IAAA,CAAA;EAC3B;;;;EAEA,WAAA,CAAA,WAAA,EAAA,MAAA,CAAA,EJwCyB,OIxCzB,CAAA,IAAA,CAAA;EA8FM;;;;ECzKC,eAAA,EAAA,GAAA,EAAkB,MAAA,CAAA,ELyHH,OKzHG,CAAA,IAAA,CAAA;EAoBtB;;;;;EAgHD,UAAA,CAAA,QAAA,EAAA,MAAA,EAAA,QAAA,ELA4B,cKA5B,CAAA,ELA6C,OKA7C,CAAA,IAAA,CAAA;EAAR;;;;;EC/IS,SAAA,CAAA,QAAA,EAAiC,MAAA,CAAA,ENsJhB,OMtJgB,CNsJR,cMtJQ,GAAA,IAAA,CAAA;EAGjC;AAGb;AAOA;AAQA;AAOA;EAkBiB,OAAA,CAAA,UAAA,EAAA,MAAqB,EAAA,IAAA,ENmHH,OMnHG,CAAA,ENmHO,OMnHP,CAAA,IAAA,CAAA;EAqBtB;;;;;EA+CA,MAAA,CAAA,UAAa,EAAA,MAAA,CAAA,ENsDA,OMtDA,CNsDQ,OMtDR,GAAA,IAAA,CAAA;EACd;;;;EAoCC,SAAA,CAAA,UAAA,EAAkB,MAAA,CAAA,ENuBF,OMvBW,CAAA,IAAA,CAAA;EAiB3B;AA8BhB;;;;AC/LA;EAkDiB,iBAAA,CAAA,KAAgB,EAAA,MAExB,CAAA,EP+H0B,OO/H1B,CAAA,OAQK,CAAA;AAcd;AAoRA;;;cPrKa,oBAAA,YAAgC;;;;;;;mCAQL,eAAe;6BAIrB,QAAQ;gCAUL;mBAIb,YAAY;yCAKU,QAAQ;2CASN,QAAQ;oCAUf;gCAOJ;yCAQS,iBAAiB;+BAI3B,QAAQ;oCAIH,UAAU;8BAIhB,QAAQ;iCAUL;oCAIG;;;;;;AA/NzC;AAaA;;AAUiD,cC5EpC,qBAAA,SAA8B,KAAA,CD4EM;EAOb,SAAA,IAAA,EAAA,MAAA;EAAR,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA;;;;;AAuBY,UC7FvB,qBAAA,CD6FuB;EAOU;EAAR,OAAA,CAAA,EClG/B,YDkG+B;EAMP;EAMH,QAAA,CAAA,EAAA,MAAA;EAWQ;EAAiB,KAAA,CAAA,EAAA,OCrHzC,UAAA,CAAW,KDqH8B;;;;;AAyBpB,cCvFxB,cAAA,CDuFwB;EAAR,QAAA,OAAA;EAMG,QAAA,QAAA;EAYG,QAAA,OAAA;EAAO,WAAA,CAAA,OAAA,CAAA,ECpGpB,qBDoGoB;EAM7B;;;;;;EA0BW,aAAA,CAAA,QAAA,EAAA,MAAA,CAAA,ECxHgB,ODwHhB,CCxHwB,cDwHxB,CAAA;EAAY;;;;;;EA+BC,mBAAA,CAAA,QAAA,EAAA,MAAA,EAAA,WAAA,EAAA,MAAA,CAAA,ECnE8B,ODmE9B,CAAA,OAAA,CAAA;;;;;AAgBI,iBCtEzB,oBAAA,CDsEyB,OAAA,CAAA,ECtEK,qBDsEL,CAAA,ECtEkC,cDsElC;;;AA/NzC;AAkBA;AAaA;AAUkC,UEpEjB,mBAAA,CFoEiB;EAAe;EAOb,OAAA,EEzE1B,YFyE0B;EAAR;EAMG,MAAA,EAAA,MAAA;EAUb;EAAY,YAAA,CAAA,EAAA,OAAA;EAOkB;EAAR,SAAA,CAAA,EAAA,OAAA;EAOU;EAAR,cAAA,CAAA,EE/FxB,cF+FwB;EAMP;EAMH,UAAA,CAAA,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,GEzGI,OFyGJ,CAAA;IAWQ,GAAA,EAAA,MAAA;IAAiB,MAAA,EAAA,MAAA;EAOnB,CAAA,GAAA,IAAA,CAAA;EAAR;EAWK,cAAA,CAAA,EAAA,GAAA,GEpIX,OFoIW,CAAA;IAAU,GAAA,EAAA,MAAA;IAOR,MAAA,EAAA,MAAA;EAAR,CAAA,GAAA,IAAA,CAAA;;;;AAwB7B;AAQwC,cElJ3B,gBAAA,SAAyB,KAAA,CFkJE;EAAe,WAAA,CAAA,OAAA,EAAA,MAAA;;;;;;AAuBD,iBE9JhC,gBAAA,CF8JgC,OAAA,EE9JN,OF8JM,CAAA,EE9JI,OF8JJ,CE9JY,MF8JZ,CAAA,MAAA,EAAA,MAAA,CAAA,CAAA;;;;AAmBb,cErJ5B,oBAAA,CFqJ4B;EAOJ,QAAA,OAAA;EAQS,QAAA,MAAA;EAAiB,QAAA,YAAA;EAInB,QAAA,SAAA;EAAR,QAAA,UAAA;EAIK,QAAA,cAAA;EAAU,QAAA,UAAA;EAIR,QAAA,cAAA;EAAR,WAAA,CAAA,MAAA,EEtKd,mBFsKc;EAUG;;;EAvFmB,eAAA,CAAA,OAAA,EE3EzB,OF2EyB,CAAA,EE3Ef,OF2Ee,CE3EP,QF2EO,CAAA;;;;ECzL5C,QAAA,mBAAsB;EAalB;AA6DjB;;EAiBgD,WAAA,CAAA,OAAA,ECoOpB,ODpOoB,CAAA,ECoOV,ODpOU,CCoOF,QDpOE,CAAA;EAAR;;;EAiGxB,QAAA,4BAA8B;;;;ECpL7B,QAAA,uBAAmB;EAE1B;;;EAYc,SAAA,CAAA,OAAA,EA+fE,OA/fF,CAAA,EA+fY,OA/fZ,CA+foB,QA/fpB,CAAA;EAAO;AAyB/B;AAWA;EAAgD,cAAA,CAAA,CAAA,EAqe7B,QAre6B;EAAkB;;;AA4BlE;;;EAwBkD,iBAAA,CAAA,OAAA,EA0dvC,OA1duC,EAAA,aAAA,CAAA,EAAA,MAAA,CAAA,EA4d9C,OA5d8C,CA4dtC,SA5dsC,GAAA,IAAA,CAAA;EAAR;;;EAiNJ,QAAA,WAAA;;;;;;;;AFxUtC;AAoBA;AAwBA;AAkBA;AAaA;;;AAiBoC,iBG3Ed,mBAAA,CH2Ec,QAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EGvEjC,OHuEiC,CAAA,OAAA,CAAA;;;AAxEpC;AAwBA;AAkBA;AAaiB,UIrEA,SAAA,CJqEY;EAUK;EAAe,GAAA,EAAA,MAAA;EAOb;EAAR,GAAA,EAAA,MAAA;EAMG;EAUb,GAAA,EAAA,MAAA;EAAY;EAOkB,GAAA,CAAA,EAAA,MAAA;EAAR;EAOU,GAAA,EAAA,MAAA;EAAR;EAMP,GAAA,EI9G7B,GJ8G6B;;;;;AAwBL,UIhIb,iBAAA,CJgIa;EAWK;EAAU,WAAA,CAAA,EAAA,MAAA;EAOR;EAAR,iBAAA,CAAA,EAAA,MAAA,EAAA;EAMG;EAYG,aAAA,CAAA,EAAA,MAAA;EAAO;EAM7B,WAAA,CAAA,EAAA,MAAA;;;;;AAsBwB,cIlLxB,SAAA,SAAkB,KAAA,CJkLM;EAIb,SAAA,IAAA,EAAA,MAAA;EAAY,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EIpLkB,YJoLlB;;;;;;;;;;AA2CA,iBIlLd,eAAA,CJkLc,OAAA,EIjL1B,OJiL0B,EAAA,OAAA,CAAA,EIhL1B,iBJgL0B,CAAA,EI/KjC,OJ+KiC,CI/KzB,SJ+KyB,CAAA;;;;;AAkBE,iBInGtB,iBAAA,CAAA,CJmGsB,EAAA,MAAA;;;AA3NtC;AAaA;;AAUiD,UKxEhC,kBAAA,CLwEgC;EAOb,KAAA,EAAA,MAAA;EAAR,iBAAA,CAAA,EAAA,MAAA;;;;;AAuBY,cKlF3B,UAAA,CLkF2B;EAOU,QAAA,OAAA;EAAR,QAAA,MAAA;EAMP,QAAA,SAAA;EAMH;;;;;;EA6Ba,WAAA,CAAA,OAAA,EKvHvB,YLuHuB,EAAA,MAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAAA,MAAA;EAOR;;;;;AAwBrC;EAQwC,iBAAA,CAAA,OAAA,EKlJN,OLkJM,CAAA,EKlJI,OLkJJ,CKlJY,QLkJZ,CAAA;EAAe;;;;;;;EAuBT,cAAA,CAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,CAAA,EKhF1C,OLgF0C,CKhFlC,MLgFkC,CAAA,MAAA,EAAA,MAAA,CAAA,GAAA,IAAA,CAAA;EASU;;;EAiBnB,OAAA,YAAA,CAAA,KAAA,EAAA,MAAA,CAAA,EAAA,OAAA;EAQS;;;EAIV,QAAA,aAAA;;;;AA3NpC;AAkBiB,cM5DJ,gBNgEE,EAAA,MAAA;AASf;AAUkC,cMhFrB,iBNgFqB,EAAA,MAAA;;AAOE,cMpFvB,aNoFuB,EAAA,MAAA;;;;;;AAuBI,iBMpGxB,mBAAA,CNoGwB,KAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;;AA8BA,iBM1HxB,gBAAA,CAAA,CN0HwB,EAAA,MAAA;;;;AAkBL,UMrIlB,eAAA,CNqIkB;EAAU;EAOR,WAAA,EAAA,MAAA;EAAR;EAMG,YAAA,EAAA,MAAA;EAYG;EAAO,SAAA,EAAA,QAAA,GAAA,MAAA;EAM7B;EAQ2B,SAAA,EAAA,MAAA;EAAe;EAIb,KAAA,EAAA,MAAA;EAAR;EAUG,GAAA,EAAA,MAAA;;;;;AAkBmB,UM1LvC,qBAAA,CN0LuC;EAAR;EAUP,GAAA,EAAA,MAAA;EAOJ;EAQS,QAAA,EAAA,MAAA;EAAiB;EAInB,KAAA,EAAA,MAAA;EAAR;EAIK,OAAA,CAAA,EAAA,MAAA;EAAU;EAIR,cAAA,CAAA,EAAA,MAAA;EAAR;EAUG,eAAA,CAAA,EAAA,MAAA;;;;;;;AChRtC;AAaiB,iBK+CD,cAAA,CL/CsB,OAE3B,EK6C6B,qBLzCb,CAAA,EAAK;EAuDnB,MAAA,EKbJ,eLakB;EAKL,SAAA,EKjBV,SLiBU;CAY0B;;;;AAiGhD;;;;ACpLiB,iBImGD,aAAA,CJnGoB,YAAA,EIoGrB,SJpGqB,EAAA,kBAAA,CAAA,EAAA,OAAA,EAAA,cAAA,CAAA,EAAA,MAAA,CAAA,EAAA;EAE1B,MAAA,EIsGD,eJtGC;EAQQ,SAAA,EI+FN,SJ/FM;CAEkB;;;AA2BpC;AAWA;;AAAkE,iBIsFlD,kBAAA,CJtFkD,MAAA,EIsFvB,eJtFuB,CAAA,EIsFL,kBJtFK;;;AA4BlE;;;;AAwB0C,iBImD1B,kBAAA,CJnD0B,OAAA,EIoDhC,OJpDgC,CAAA,EAAA;EAiNd,KAAA,EAAA,MAAA;EAAkB,IAAA,EAAA,QAAA,GAAA,MAAA;CAAR,GAAA,IAAA;;;;;;AA2Q1B,iBI3YI,YAAA,CJ2YJ,SAAA,EI3Y4B,SJ2Y5B,CAAA,EAAA,OAAA;;;AF/jBZ;AAwBA;AAkBA;AAaA;;;;;;;AAiC8B,cOnGjB,cAAA,GPmGiB,kIAAA;;;;AAcY,UO/DzB,gBAAA,CP+DyB;EAMP;EAMH,MAAA,EOzEvB,cPyEuB;EAWQ;EAAiB,KAAA,EAAA,MAAA;EAOnB;EAAR,YAAA,EAAA,MAAA;EAWK;EAAU,KAAA,EAAA,MAAA;EAOR;EAAR,WAAA,EOrGf,MPqGe,CAAA,MAAA,EAAA,MAAA,CAAA;EAMG;EAYG,UAAA,CAAA,EAAA,MAAA;EAAO;EAM7B,SAAA,CAAA,EAAA,OAAA;EAQ2B;EAAe,KAAA,CAAA,EAAA,MAAA;;;;;;;AAuBT,iBO9I9B,eAAA,CP8I8B,OAAA,EO9IL,gBP8IK,CAAA,EAAA,MAAA;;;;;;;;AAsCV,iBOgGpB,eAAA,CPhGoB,KAAA,EAAA,MAAA,EAAA,WAAA,EAAA,MAAA,EAAA,WAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA"}
|