@happyview/oauth-client 1.1.0-dev.2 → 1.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/dist/index.cjs +5 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -63
- package/dist/index.d.ts +21 -63
- package/dist/index.js +4 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -15,7 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
15
|
}
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
19
|
|
|
21
20
|
// src/index.ts
|
|
@@ -29,14 +28,11 @@ __export(index_exports, {
|
|
|
29
28
|
InvalidStateError: () => InvalidStateError,
|
|
30
29
|
LAST_ACTIVE_KEY: () => LAST_ACTIVE_KEY,
|
|
31
30
|
MemoryStorage: () => MemoryStorage,
|
|
32
|
-
OAuthCallbackError: () => OAuthCallbackError,
|
|
33
31
|
ResolutionError: () => ResolutionError,
|
|
34
32
|
TokenExchangeError: () => TokenExchangeError,
|
|
35
33
|
importJwk: () => importJwk
|
|
36
34
|
});
|
|
37
35
|
module.exports = __toCommonJS(index_exports);
|
|
38
|
-
__reExport(index_exports, require("@atproto/jwk"), module.exports);
|
|
39
|
-
__reExport(index_exports, require("@atproto/jwk-webcrypto"), module.exports);
|
|
40
36
|
|
|
41
37
|
// src/errors.ts
|
|
42
38
|
var HappyViewError = class extends Error {
|
|
@@ -85,26 +81,6 @@ var ResolutionError = class extends HappyViewError {
|
|
|
85
81
|
this.name = "ResolutionError";
|
|
86
82
|
}
|
|
87
83
|
};
|
|
88
|
-
var OAuthCallbackError = class _OAuthCallbackError extends HappyViewError {
|
|
89
|
-
params;
|
|
90
|
-
state;
|
|
91
|
-
static from(err, params, state) {
|
|
92
|
-
if (err instanceof _OAuthCallbackError) return err;
|
|
93
|
-
const message = err instanceof Error ? err.message : void 0;
|
|
94
|
-
return new _OAuthCallbackError(params, message, state, err);
|
|
95
|
-
}
|
|
96
|
-
constructor(params, message, state, cause) {
|
|
97
|
-
super(
|
|
98
|
-
message ?? params.get("error_description") ?? "OAuth callback error"
|
|
99
|
-
);
|
|
100
|
-
this.name = "OAuthCallbackError";
|
|
101
|
-
this.params = params;
|
|
102
|
-
this.state = state;
|
|
103
|
-
if (cause !== void 0) {
|
|
104
|
-
this.cause = cause;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
84
|
|
|
109
85
|
// src/import-jwk.ts
|
|
110
86
|
var import_jwk_webcrypto = require("@atproto/jwk-webcrypto");
|
|
@@ -146,11 +122,7 @@ var HappyViewSession = class {
|
|
|
146
122
|
accessToken;
|
|
147
123
|
clientKey;
|
|
148
124
|
instanceUrl;
|
|
149
|
-
_scopes;
|
|
150
|
-
_pdsUrl;
|
|
151
|
-
_issuer;
|
|
152
125
|
_fetch;
|
|
153
|
-
_onSignOut;
|
|
154
126
|
dpopNonce;
|
|
155
127
|
constructor(options) {
|
|
156
128
|
this.did = options.did;
|
|
@@ -158,27 +130,7 @@ var HappyViewSession = class {
|
|
|
158
130
|
this.accessToken = options.accessToken;
|
|
159
131
|
this.clientKey = options.clientKey;
|
|
160
132
|
this.instanceUrl = options.instanceUrl.replace(/\/+$/, "");
|
|
161
|
-
this._scopes = options.scopes;
|
|
162
|
-
this._pdsUrl = options.pdsUrl;
|
|
163
|
-
this._issuer = options.issuer;
|
|
164
133
|
this._fetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
165
|
-
this._onSignOut = options.onSignOut;
|
|
166
|
-
}
|
|
167
|
-
get sub() {
|
|
168
|
-
return this.did;
|
|
169
|
-
}
|
|
170
|
-
getTokenInfo() {
|
|
171
|
-
return {
|
|
172
|
-
scope: this._scopes,
|
|
173
|
-
iss: this._issuer,
|
|
174
|
-
aud: this._pdsUrl,
|
|
175
|
-
sub: this.did
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
async signOut() {
|
|
179
|
-
if (this._onSignOut) {
|
|
180
|
-
await this._onSignOut();
|
|
181
|
-
}
|
|
182
134
|
}
|
|
183
135
|
async fetchHandler(url, init) {
|
|
184
136
|
const fullUrl = /^https?:\/\//i.test(url) ? url : `${this.instanceUrl}${url}`;
|
|
@@ -257,37 +209,17 @@ var MemoryStorage = class {
|
|
|
257
209
|
var STORAGE_PREFIX = "happyview:session:";
|
|
258
210
|
var LAST_ACTIVE_KEY = "happyview:last-active-did";
|
|
259
211
|
var HappyViewOAuthClient = class {
|
|
260
|
-
static async fetchMetadata({
|
|
261
|
-
clientId,
|
|
262
|
-
fetch: fetchFn = globalThis.fetch,
|
|
263
|
-
signal
|
|
264
|
-
}) {
|
|
265
|
-
signal?.throwIfAborted();
|
|
266
|
-
const resp = await fetchFn(clientId, { redirect: "error", signal });
|
|
267
|
-
if (resp.status !== 200) {
|
|
268
|
-
resp.body?.cancel?.();
|
|
269
|
-
throw new Error(`Failed to fetch client metadata: ${resp.status}`);
|
|
270
|
-
}
|
|
271
|
-
const mime = resp.headers.get("content-type")?.split(";")[0].trim();
|
|
272
|
-
if (mime !== "application/json") {
|
|
273
|
-
resp.body?.cancel?.();
|
|
274
|
-
throw new Error(`Invalid client metadata content type: ${mime}`);
|
|
275
|
-
}
|
|
276
|
-
return resp.json();
|
|
277
|
-
}
|
|
278
212
|
instanceUrl;
|
|
279
213
|
clientKey;
|
|
280
214
|
storage;
|
|
281
215
|
clientSecret;
|
|
282
216
|
_fetch;
|
|
283
|
-
sessionHooks;
|
|
284
217
|
constructor(options) {
|
|
285
218
|
this.instanceUrl = options.instanceUrl.replace(/\/+$/, "");
|
|
286
219
|
this.clientKey = options.clientKey;
|
|
287
220
|
this.clientSecret = options.clientSecret;
|
|
288
221
|
this.storage = options.storage ?? new MemoryStorage();
|
|
289
222
|
this._fetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
290
|
-
this.sessionHooks = options.sessionHooks ?? {};
|
|
291
223
|
}
|
|
292
224
|
get isConfidential() {
|
|
293
225
|
return this.clientSecret !== void 0;
|
|
@@ -369,30 +301,21 @@ var HappyViewOAuthClient = class {
|
|
|
369
301
|
dpopKey: params.dpopKey,
|
|
370
302
|
accessToken: params.accessToken,
|
|
371
303
|
clientKey: this.clientKey,
|
|
372
|
-
instanceUrl: this.instanceUrl
|
|
373
|
-
scopes: params.scopes,
|
|
374
|
-
pdsUrl: params.pdsUrl,
|
|
375
|
-
issuer: params.issuer
|
|
304
|
+
instanceUrl: this.instanceUrl
|
|
376
305
|
};
|
|
377
306
|
await this.storage.set(
|
|
378
307
|
`${STORAGE_PREFIX}${data.did}`,
|
|
379
308
|
JSON.stringify(storedSession)
|
|
380
309
|
);
|
|
381
310
|
await this.storage.set(LAST_ACTIVE_KEY, data.did);
|
|
382
|
-
|
|
311
|
+
return new HappyViewSession({
|
|
383
312
|
did: data.did,
|
|
384
313
|
dpopKey,
|
|
385
314
|
accessToken: params.accessToken,
|
|
386
315
|
clientKey: this.clientKey,
|
|
387
316
|
instanceUrl: this.instanceUrl,
|
|
388
|
-
|
|
389
|
-
pdsUrl: params.pdsUrl,
|
|
390
|
-
issuer: params.issuer,
|
|
391
|
-
fetch: this._fetch,
|
|
392
|
-
onSignOut: () => this.deleteSession(data.did)
|
|
317
|
+
fetch: this._fetch
|
|
393
318
|
});
|
|
394
|
-
this.sessionHooks.onSessionUpdate?.(data.did);
|
|
395
|
-
return session;
|
|
396
319
|
}
|
|
397
320
|
async deleteSession(did) {
|
|
398
321
|
const session = await this.restoreSession(did);
|
|
@@ -415,7 +338,6 @@ var HappyViewOAuthClient = class {
|
|
|
415
338
|
if (lastActive === did) {
|
|
416
339
|
await this.storage.delete(LAST_ACTIVE_KEY);
|
|
417
340
|
}
|
|
418
|
-
this.sessionHooks.onSessionDelete?.(did);
|
|
419
341
|
}
|
|
420
342
|
async restoreSession(did) {
|
|
421
343
|
const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);
|
|
@@ -428,11 +350,7 @@ var HappyViewOAuthClient = class {
|
|
|
428
350
|
accessToken: data.accessToken,
|
|
429
351
|
clientKey: data.clientKey,
|
|
430
352
|
instanceUrl: data.instanceUrl,
|
|
431
|
-
|
|
432
|
-
pdsUrl: data.pdsUrl,
|
|
433
|
-
issuer: data.issuer,
|
|
434
|
-
fetch: this._fetch,
|
|
435
|
-
onSignOut: () => this.deleteSession(data.did)
|
|
353
|
+
fetch: this._fetch
|
|
436
354
|
});
|
|
437
355
|
}
|
|
438
356
|
async restore() {
|
|
@@ -471,11 +389,8 @@ async function computePkceChallenge(verifier) {
|
|
|
471
389
|
InvalidStateError,
|
|
472
390
|
LAST_ACTIVE_KEY,
|
|
473
391
|
MemoryStorage,
|
|
474
|
-
OAuthCallbackError,
|
|
475
392
|
ResolutionError,
|
|
476
393
|
TokenExchangeError,
|
|
477
|
-
importJwk
|
|
478
|
-
...require("@atproto/jwk"),
|
|
479
|
-
...require("@atproto/jwk-webcrypto")
|
|
394
|
+
importJwk
|
|
480
395
|
});
|
|
481
396
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/import-jwk.ts","../src/session.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export * from \"@atproto/jwk\";\nexport * from \"@atproto/jwk-webcrypto\";\n\nexport { HappyViewOAuthClient, LAST_ACTIVE_KEY } from \"./client\";\nexport type { FetchMetadataOptions } from \"./client\";\nexport { importJwk } from \"./import-jwk\";\nexport {\n ApiError,\n AuthenticationError,\n HappyViewError,\n InvalidStateError,\n OAuthCallbackError,\n ResolutionError,\n TokenExchangeError,\n} from \"./errors\";\nexport { HappyViewSession } from \"./session\";\nexport type { HappyViewSessionOptions } from \"./session\";\nexport { MemoryStorage } from \"./storage\";\nexport type {\n DpopProvision,\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n SessionEventHooks,\n StorageAdapter,\n StoredSession,\n TokenInfo,\n} from \"./types\";\n","export class HappyViewError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HappyViewError\";\n }\n}\n\nexport class ApiError extends HappyViewError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends HappyViewError {\n readonly status: number;\n\n constructor(message: string, status: number = 401) {\n super(message);\n this.name = \"AuthenticationError\";\n this.status = status;\n }\n}\n\nexport class InvalidStateError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidStateError\";\n }\n}\n\nexport class TokenExchangeError extends HappyViewError {\n readonly status: number;\n readonly body: string;\n\n constructor(message: string, status: number, body: string) {\n super(message);\n this.name = \"TokenExchangeError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class ResolutionError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"ResolutionError\";\n }\n}\n\nexport class OAuthCallbackError extends HappyViewError {\n readonly params: URLSearchParams;\n readonly state: string | undefined;\n\n static from(\n err: unknown,\n params: URLSearchParams,\n state?: string,\n ): OAuthCallbackError {\n if (err instanceof OAuthCallbackError) return err;\n const message = err instanceof Error ? err.message : undefined;\n return new OAuthCallbackError(params, message, state, err);\n }\n\n constructor(\n params: URLSearchParams,\n message?: string,\n state?: string,\n cause?: unknown,\n ) {\n super(\n message ??\n params.get(\"error_description\") ??\n \"OAuth callback error\",\n );\n this.name = \"OAuthCallbackError\";\n this.params = params;\n this.state = state;\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { WebcryptoKey } from \"@atproto/jwk-webcrypto\";\n\n/**\n * Import a raw ES256 JWK (as returned by HappyView's /oauth/dpop-keys)\n * into a WebcryptoKey that can sign JWTs.\n */\nexport async function importJwk(jwk: JsonWebKey): Promise<WebcryptoKey> {\n const privateKey = await crypto.subtle.importKey(\n \"jwk\",\n jwk,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"sign\"],\n );\n\n // Derive public key by stripping the private component\n const { d: _, ...publicJwkFields } = jwk;\n const publicKey = await crypto.subtle.importKey(\n \"jwk\",\n publicJwkFields,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"verify\"],\n );\n\n return WebcryptoKey.fromKeypair({ privateKey, publicKey });\n}\n","import type { Key } from \"@atproto/jwk\";\nimport type { TokenInfo } from \"./types\";\n\nexport interface HappyViewSessionOptions {\n did: string;\n dpopKey: Key;\n accessToken: string;\n clientKey: string;\n instanceUrl: string;\n scopes?: string;\n pdsUrl?: string;\n issuer?: string;\n fetch?: typeof globalThis.fetch;\n onSignOut?: () => Promise<void>;\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = crypto.getRandomValues(new Uint8Array(byteLength));\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nfunction base64url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nexport class HappyViewSession {\n readonly did: string;\n\n private readonly dpopKey: Key;\n private readonly accessToken: string;\n private readonly clientKey: string;\n private readonly instanceUrl: string;\n private readonly _scopes: string | undefined;\n private readonly _pdsUrl: string | undefined;\n private readonly _issuer: string | undefined;\n private readonly _fetch: typeof globalThis.fetch;\n private readonly _onSignOut: (() => Promise<void>) | undefined;\n private dpopNonce: string | undefined;\n\n constructor(options: HappyViewSessionOptions) {\n this.did = options.did;\n this.dpopKey = options.dpopKey;\n this.accessToken = options.accessToken;\n this.clientKey = options.clientKey;\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this._scopes = options.scopes;\n this._pdsUrl = options.pdsUrl;\n this._issuer = options.issuer;\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n this._onSignOut = options.onSignOut;\n }\n\n get sub(): string {\n return this.did;\n }\n\n getTokenInfo(): TokenInfo {\n return {\n scope: this._scopes,\n iss: this._issuer,\n aud: this._pdsUrl,\n sub: this.did,\n };\n }\n\n async signOut(): Promise<void> {\n if (this._onSignOut) {\n await this._onSignOut();\n }\n }\n\n async fetchHandler(url: string, init: RequestInit): Promise<Response> {\n const fullUrl = /^https?:\\/\\//i.test(url)\n ? url\n : `${this.instanceUrl}${url}`;\n const method = (init.method ?? \"GET\").toUpperCase();\n const htu = fullUrl.split(\"?\")[0];\n\n const sendWithProof = async (): Promise<Response> => {\n const tokenHash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(this.accessToken),\n );\n const ath = base64url(tokenHash);\n\n const proof = await this.dpopKey.createJwt(\n {\n alg: \"ES256\",\n typ: \"dpop+jwt\",\n jwk: this.dpopKey.publicJwk!,\n },\n {\n htm: method,\n htu,\n iat: Math.floor(Date.now() / 1000),\n jti: randomHex(16),\n ath,\n nonce: this.dpopNonce,\n },\n );\n\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `DPoP ${this.accessToken}`);\n headers.set(\"dpop\", proof);\n headers.set(\"x-client-key\", this.clientKey);\n\n return this._fetch(fullUrl, { ...init, headers });\n };\n\n let response: Response;\n try {\n response = await sendWithProof();\n } catch (e) {\n const info = `_fetch type: ${typeof this._fetch}, toString: ${String(this._fetch).slice(0, 120)}, url: ${fullUrl}`;\n throw new Error(`fetchHandler failed: ${(e as Error).message}\\n\\nDEBUG: ${info}`);\n }\n\n // Retry once if the server requires a DPoP nonce we didn't have\n const nonce = response.headers.get(\"dpop-nonce\");\n if (nonce && nonce !== this.dpopNonce && (response.status === 401 || response.status === 400)) {\n this.dpopNonce = nonce;\n try {\n response = await sendWithProof();\n } catch (e) {\n throw new Error(`fetchHandler retry failed: ${(e as Error).message}`);\n }\n const retryNonce = response.headers.get(\"dpop-nonce\");\n if (retryNonce) {\n this.dpopNonce = retryNonce;\n }\n } else if (nonce) {\n this.dpopNonce = nonce;\n }\n\n return response;\n }\n}\n","import type { StorageAdapter } from \"./types\";\n\nexport class MemoryStorage implements StorageAdapter {\n private data = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.data.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.data.set(key, value);\n }\n\n async delete(key: string): Promise<void> {\n this.data.delete(key);\n }\n}\n","import type { Key } from \"@atproto/jwk\";\nimport { ApiError } from \"./errors\";\nimport { importJwk } from \"./import-jwk\";\nimport { HappyViewSession } from \"./session\";\nimport { MemoryStorage } from \"./storage\";\nimport type {\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n SessionEventHooks,\n StorageAdapter,\n StoredSession,\n} from \"./types\";\n\nconst STORAGE_PREFIX = \"happyview:session:\";\nexport const LAST_ACTIVE_KEY = \"happyview:last-active-did\";\n\nexport interface FetchMetadataOptions {\n clientId: string;\n fetch?: typeof globalThis.fetch;\n signal?: AbortSignal;\n}\n\nexport class HappyViewOAuthClient {\n static async fetchMetadata({\n clientId,\n fetch: fetchFn = globalThis.fetch,\n signal,\n }: FetchMetadataOptions): Promise<Record<string, unknown>> {\n signal?.throwIfAborted();\n const resp = await fetchFn(clientId, { redirect: \"error\", signal });\n if (resp.status !== 200) {\n resp.body?.cancel?.();\n throw new Error(`Failed to fetch client metadata: ${resp.status}`);\n }\n const mime = resp.headers.get(\"content-type\")?.split(\";\")[0].trim();\n if (mime !== \"application/json\") {\n resp.body?.cancel?.();\n throw new Error(`Invalid client metadata content type: ${mime}`);\n }\n return resp.json() as Promise<Record<string, unknown>>;\n }\n\n protected readonly instanceUrl: string;\n protected readonly clientKey: string;\n protected readonly storage: StorageAdapter;\n private readonly clientSecret: string | undefined;\n protected readonly _fetch: typeof globalThis.fetch;\n private readonly sessionHooks: SessionEventHooks;\n\n constructor(\n options: HappyViewOAuthClientOptions & {\n fetch?: typeof globalThis.fetch;\n },\n ) {\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this.clientKey = options.clientKey;\n this.clientSecret = options.clientSecret;\n this.storage = options.storage ?? new MemoryStorage();\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n this.sessionHooks = options.sessionHooks ?? {};\n }\n\n get isConfidential(): boolean {\n return this.clientSecret !== undefined;\n }\n\n async provisionDpopKey(): Promise<{\n provisionId: string;\n dpopKey: Key;\n rawJwk: JsonWebKey;\n pkceVerifier?: string;\n }> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {};\n let pkceVerifier: string | undefined;\n\n if (!this.isConfidential) {\n pkceVerifier = generatePkceVerifier();\n const challenge = await computePkceChallenge(pkceVerifier);\n body.pkce_challenge = challenge;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/dpop-keys`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to provision DPoP key: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: ProvisionKeyResponse = await resp.json();\n const dpopKey = await importJwk(data.dpop_key);\n return {\n provisionId: data.provision_id,\n dpopKey,\n rawJwk: data.dpop_key,\n pkceVerifier,\n };\n }\n\n async registerSession(\n params: RegisterSessionParams,\n ): Promise<HappyViewSession> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {\n provision_id: params.provisionId,\n did: params.did,\n access_token: params.accessToken,\n refresh_token: params.refreshToken,\n scopes: params.scopes,\n pds_url: params.pdsUrl,\n issuer: params.issuer,\n };\n\n if (!this.isConfidential && params.pkceVerifier) {\n body.pkce_verifier = params.pkceVerifier;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/sessions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to register session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: RegisterSessionResponse = await resp.json();\n const dpopKey = await importJwk(params.dpopKey);\n\n const storedSession: StoredSession = {\n did: data.did,\n dpopKey: params.dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n scopes: params.scopes,\n pdsUrl: params.pdsUrl,\n issuer: params.issuer,\n };\n await this.storage.set(\n `${STORAGE_PREFIX}${data.did}`,\n JSON.stringify(storedSession),\n );\n await this.storage.set(LAST_ACTIVE_KEY, data.did);\n\n const session = new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n scopes: params.scopes,\n pdsUrl: params.pdsUrl,\n issuer: params.issuer,\n fetch: this._fetch,\n onSignOut: () => this.deleteSession(data.did),\n });\n\n this.sessionHooks.onSessionUpdate?.(data.did);\n\n return session;\n }\n\n async deleteSession(did: string): Promise<void> {\n const session = await this.restoreSession(did);\n if (session) {\n const resp = await session.fetchHandler(\n `${this.instanceUrl}/oauth/sessions/${did}`,\n { method: \"DELETE\" },\n );\n\n if (!resp.ok && resp.status !== 404) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to delete session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n }\n\n await this.storage.delete(`${STORAGE_PREFIX}${did}`);\n\n const lastActive = await this.storage.get(LAST_ACTIVE_KEY);\n if (lastActive === did) {\n await this.storage.delete(LAST_ACTIVE_KEY);\n }\n\n this.sessionHooks.onSessionDelete?.(did);\n }\n\n async restoreSession(did: string): Promise<HappyViewSession | null> {\n const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);\n if (!stored) return null;\n\n const data: StoredSession = JSON.parse(stored);\n const dpopKey = await importJwk(data.dpopKey);\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: data.accessToken,\n clientKey: data.clientKey,\n instanceUrl: data.instanceUrl,\n scopes: data.scopes,\n pdsUrl: data.pdsUrl,\n issuer: data.issuer,\n fetch: this._fetch,\n onSignOut: () => this.deleteSession(data.did),\n });\n }\n\n async restore(): Promise<HappyViewSession | null> {\n const did = await this.storage.get(LAST_ACTIVE_KEY);\n if (!did) return null;\n return this.restoreSession(did);\n }\n}\n\n// PKCE helpers — inline since they're just a few lines of Web Crypto\nfunction generatePkceVerifier(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nasync function computePkceChallenge(verifier: string): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(verifier),\n );\n const bytes = new Uint8Array(hash);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":["body"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/import-jwk.ts","../src/session.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export { HappyViewOAuthClient, LAST_ACTIVE_KEY } from \"./client\";\nexport { importJwk } from \"./import-jwk\";\nexport {\n ApiError,\n AuthenticationError,\n HappyViewError,\n InvalidStateError,\n ResolutionError,\n TokenExchangeError,\n} from \"./errors\";\nexport { HappyViewSession } from \"./session\";\nexport type { HappyViewSessionOptions } from \"./session\";\nexport { MemoryStorage } from \"./storage\";\nexport type {\n DpopProvision,\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n StorageAdapter,\n StoredSession,\n} from \"./types\";\n","export class HappyViewError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HappyViewError\";\n }\n}\n\nexport class ApiError extends HappyViewError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends HappyViewError {\n readonly status: number;\n\n constructor(message: string, status: number = 401) {\n super(message);\n this.name = \"AuthenticationError\";\n this.status = status;\n }\n}\n\nexport class InvalidStateError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidStateError\";\n }\n}\n\nexport class TokenExchangeError extends HappyViewError {\n readonly status: number;\n readonly body: string;\n\n constructor(message: string, status: number, body: string) {\n super(message);\n this.name = \"TokenExchangeError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class ResolutionError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"ResolutionError\";\n }\n}\n","import { WebcryptoKey } from \"@atproto/jwk-webcrypto\";\n\n/**\n * Import a raw ES256 JWK (as returned by HappyView's /oauth/dpop-keys)\n * into a WebcryptoKey that can sign JWTs.\n */\nexport async function importJwk(jwk: JsonWebKey): Promise<WebcryptoKey> {\n const privateKey = await crypto.subtle.importKey(\n \"jwk\",\n jwk,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"sign\"],\n );\n\n // Derive public key by stripping the private component\n const { d: _, ...publicJwkFields } = jwk;\n const publicKey = await crypto.subtle.importKey(\n \"jwk\",\n publicJwkFields,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"verify\"],\n );\n\n return WebcryptoKey.fromKeypair({ privateKey, publicKey });\n}\n","import type { Key } from \"@atproto/jwk\";\n\nexport interface HappyViewSessionOptions {\n did: string;\n dpopKey: Key;\n accessToken: string;\n clientKey: string;\n instanceUrl: string;\n fetch?: typeof globalThis.fetch;\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = crypto.getRandomValues(new Uint8Array(byteLength));\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nfunction base64url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nexport class HappyViewSession {\n readonly did: string;\n\n private readonly dpopKey: Key;\n private readonly accessToken: string;\n private readonly clientKey: string;\n private readonly instanceUrl: string;\n private readonly _fetch: typeof globalThis.fetch;\n private dpopNonce: string | undefined;\n\n constructor(options: HappyViewSessionOptions) {\n this.did = options.did;\n this.dpopKey = options.dpopKey;\n this.accessToken = options.accessToken;\n this.clientKey = options.clientKey;\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n }\n\n async fetchHandler(url: string, init: RequestInit): Promise<Response> {\n const fullUrl = /^https?:\\/\\//i.test(url)\n ? url\n : `${this.instanceUrl}${url}`;\n const method = (init.method ?? \"GET\").toUpperCase();\n const htu = fullUrl.split(\"?\")[0];\n\n const sendWithProof = async (): Promise<Response> => {\n const tokenHash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(this.accessToken),\n );\n const ath = base64url(tokenHash);\n\n const proof = await this.dpopKey.createJwt(\n {\n alg: \"ES256\",\n typ: \"dpop+jwt\",\n jwk: this.dpopKey.publicJwk!,\n },\n {\n htm: method,\n htu,\n iat: Math.floor(Date.now() / 1000),\n jti: randomHex(16),\n ath,\n nonce: this.dpopNonce,\n },\n );\n\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `DPoP ${this.accessToken}`);\n headers.set(\"dpop\", proof);\n headers.set(\"x-client-key\", this.clientKey);\n\n return this._fetch(fullUrl, { ...init, headers });\n };\n\n let response: Response;\n try {\n response = await sendWithProof();\n } catch (e) {\n const info = `_fetch type: ${typeof this._fetch}, toString: ${String(this._fetch).slice(0, 120)}, url: ${fullUrl}`;\n throw new Error(`fetchHandler failed: ${(e as Error).message}\\n\\nDEBUG: ${info}`);\n }\n\n // Retry once if the server requires a DPoP nonce we didn't have\n const nonce = response.headers.get(\"dpop-nonce\");\n if (nonce && nonce !== this.dpopNonce && (response.status === 401 || response.status === 400)) {\n this.dpopNonce = nonce;\n try {\n response = await sendWithProof();\n } catch (e) {\n throw new Error(`fetchHandler retry failed: ${(e as Error).message}`);\n }\n const retryNonce = response.headers.get(\"dpop-nonce\");\n if (retryNonce) {\n this.dpopNonce = retryNonce;\n }\n } else if (nonce) {\n this.dpopNonce = nonce;\n }\n\n return response;\n }\n}\n","import type { StorageAdapter } from \"./types\";\n\nexport class MemoryStorage implements StorageAdapter {\n private data = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.data.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.data.set(key, value);\n }\n\n async delete(key: string): Promise<void> {\n this.data.delete(key);\n }\n}\n","import type { Key } from \"@atproto/jwk\";\nimport { ApiError } from \"./errors\";\nimport { importJwk } from \"./import-jwk\";\nimport { HappyViewSession } from \"./session\";\nimport { MemoryStorage } from \"./storage\";\nimport type {\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n StorageAdapter,\n StoredSession,\n} from \"./types\";\n\nconst STORAGE_PREFIX = \"happyview:session:\";\nexport const LAST_ACTIVE_KEY = \"happyview:last-active-did\";\n\nexport class HappyViewOAuthClient {\n protected readonly instanceUrl: string;\n protected readonly clientKey: string;\n protected readonly storage: StorageAdapter;\n private readonly clientSecret: string | undefined;\n protected readonly _fetch: typeof globalThis.fetch;\n\n constructor(\n options: HappyViewOAuthClientOptions & {\n fetch?: typeof globalThis.fetch;\n },\n ) {\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this.clientKey = options.clientKey;\n this.clientSecret = options.clientSecret;\n this.storage = options.storage ?? new MemoryStorage();\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n }\n\n get isConfidential(): boolean {\n return this.clientSecret !== undefined;\n }\n\n async provisionDpopKey(): Promise<{\n provisionId: string;\n dpopKey: Key;\n rawJwk: JsonWebKey;\n pkceVerifier?: string;\n }> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {};\n let pkceVerifier: string | undefined;\n\n if (!this.isConfidential) {\n pkceVerifier = generatePkceVerifier();\n const challenge = await computePkceChallenge(pkceVerifier);\n body.pkce_challenge = challenge;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/dpop-keys`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to provision DPoP key: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: ProvisionKeyResponse = await resp.json();\n const dpopKey = await importJwk(data.dpop_key);\n return {\n provisionId: data.provision_id,\n dpopKey,\n rawJwk: data.dpop_key,\n pkceVerifier,\n };\n }\n\n async registerSession(\n params: RegisterSessionParams,\n ): Promise<HappyViewSession> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {\n provision_id: params.provisionId,\n did: params.did,\n access_token: params.accessToken,\n refresh_token: params.refreshToken,\n scopes: params.scopes,\n pds_url: params.pdsUrl,\n issuer: params.issuer,\n };\n\n if (!this.isConfidential && params.pkceVerifier) {\n body.pkce_verifier = params.pkceVerifier;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/sessions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to register session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: RegisterSessionResponse = await resp.json();\n const dpopKey = await importJwk(params.dpopKey);\n\n const storedSession: StoredSession = {\n did: data.did,\n dpopKey: params.dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n };\n await this.storage.set(\n `${STORAGE_PREFIX}${data.did}`,\n JSON.stringify(storedSession),\n );\n await this.storage.set(LAST_ACTIVE_KEY, data.did);\n\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n fetch: this._fetch,\n });\n }\n\n async deleteSession(did: string): Promise<void> {\n const session = await this.restoreSession(did);\n if (session) {\n const resp = await session.fetchHandler(\n `${this.instanceUrl}/oauth/sessions/${did}`,\n { method: \"DELETE\" },\n );\n\n if (!resp.ok && resp.status !== 404) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to delete session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n }\n\n await this.storage.delete(`${STORAGE_PREFIX}${did}`);\n\n const lastActive = await this.storage.get(LAST_ACTIVE_KEY);\n if (lastActive === did) {\n await this.storage.delete(LAST_ACTIVE_KEY);\n }\n }\n\n async restoreSession(did: string): Promise<HappyViewSession | null> {\n const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);\n if (!stored) return null;\n\n const data: StoredSession = JSON.parse(stored);\n const dpopKey = await importJwk(data.dpopKey);\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: data.accessToken,\n clientKey: data.clientKey,\n instanceUrl: data.instanceUrl,\n fetch: this._fetch,\n });\n }\n\n async restore(): Promise<HappyViewSession | null> {\n const did = await this.storage.get(LAST_ACTIVE_KEY);\n if (!did) return null;\n return this.restoreSession(did);\n }\n}\n\n// PKCE helpers — inline since they're just a few lines of Web Crypto\nfunction generatePkceVerifier(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nasync function computePkceChallenge(verifier: string): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(verifier),\n );\n const bytes = new Uint8Array(hash);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":["body"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { Key } from '@atproto/jwk';
|
|
2
|
-
export * from '@atproto/jwk';
|
|
3
2
|
import { WebcryptoKey } from '@atproto/jwk-webcrypto';
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
interface HappyViewSessionOptions {
|
|
5
|
+
did: string;
|
|
6
|
+
dpopKey: Key;
|
|
7
|
+
accessToken: string;
|
|
8
|
+
clientKey: string;
|
|
9
|
+
instanceUrl: string;
|
|
10
|
+
fetch?: typeof globalThis.fetch;
|
|
11
|
+
}
|
|
12
|
+
declare class HappyViewSession {
|
|
13
|
+
readonly did: string;
|
|
14
|
+
private readonly dpopKey;
|
|
15
|
+
private readonly accessToken;
|
|
16
|
+
private readonly clientKey;
|
|
17
|
+
private readonly instanceUrl;
|
|
18
|
+
private readonly _fetch;
|
|
19
|
+
private dpopNonce;
|
|
20
|
+
constructor(options: HappyViewSessionOptions);
|
|
21
|
+
fetchHandler(url: string, init: RequestInit): Promise<Response>;
|
|
22
|
+
}
|
|
5
23
|
|
|
6
24
|
interface StorageAdapter {
|
|
7
25
|
get(key: string): Promise<string | null>;
|
|
8
26
|
set(key: string, value: string): Promise<void>;
|
|
9
27
|
delete(key: string): Promise<void>;
|
|
10
28
|
}
|
|
11
|
-
interface SessionEventHooks {
|
|
12
|
-
onSessionUpdate?: (did: string) => void;
|
|
13
|
-
onSessionDelete?: (did: string, cause?: unknown) => void;
|
|
14
|
-
}
|
|
15
29
|
interface HappyViewOAuthClientOptions {
|
|
16
30
|
instanceUrl: string;
|
|
17
31
|
clientKey: string;
|
|
18
32
|
clientSecret?: string;
|
|
19
33
|
storage?: StorageAdapter;
|
|
20
|
-
sessionHooks?: SessionEventHooks;
|
|
21
34
|
}
|
|
22
35
|
interface DpopProvision {
|
|
23
36
|
provisionId: string;
|
|
@@ -46,17 +59,6 @@ interface StoredSession {
|
|
|
46
59
|
accessToken: string;
|
|
47
60
|
clientKey: string;
|
|
48
61
|
instanceUrl: string;
|
|
49
|
-
scopes?: string;
|
|
50
|
-
pdsUrl?: string;
|
|
51
|
-
issuer?: string;
|
|
52
|
-
}
|
|
53
|
-
interface TokenInfo {
|
|
54
|
-
expiresAt?: Date;
|
|
55
|
-
expired?: boolean;
|
|
56
|
-
scope?: string;
|
|
57
|
-
iss?: string;
|
|
58
|
-
aud?: string;
|
|
59
|
-
sub: string;
|
|
60
62
|
}
|
|
61
63
|
interface ProvisionKeyResponse {
|
|
62
64
|
provision_id: string;
|
|
@@ -67,51 +69,13 @@ interface RegisterSessionResponse {
|
|
|
67
69
|
did: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
interface HappyViewSessionOptions {
|
|
71
|
-
did: string;
|
|
72
|
-
dpopKey: Key;
|
|
73
|
-
accessToken: string;
|
|
74
|
-
clientKey: string;
|
|
75
|
-
instanceUrl: string;
|
|
76
|
-
scopes?: string;
|
|
77
|
-
pdsUrl?: string;
|
|
78
|
-
issuer?: string;
|
|
79
|
-
fetch?: typeof globalThis.fetch;
|
|
80
|
-
onSignOut?: () => Promise<void>;
|
|
81
|
-
}
|
|
82
|
-
declare class HappyViewSession {
|
|
83
|
-
readonly did: string;
|
|
84
|
-
private readonly dpopKey;
|
|
85
|
-
private readonly accessToken;
|
|
86
|
-
private readonly clientKey;
|
|
87
|
-
private readonly instanceUrl;
|
|
88
|
-
private readonly _scopes;
|
|
89
|
-
private readonly _pdsUrl;
|
|
90
|
-
private readonly _issuer;
|
|
91
|
-
private readonly _fetch;
|
|
92
|
-
private readonly _onSignOut;
|
|
93
|
-
private dpopNonce;
|
|
94
|
-
constructor(options: HappyViewSessionOptions);
|
|
95
|
-
get sub(): string;
|
|
96
|
-
getTokenInfo(): TokenInfo;
|
|
97
|
-
signOut(): Promise<void>;
|
|
98
|
-
fetchHandler(url: string, init: RequestInit): Promise<Response>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
72
|
declare const LAST_ACTIVE_KEY = "happyview:last-active-did";
|
|
102
|
-
interface FetchMetadataOptions {
|
|
103
|
-
clientId: string;
|
|
104
|
-
fetch?: typeof globalThis.fetch;
|
|
105
|
-
signal?: AbortSignal;
|
|
106
|
-
}
|
|
107
73
|
declare class HappyViewOAuthClient {
|
|
108
|
-
static fetchMetadata({ clientId, fetch: fetchFn, signal, }: FetchMetadataOptions): Promise<Record<string, unknown>>;
|
|
109
74
|
protected readonly instanceUrl: string;
|
|
110
75
|
protected readonly clientKey: string;
|
|
111
76
|
protected readonly storage: StorageAdapter;
|
|
112
77
|
private readonly clientSecret;
|
|
113
78
|
protected readonly _fetch: typeof globalThis.fetch;
|
|
114
|
-
private readonly sessionHooks;
|
|
115
79
|
constructor(options: HappyViewOAuthClientOptions & {
|
|
116
80
|
fetch?: typeof globalThis.fetch;
|
|
117
81
|
});
|
|
@@ -157,12 +121,6 @@ declare class TokenExchangeError extends HappyViewError {
|
|
|
157
121
|
declare class ResolutionError extends HappyViewError {
|
|
158
122
|
constructor(message: string);
|
|
159
123
|
}
|
|
160
|
-
declare class OAuthCallbackError extends HappyViewError {
|
|
161
|
-
readonly params: URLSearchParams;
|
|
162
|
-
readonly state: string | undefined;
|
|
163
|
-
static from(err: unknown, params: URLSearchParams, state?: string): OAuthCallbackError;
|
|
164
|
-
constructor(params: URLSearchParams, message?: string, state?: string, cause?: unknown);
|
|
165
|
-
}
|
|
166
124
|
|
|
167
125
|
declare class MemoryStorage implements StorageAdapter {
|
|
168
126
|
private data;
|
|
@@ -171,4 +129,4 @@ declare class MemoryStorage implements StorageAdapter {
|
|
|
171
129
|
delete(key: string): Promise<void>;
|
|
172
130
|
}
|
|
173
131
|
|
|
174
|
-
export { ApiError, AuthenticationError, type DpopProvision,
|
|
132
|
+
export { ApiError, AuthenticationError, type DpopProvision, HappyViewError, HappyViewOAuthClient, type HappyViewOAuthClientOptions, HappyViewSession, type HappyViewSessionOptions, InvalidStateError, LAST_ACTIVE_KEY, MemoryStorage, type ProvisionKeyResponse, type RegisterSessionParams, type RegisterSessionResponse, ResolutionError, type StorageAdapter, type StoredSession, TokenExchangeError, importJwk };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { Key } from '@atproto/jwk';
|
|
2
|
-
export * from '@atproto/jwk';
|
|
3
2
|
import { WebcryptoKey } from '@atproto/jwk-webcrypto';
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
interface HappyViewSessionOptions {
|
|
5
|
+
did: string;
|
|
6
|
+
dpopKey: Key;
|
|
7
|
+
accessToken: string;
|
|
8
|
+
clientKey: string;
|
|
9
|
+
instanceUrl: string;
|
|
10
|
+
fetch?: typeof globalThis.fetch;
|
|
11
|
+
}
|
|
12
|
+
declare class HappyViewSession {
|
|
13
|
+
readonly did: string;
|
|
14
|
+
private readonly dpopKey;
|
|
15
|
+
private readonly accessToken;
|
|
16
|
+
private readonly clientKey;
|
|
17
|
+
private readonly instanceUrl;
|
|
18
|
+
private readonly _fetch;
|
|
19
|
+
private dpopNonce;
|
|
20
|
+
constructor(options: HappyViewSessionOptions);
|
|
21
|
+
fetchHandler(url: string, init: RequestInit): Promise<Response>;
|
|
22
|
+
}
|
|
5
23
|
|
|
6
24
|
interface StorageAdapter {
|
|
7
25
|
get(key: string): Promise<string | null>;
|
|
8
26
|
set(key: string, value: string): Promise<void>;
|
|
9
27
|
delete(key: string): Promise<void>;
|
|
10
28
|
}
|
|
11
|
-
interface SessionEventHooks {
|
|
12
|
-
onSessionUpdate?: (did: string) => void;
|
|
13
|
-
onSessionDelete?: (did: string, cause?: unknown) => void;
|
|
14
|
-
}
|
|
15
29
|
interface HappyViewOAuthClientOptions {
|
|
16
30
|
instanceUrl: string;
|
|
17
31
|
clientKey: string;
|
|
18
32
|
clientSecret?: string;
|
|
19
33
|
storage?: StorageAdapter;
|
|
20
|
-
sessionHooks?: SessionEventHooks;
|
|
21
34
|
}
|
|
22
35
|
interface DpopProvision {
|
|
23
36
|
provisionId: string;
|
|
@@ -46,17 +59,6 @@ interface StoredSession {
|
|
|
46
59
|
accessToken: string;
|
|
47
60
|
clientKey: string;
|
|
48
61
|
instanceUrl: string;
|
|
49
|
-
scopes?: string;
|
|
50
|
-
pdsUrl?: string;
|
|
51
|
-
issuer?: string;
|
|
52
|
-
}
|
|
53
|
-
interface TokenInfo {
|
|
54
|
-
expiresAt?: Date;
|
|
55
|
-
expired?: boolean;
|
|
56
|
-
scope?: string;
|
|
57
|
-
iss?: string;
|
|
58
|
-
aud?: string;
|
|
59
|
-
sub: string;
|
|
60
62
|
}
|
|
61
63
|
interface ProvisionKeyResponse {
|
|
62
64
|
provision_id: string;
|
|
@@ -67,51 +69,13 @@ interface RegisterSessionResponse {
|
|
|
67
69
|
did: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
interface HappyViewSessionOptions {
|
|
71
|
-
did: string;
|
|
72
|
-
dpopKey: Key;
|
|
73
|
-
accessToken: string;
|
|
74
|
-
clientKey: string;
|
|
75
|
-
instanceUrl: string;
|
|
76
|
-
scopes?: string;
|
|
77
|
-
pdsUrl?: string;
|
|
78
|
-
issuer?: string;
|
|
79
|
-
fetch?: typeof globalThis.fetch;
|
|
80
|
-
onSignOut?: () => Promise<void>;
|
|
81
|
-
}
|
|
82
|
-
declare class HappyViewSession {
|
|
83
|
-
readonly did: string;
|
|
84
|
-
private readonly dpopKey;
|
|
85
|
-
private readonly accessToken;
|
|
86
|
-
private readonly clientKey;
|
|
87
|
-
private readonly instanceUrl;
|
|
88
|
-
private readonly _scopes;
|
|
89
|
-
private readonly _pdsUrl;
|
|
90
|
-
private readonly _issuer;
|
|
91
|
-
private readonly _fetch;
|
|
92
|
-
private readonly _onSignOut;
|
|
93
|
-
private dpopNonce;
|
|
94
|
-
constructor(options: HappyViewSessionOptions);
|
|
95
|
-
get sub(): string;
|
|
96
|
-
getTokenInfo(): TokenInfo;
|
|
97
|
-
signOut(): Promise<void>;
|
|
98
|
-
fetchHandler(url: string, init: RequestInit): Promise<Response>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
72
|
declare const LAST_ACTIVE_KEY = "happyview:last-active-did";
|
|
102
|
-
interface FetchMetadataOptions {
|
|
103
|
-
clientId: string;
|
|
104
|
-
fetch?: typeof globalThis.fetch;
|
|
105
|
-
signal?: AbortSignal;
|
|
106
|
-
}
|
|
107
73
|
declare class HappyViewOAuthClient {
|
|
108
|
-
static fetchMetadata({ clientId, fetch: fetchFn, signal, }: FetchMetadataOptions): Promise<Record<string, unknown>>;
|
|
109
74
|
protected readonly instanceUrl: string;
|
|
110
75
|
protected readonly clientKey: string;
|
|
111
76
|
protected readonly storage: StorageAdapter;
|
|
112
77
|
private readonly clientSecret;
|
|
113
78
|
protected readonly _fetch: typeof globalThis.fetch;
|
|
114
|
-
private readonly sessionHooks;
|
|
115
79
|
constructor(options: HappyViewOAuthClientOptions & {
|
|
116
80
|
fetch?: typeof globalThis.fetch;
|
|
117
81
|
});
|
|
@@ -157,12 +121,6 @@ declare class TokenExchangeError extends HappyViewError {
|
|
|
157
121
|
declare class ResolutionError extends HappyViewError {
|
|
158
122
|
constructor(message: string);
|
|
159
123
|
}
|
|
160
|
-
declare class OAuthCallbackError extends HappyViewError {
|
|
161
|
-
readonly params: URLSearchParams;
|
|
162
|
-
readonly state: string | undefined;
|
|
163
|
-
static from(err: unknown, params: URLSearchParams, state?: string): OAuthCallbackError;
|
|
164
|
-
constructor(params: URLSearchParams, message?: string, state?: string, cause?: unknown);
|
|
165
|
-
}
|
|
166
124
|
|
|
167
125
|
declare class MemoryStorage implements StorageAdapter {
|
|
168
126
|
private data;
|
|
@@ -171,4 +129,4 @@ declare class MemoryStorage implements StorageAdapter {
|
|
|
171
129
|
delete(key: string): Promise<void>;
|
|
172
130
|
}
|
|
173
131
|
|
|
174
|
-
export { ApiError, AuthenticationError, type DpopProvision,
|
|
132
|
+
export { ApiError, AuthenticationError, type DpopProvision, HappyViewError, HappyViewOAuthClient, type HappyViewOAuthClientOptions, HappyViewSession, type HappyViewSessionOptions, InvalidStateError, LAST_ACTIVE_KEY, MemoryStorage, type ProvisionKeyResponse, type RegisterSessionParams, type RegisterSessionResponse, ResolutionError, type StorageAdapter, type StoredSession, TokenExchangeError, importJwk };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
export * from "@atproto/jwk";
|
|
3
|
-
export * from "@atproto/jwk-webcrypto";
|
|
4
|
-
|
|
5
1
|
// src/errors.ts
|
|
6
2
|
var HappyViewError = class extends Error {
|
|
7
3
|
constructor(message) {
|
|
@@ -49,26 +45,6 @@ var ResolutionError = class extends HappyViewError {
|
|
|
49
45
|
this.name = "ResolutionError";
|
|
50
46
|
}
|
|
51
47
|
};
|
|
52
|
-
var OAuthCallbackError = class _OAuthCallbackError extends HappyViewError {
|
|
53
|
-
params;
|
|
54
|
-
state;
|
|
55
|
-
static from(err, params, state) {
|
|
56
|
-
if (err instanceof _OAuthCallbackError) return err;
|
|
57
|
-
const message = err instanceof Error ? err.message : void 0;
|
|
58
|
-
return new _OAuthCallbackError(params, message, state, err);
|
|
59
|
-
}
|
|
60
|
-
constructor(params, message, state, cause) {
|
|
61
|
-
super(
|
|
62
|
-
message ?? params.get("error_description") ?? "OAuth callback error"
|
|
63
|
-
);
|
|
64
|
-
this.name = "OAuthCallbackError";
|
|
65
|
-
this.params = params;
|
|
66
|
-
this.state = state;
|
|
67
|
-
if (cause !== void 0) {
|
|
68
|
-
this.cause = cause;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
48
|
|
|
73
49
|
// src/import-jwk.ts
|
|
74
50
|
import { WebcryptoKey } from "@atproto/jwk-webcrypto";
|
|
@@ -110,11 +86,7 @@ var HappyViewSession = class {
|
|
|
110
86
|
accessToken;
|
|
111
87
|
clientKey;
|
|
112
88
|
instanceUrl;
|
|
113
|
-
_scopes;
|
|
114
|
-
_pdsUrl;
|
|
115
|
-
_issuer;
|
|
116
89
|
_fetch;
|
|
117
|
-
_onSignOut;
|
|
118
90
|
dpopNonce;
|
|
119
91
|
constructor(options) {
|
|
120
92
|
this.did = options.did;
|
|
@@ -122,27 +94,7 @@ var HappyViewSession = class {
|
|
|
122
94
|
this.accessToken = options.accessToken;
|
|
123
95
|
this.clientKey = options.clientKey;
|
|
124
96
|
this.instanceUrl = options.instanceUrl.replace(/\/+$/, "");
|
|
125
|
-
this._scopes = options.scopes;
|
|
126
|
-
this._pdsUrl = options.pdsUrl;
|
|
127
|
-
this._issuer = options.issuer;
|
|
128
97
|
this._fetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
129
|
-
this._onSignOut = options.onSignOut;
|
|
130
|
-
}
|
|
131
|
-
get sub() {
|
|
132
|
-
return this.did;
|
|
133
|
-
}
|
|
134
|
-
getTokenInfo() {
|
|
135
|
-
return {
|
|
136
|
-
scope: this._scopes,
|
|
137
|
-
iss: this._issuer,
|
|
138
|
-
aud: this._pdsUrl,
|
|
139
|
-
sub: this.did
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
async signOut() {
|
|
143
|
-
if (this._onSignOut) {
|
|
144
|
-
await this._onSignOut();
|
|
145
|
-
}
|
|
146
98
|
}
|
|
147
99
|
async fetchHandler(url, init) {
|
|
148
100
|
const fullUrl = /^https?:\/\//i.test(url) ? url : `${this.instanceUrl}${url}`;
|
|
@@ -221,37 +173,17 @@ var MemoryStorage = class {
|
|
|
221
173
|
var STORAGE_PREFIX = "happyview:session:";
|
|
222
174
|
var LAST_ACTIVE_KEY = "happyview:last-active-did";
|
|
223
175
|
var HappyViewOAuthClient = class {
|
|
224
|
-
static async fetchMetadata({
|
|
225
|
-
clientId,
|
|
226
|
-
fetch: fetchFn = globalThis.fetch,
|
|
227
|
-
signal
|
|
228
|
-
}) {
|
|
229
|
-
signal?.throwIfAborted();
|
|
230
|
-
const resp = await fetchFn(clientId, { redirect: "error", signal });
|
|
231
|
-
if (resp.status !== 200) {
|
|
232
|
-
resp.body?.cancel?.();
|
|
233
|
-
throw new Error(`Failed to fetch client metadata: ${resp.status}`);
|
|
234
|
-
}
|
|
235
|
-
const mime = resp.headers.get("content-type")?.split(";")[0].trim();
|
|
236
|
-
if (mime !== "application/json") {
|
|
237
|
-
resp.body?.cancel?.();
|
|
238
|
-
throw new Error(`Invalid client metadata content type: ${mime}`);
|
|
239
|
-
}
|
|
240
|
-
return resp.json();
|
|
241
|
-
}
|
|
242
176
|
instanceUrl;
|
|
243
177
|
clientKey;
|
|
244
178
|
storage;
|
|
245
179
|
clientSecret;
|
|
246
180
|
_fetch;
|
|
247
|
-
sessionHooks;
|
|
248
181
|
constructor(options) {
|
|
249
182
|
this.instanceUrl = options.instanceUrl.replace(/\/+$/, "");
|
|
250
183
|
this.clientKey = options.clientKey;
|
|
251
184
|
this.clientSecret = options.clientSecret;
|
|
252
185
|
this.storage = options.storage ?? new MemoryStorage();
|
|
253
186
|
this._fetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
254
|
-
this.sessionHooks = options.sessionHooks ?? {};
|
|
255
187
|
}
|
|
256
188
|
get isConfidential() {
|
|
257
189
|
return this.clientSecret !== void 0;
|
|
@@ -333,30 +265,21 @@ var HappyViewOAuthClient = class {
|
|
|
333
265
|
dpopKey: params.dpopKey,
|
|
334
266
|
accessToken: params.accessToken,
|
|
335
267
|
clientKey: this.clientKey,
|
|
336
|
-
instanceUrl: this.instanceUrl
|
|
337
|
-
scopes: params.scopes,
|
|
338
|
-
pdsUrl: params.pdsUrl,
|
|
339
|
-
issuer: params.issuer
|
|
268
|
+
instanceUrl: this.instanceUrl
|
|
340
269
|
};
|
|
341
270
|
await this.storage.set(
|
|
342
271
|
`${STORAGE_PREFIX}${data.did}`,
|
|
343
272
|
JSON.stringify(storedSession)
|
|
344
273
|
);
|
|
345
274
|
await this.storage.set(LAST_ACTIVE_KEY, data.did);
|
|
346
|
-
|
|
275
|
+
return new HappyViewSession({
|
|
347
276
|
did: data.did,
|
|
348
277
|
dpopKey,
|
|
349
278
|
accessToken: params.accessToken,
|
|
350
279
|
clientKey: this.clientKey,
|
|
351
280
|
instanceUrl: this.instanceUrl,
|
|
352
|
-
|
|
353
|
-
pdsUrl: params.pdsUrl,
|
|
354
|
-
issuer: params.issuer,
|
|
355
|
-
fetch: this._fetch,
|
|
356
|
-
onSignOut: () => this.deleteSession(data.did)
|
|
281
|
+
fetch: this._fetch
|
|
357
282
|
});
|
|
358
|
-
this.sessionHooks.onSessionUpdate?.(data.did);
|
|
359
|
-
return session;
|
|
360
283
|
}
|
|
361
284
|
async deleteSession(did) {
|
|
362
285
|
const session = await this.restoreSession(did);
|
|
@@ -379,7 +302,6 @@ var HappyViewOAuthClient = class {
|
|
|
379
302
|
if (lastActive === did) {
|
|
380
303
|
await this.storage.delete(LAST_ACTIVE_KEY);
|
|
381
304
|
}
|
|
382
|
-
this.sessionHooks.onSessionDelete?.(did);
|
|
383
305
|
}
|
|
384
306
|
async restoreSession(did) {
|
|
385
307
|
const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);
|
|
@@ -392,11 +314,7 @@ var HappyViewOAuthClient = class {
|
|
|
392
314
|
accessToken: data.accessToken,
|
|
393
315
|
clientKey: data.clientKey,
|
|
394
316
|
instanceUrl: data.instanceUrl,
|
|
395
|
-
|
|
396
|
-
pdsUrl: data.pdsUrl,
|
|
397
|
-
issuer: data.issuer,
|
|
398
|
-
fetch: this._fetch,
|
|
399
|
-
onSignOut: () => this.deleteSession(data.did)
|
|
317
|
+
fetch: this._fetch
|
|
400
318
|
});
|
|
401
319
|
}
|
|
402
320
|
async restore() {
|
|
@@ -434,7 +352,6 @@ export {
|
|
|
434
352
|
InvalidStateError,
|
|
435
353
|
LAST_ACTIVE_KEY,
|
|
436
354
|
MemoryStorage,
|
|
437
|
-
OAuthCallbackError,
|
|
438
355
|
ResolutionError,
|
|
439
356
|
TokenExchangeError,
|
|
440
357
|
importJwk
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/import-jwk.ts","../src/session.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export * from \"@atproto/jwk\";\nexport * from \"@atproto/jwk-webcrypto\";\n\nexport { HappyViewOAuthClient, LAST_ACTIVE_KEY } from \"./client\";\nexport type { FetchMetadataOptions } from \"./client\";\nexport { importJwk } from \"./import-jwk\";\nexport {\n ApiError,\n AuthenticationError,\n HappyViewError,\n InvalidStateError,\n OAuthCallbackError,\n ResolutionError,\n TokenExchangeError,\n} from \"./errors\";\nexport { HappyViewSession } from \"./session\";\nexport type { HappyViewSessionOptions } from \"./session\";\nexport { MemoryStorage } from \"./storage\";\nexport type {\n DpopProvision,\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n SessionEventHooks,\n StorageAdapter,\n StoredSession,\n TokenInfo,\n} from \"./types\";\n","export class HappyViewError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HappyViewError\";\n }\n}\n\nexport class ApiError extends HappyViewError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends HappyViewError {\n readonly status: number;\n\n constructor(message: string, status: number = 401) {\n super(message);\n this.name = \"AuthenticationError\";\n this.status = status;\n }\n}\n\nexport class InvalidStateError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidStateError\";\n }\n}\n\nexport class TokenExchangeError extends HappyViewError {\n readonly status: number;\n readonly body: string;\n\n constructor(message: string, status: number, body: string) {\n super(message);\n this.name = \"TokenExchangeError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class ResolutionError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"ResolutionError\";\n }\n}\n\nexport class OAuthCallbackError extends HappyViewError {\n readonly params: URLSearchParams;\n readonly state: string | undefined;\n\n static from(\n err: unknown,\n params: URLSearchParams,\n state?: string,\n ): OAuthCallbackError {\n if (err instanceof OAuthCallbackError) return err;\n const message = err instanceof Error ? err.message : undefined;\n return new OAuthCallbackError(params, message, state, err);\n }\n\n constructor(\n params: URLSearchParams,\n message?: string,\n state?: string,\n cause?: unknown,\n ) {\n super(\n message ??\n params.get(\"error_description\") ??\n \"OAuth callback error\",\n );\n this.name = \"OAuthCallbackError\";\n this.params = params;\n this.state = state;\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { WebcryptoKey } from \"@atproto/jwk-webcrypto\";\n\n/**\n * Import a raw ES256 JWK (as returned by HappyView's /oauth/dpop-keys)\n * into a WebcryptoKey that can sign JWTs.\n */\nexport async function importJwk(jwk: JsonWebKey): Promise<WebcryptoKey> {\n const privateKey = await crypto.subtle.importKey(\n \"jwk\",\n jwk,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"sign\"],\n );\n\n // Derive public key by stripping the private component\n const { d: _, ...publicJwkFields } = jwk;\n const publicKey = await crypto.subtle.importKey(\n \"jwk\",\n publicJwkFields,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"verify\"],\n );\n\n return WebcryptoKey.fromKeypair({ privateKey, publicKey });\n}\n","import type { Key } from \"@atproto/jwk\";\nimport type { TokenInfo } from \"./types\";\n\nexport interface HappyViewSessionOptions {\n did: string;\n dpopKey: Key;\n accessToken: string;\n clientKey: string;\n instanceUrl: string;\n scopes?: string;\n pdsUrl?: string;\n issuer?: string;\n fetch?: typeof globalThis.fetch;\n onSignOut?: () => Promise<void>;\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = crypto.getRandomValues(new Uint8Array(byteLength));\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nfunction base64url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nexport class HappyViewSession {\n readonly did: string;\n\n private readonly dpopKey: Key;\n private readonly accessToken: string;\n private readonly clientKey: string;\n private readonly instanceUrl: string;\n private readonly _scopes: string | undefined;\n private readonly _pdsUrl: string | undefined;\n private readonly _issuer: string | undefined;\n private readonly _fetch: typeof globalThis.fetch;\n private readonly _onSignOut: (() => Promise<void>) | undefined;\n private dpopNonce: string | undefined;\n\n constructor(options: HappyViewSessionOptions) {\n this.did = options.did;\n this.dpopKey = options.dpopKey;\n this.accessToken = options.accessToken;\n this.clientKey = options.clientKey;\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this._scopes = options.scopes;\n this._pdsUrl = options.pdsUrl;\n this._issuer = options.issuer;\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n this._onSignOut = options.onSignOut;\n }\n\n get sub(): string {\n return this.did;\n }\n\n getTokenInfo(): TokenInfo {\n return {\n scope: this._scopes,\n iss: this._issuer,\n aud: this._pdsUrl,\n sub: this.did,\n };\n }\n\n async signOut(): Promise<void> {\n if (this._onSignOut) {\n await this._onSignOut();\n }\n }\n\n async fetchHandler(url: string, init: RequestInit): Promise<Response> {\n const fullUrl = /^https?:\\/\\//i.test(url)\n ? url\n : `${this.instanceUrl}${url}`;\n const method = (init.method ?? \"GET\").toUpperCase();\n const htu = fullUrl.split(\"?\")[0];\n\n const sendWithProof = async (): Promise<Response> => {\n const tokenHash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(this.accessToken),\n );\n const ath = base64url(tokenHash);\n\n const proof = await this.dpopKey.createJwt(\n {\n alg: \"ES256\",\n typ: \"dpop+jwt\",\n jwk: this.dpopKey.publicJwk!,\n },\n {\n htm: method,\n htu,\n iat: Math.floor(Date.now() / 1000),\n jti: randomHex(16),\n ath,\n nonce: this.dpopNonce,\n },\n );\n\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `DPoP ${this.accessToken}`);\n headers.set(\"dpop\", proof);\n headers.set(\"x-client-key\", this.clientKey);\n\n return this._fetch(fullUrl, { ...init, headers });\n };\n\n let response: Response;\n try {\n response = await sendWithProof();\n } catch (e) {\n const info = `_fetch type: ${typeof this._fetch}, toString: ${String(this._fetch).slice(0, 120)}, url: ${fullUrl}`;\n throw new Error(`fetchHandler failed: ${(e as Error).message}\\n\\nDEBUG: ${info}`);\n }\n\n // Retry once if the server requires a DPoP nonce we didn't have\n const nonce = response.headers.get(\"dpop-nonce\");\n if (nonce && nonce !== this.dpopNonce && (response.status === 401 || response.status === 400)) {\n this.dpopNonce = nonce;\n try {\n response = await sendWithProof();\n } catch (e) {\n throw new Error(`fetchHandler retry failed: ${(e as Error).message}`);\n }\n const retryNonce = response.headers.get(\"dpop-nonce\");\n if (retryNonce) {\n this.dpopNonce = retryNonce;\n }\n } else if (nonce) {\n this.dpopNonce = nonce;\n }\n\n return response;\n }\n}\n","import type { StorageAdapter } from \"./types\";\n\nexport class MemoryStorage implements StorageAdapter {\n private data = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.data.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.data.set(key, value);\n }\n\n async delete(key: string): Promise<void> {\n this.data.delete(key);\n }\n}\n","import type { Key } from \"@atproto/jwk\";\nimport { ApiError } from \"./errors\";\nimport { importJwk } from \"./import-jwk\";\nimport { HappyViewSession } from \"./session\";\nimport { MemoryStorage } from \"./storage\";\nimport type {\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n SessionEventHooks,\n StorageAdapter,\n StoredSession,\n} from \"./types\";\n\nconst STORAGE_PREFIX = \"happyview:session:\";\nexport const LAST_ACTIVE_KEY = \"happyview:last-active-did\";\n\nexport interface FetchMetadataOptions {\n clientId: string;\n fetch?: typeof globalThis.fetch;\n signal?: AbortSignal;\n}\n\nexport class HappyViewOAuthClient {\n static async fetchMetadata({\n clientId,\n fetch: fetchFn = globalThis.fetch,\n signal,\n }: FetchMetadataOptions): Promise<Record<string, unknown>> {\n signal?.throwIfAborted();\n const resp = await fetchFn(clientId, { redirect: \"error\", signal });\n if (resp.status !== 200) {\n resp.body?.cancel?.();\n throw new Error(`Failed to fetch client metadata: ${resp.status}`);\n }\n const mime = resp.headers.get(\"content-type\")?.split(\";\")[0].trim();\n if (mime !== \"application/json\") {\n resp.body?.cancel?.();\n throw new Error(`Invalid client metadata content type: ${mime}`);\n }\n return resp.json() as Promise<Record<string, unknown>>;\n }\n\n protected readonly instanceUrl: string;\n protected readonly clientKey: string;\n protected readonly storage: StorageAdapter;\n private readonly clientSecret: string | undefined;\n protected readonly _fetch: typeof globalThis.fetch;\n private readonly sessionHooks: SessionEventHooks;\n\n constructor(\n options: HappyViewOAuthClientOptions & {\n fetch?: typeof globalThis.fetch;\n },\n ) {\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this.clientKey = options.clientKey;\n this.clientSecret = options.clientSecret;\n this.storage = options.storage ?? new MemoryStorage();\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n this.sessionHooks = options.sessionHooks ?? {};\n }\n\n get isConfidential(): boolean {\n return this.clientSecret !== undefined;\n }\n\n async provisionDpopKey(): Promise<{\n provisionId: string;\n dpopKey: Key;\n rawJwk: JsonWebKey;\n pkceVerifier?: string;\n }> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {};\n let pkceVerifier: string | undefined;\n\n if (!this.isConfidential) {\n pkceVerifier = generatePkceVerifier();\n const challenge = await computePkceChallenge(pkceVerifier);\n body.pkce_challenge = challenge;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/dpop-keys`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to provision DPoP key: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: ProvisionKeyResponse = await resp.json();\n const dpopKey = await importJwk(data.dpop_key);\n return {\n provisionId: data.provision_id,\n dpopKey,\n rawJwk: data.dpop_key,\n pkceVerifier,\n };\n }\n\n async registerSession(\n params: RegisterSessionParams,\n ): Promise<HappyViewSession> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {\n provision_id: params.provisionId,\n did: params.did,\n access_token: params.accessToken,\n refresh_token: params.refreshToken,\n scopes: params.scopes,\n pds_url: params.pdsUrl,\n issuer: params.issuer,\n };\n\n if (!this.isConfidential && params.pkceVerifier) {\n body.pkce_verifier = params.pkceVerifier;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/sessions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to register session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: RegisterSessionResponse = await resp.json();\n const dpopKey = await importJwk(params.dpopKey);\n\n const storedSession: StoredSession = {\n did: data.did,\n dpopKey: params.dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n scopes: params.scopes,\n pdsUrl: params.pdsUrl,\n issuer: params.issuer,\n };\n await this.storage.set(\n `${STORAGE_PREFIX}${data.did}`,\n JSON.stringify(storedSession),\n );\n await this.storage.set(LAST_ACTIVE_KEY, data.did);\n\n const session = new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n scopes: params.scopes,\n pdsUrl: params.pdsUrl,\n issuer: params.issuer,\n fetch: this._fetch,\n onSignOut: () => this.deleteSession(data.did),\n });\n\n this.sessionHooks.onSessionUpdate?.(data.did);\n\n return session;\n }\n\n async deleteSession(did: string): Promise<void> {\n const session = await this.restoreSession(did);\n if (session) {\n const resp = await session.fetchHandler(\n `${this.instanceUrl}/oauth/sessions/${did}`,\n { method: \"DELETE\" },\n );\n\n if (!resp.ok && resp.status !== 404) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to delete session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n }\n\n await this.storage.delete(`${STORAGE_PREFIX}${did}`);\n\n const lastActive = await this.storage.get(LAST_ACTIVE_KEY);\n if (lastActive === did) {\n await this.storage.delete(LAST_ACTIVE_KEY);\n }\n\n this.sessionHooks.onSessionDelete?.(did);\n }\n\n async restoreSession(did: string): Promise<HappyViewSession | null> {\n const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);\n if (!stored) return null;\n\n const data: StoredSession = JSON.parse(stored);\n const dpopKey = await importJwk(data.dpopKey);\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: data.accessToken,\n clientKey: data.clientKey,\n instanceUrl: data.instanceUrl,\n scopes: data.scopes,\n pdsUrl: data.pdsUrl,\n issuer: data.issuer,\n fetch: this._fetch,\n onSignOut: () => this.deleteSession(data.did),\n });\n }\n\n async restore(): Promise<HappyViewSession | null> {\n const did = await this.storage.get(LAST_ACTIVE_KEY);\n if (!did) return null;\n return this.restoreSession(did);\n }\n}\n\n// PKCE helpers — inline since they're just a few lines of Web Crypto\nfunction generatePkceVerifier(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nasync function computePkceChallenge(verifier: string): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(verifier),\n );\n const bytes = new Uint8Array(hash);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":["body"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/import-jwk.ts","../src/session.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export class HappyViewError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HappyViewError\";\n }\n}\n\nexport class ApiError extends HappyViewError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends HappyViewError {\n readonly status: number;\n\n constructor(message: string, status: number = 401) {\n super(message);\n this.name = \"AuthenticationError\";\n this.status = status;\n }\n}\n\nexport class InvalidStateError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidStateError\";\n }\n}\n\nexport class TokenExchangeError extends HappyViewError {\n readonly status: number;\n readonly body: string;\n\n constructor(message: string, status: number, body: string) {\n super(message);\n this.name = \"TokenExchangeError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport class ResolutionError extends HappyViewError {\n constructor(message: string) {\n super(message);\n this.name = \"ResolutionError\";\n }\n}\n","import { WebcryptoKey } from \"@atproto/jwk-webcrypto\";\n\n/**\n * Import a raw ES256 JWK (as returned by HappyView's /oauth/dpop-keys)\n * into a WebcryptoKey that can sign JWTs.\n */\nexport async function importJwk(jwk: JsonWebKey): Promise<WebcryptoKey> {\n const privateKey = await crypto.subtle.importKey(\n \"jwk\",\n jwk,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"sign\"],\n );\n\n // Derive public key by stripping the private component\n const { d: _, ...publicJwkFields } = jwk;\n const publicKey = await crypto.subtle.importKey(\n \"jwk\",\n publicJwkFields,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"verify\"],\n );\n\n return WebcryptoKey.fromKeypair({ privateKey, publicKey });\n}\n","import type { Key } from \"@atproto/jwk\";\n\nexport interface HappyViewSessionOptions {\n did: string;\n dpopKey: Key;\n accessToken: string;\n clientKey: string;\n instanceUrl: string;\n fetch?: typeof globalThis.fetch;\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = crypto.getRandomValues(new Uint8Array(byteLength));\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nfunction base64url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nexport class HappyViewSession {\n readonly did: string;\n\n private readonly dpopKey: Key;\n private readonly accessToken: string;\n private readonly clientKey: string;\n private readonly instanceUrl: string;\n private readonly _fetch: typeof globalThis.fetch;\n private dpopNonce: string | undefined;\n\n constructor(options: HappyViewSessionOptions) {\n this.did = options.did;\n this.dpopKey = options.dpopKey;\n this.accessToken = options.accessToken;\n this.clientKey = options.clientKey;\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n }\n\n async fetchHandler(url: string, init: RequestInit): Promise<Response> {\n const fullUrl = /^https?:\\/\\//i.test(url)\n ? url\n : `${this.instanceUrl}${url}`;\n const method = (init.method ?? \"GET\").toUpperCase();\n const htu = fullUrl.split(\"?\")[0];\n\n const sendWithProof = async (): Promise<Response> => {\n const tokenHash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(this.accessToken),\n );\n const ath = base64url(tokenHash);\n\n const proof = await this.dpopKey.createJwt(\n {\n alg: \"ES256\",\n typ: \"dpop+jwt\",\n jwk: this.dpopKey.publicJwk!,\n },\n {\n htm: method,\n htu,\n iat: Math.floor(Date.now() / 1000),\n jti: randomHex(16),\n ath,\n nonce: this.dpopNonce,\n },\n );\n\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `DPoP ${this.accessToken}`);\n headers.set(\"dpop\", proof);\n headers.set(\"x-client-key\", this.clientKey);\n\n return this._fetch(fullUrl, { ...init, headers });\n };\n\n let response: Response;\n try {\n response = await sendWithProof();\n } catch (e) {\n const info = `_fetch type: ${typeof this._fetch}, toString: ${String(this._fetch).slice(0, 120)}, url: ${fullUrl}`;\n throw new Error(`fetchHandler failed: ${(e as Error).message}\\n\\nDEBUG: ${info}`);\n }\n\n // Retry once if the server requires a DPoP nonce we didn't have\n const nonce = response.headers.get(\"dpop-nonce\");\n if (nonce && nonce !== this.dpopNonce && (response.status === 401 || response.status === 400)) {\n this.dpopNonce = nonce;\n try {\n response = await sendWithProof();\n } catch (e) {\n throw new Error(`fetchHandler retry failed: ${(e as Error).message}`);\n }\n const retryNonce = response.headers.get(\"dpop-nonce\");\n if (retryNonce) {\n this.dpopNonce = retryNonce;\n }\n } else if (nonce) {\n this.dpopNonce = nonce;\n }\n\n return response;\n }\n}\n","import type { StorageAdapter } from \"./types\";\n\nexport class MemoryStorage implements StorageAdapter {\n private data = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.data.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.data.set(key, value);\n }\n\n async delete(key: string): Promise<void> {\n this.data.delete(key);\n }\n}\n","import type { Key } from \"@atproto/jwk\";\nimport { ApiError } from \"./errors\";\nimport { importJwk } from \"./import-jwk\";\nimport { HappyViewSession } from \"./session\";\nimport { MemoryStorage } from \"./storage\";\nimport type {\n HappyViewOAuthClientOptions,\n ProvisionKeyResponse,\n RegisterSessionParams,\n RegisterSessionResponse,\n StorageAdapter,\n StoredSession,\n} from \"./types\";\n\nconst STORAGE_PREFIX = \"happyview:session:\";\nexport const LAST_ACTIVE_KEY = \"happyview:last-active-did\";\n\nexport class HappyViewOAuthClient {\n protected readonly instanceUrl: string;\n protected readonly clientKey: string;\n protected readonly storage: StorageAdapter;\n private readonly clientSecret: string | undefined;\n protected readonly _fetch: typeof globalThis.fetch;\n\n constructor(\n options: HappyViewOAuthClientOptions & {\n fetch?: typeof globalThis.fetch;\n },\n ) {\n this.instanceUrl = options.instanceUrl.replace(/\\/+$/, \"\");\n this.clientKey = options.clientKey;\n this.clientSecret = options.clientSecret;\n this.storage = options.storage ?? new MemoryStorage();\n this._fetch = options.fetch ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)) as typeof globalThis.fetch;\n }\n\n get isConfidential(): boolean {\n return this.clientSecret !== undefined;\n }\n\n async provisionDpopKey(): Promise<{\n provisionId: string;\n dpopKey: Key;\n rawJwk: JsonWebKey;\n pkceVerifier?: string;\n }> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {};\n let pkceVerifier: string | undefined;\n\n if (!this.isConfidential) {\n pkceVerifier = generatePkceVerifier();\n const challenge = await computePkceChallenge(pkceVerifier);\n body.pkce_challenge = challenge;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/dpop-keys`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to provision DPoP key: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: ProvisionKeyResponse = await resp.json();\n const dpopKey = await importJwk(data.dpop_key);\n return {\n provisionId: data.provision_id,\n dpopKey,\n rawJwk: data.dpop_key,\n pkceVerifier,\n };\n }\n\n async registerSession(\n params: RegisterSessionParams,\n ): Promise<HappyViewSession> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"x-client-key\": this.clientKey,\n };\n if (this.clientSecret) {\n headers[\"x-client-secret\"] = this.clientSecret;\n }\n\n const body: Record<string, unknown> = {\n provision_id: params.provisionId,\n did: params.did,\n access_token: params.accessToken,\n refresh_token: params.refreshToken,\n scopes: params.scopes,\n pds_url: params.pdsUrl,\n issuer: params.issuer,\n };\n\n if (!this.isConfidential && params.pkceVerifier) {\n body.pkce_verifier = params.pkceVerifier;\n }\n\n const resp = await this._fetch(`${this.instanceUrl}/oauth/sessions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to register session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n\n const data: RegisterSessionResponse = await resp.json();\n const dpopKey = await importJwk(params.dpopKey);\n\n const storedSession: StoredSession = {\n did: data.did,\n dpopKey: params.dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n };\n await this.storage.set(\n `${STORAGE_PREFIX}${data.did}`,\n JSON.stringify(storedSession),\n );\n await this.storage.set(LAST_ACTIVE_KEY, data.did);\n\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: params.accessToken,\n clientKey: this.clientKey,\n instanceUrl: this.instanceUrl,\n fetch: this._fetch,\n });\n }\n\n async deleteSession(did: string): Promise<void> {\n const session = await this.restoreSession(did);\n if (session) {\n const resp = await session.fetchHandler(\n `${this.instanceUrl}/oauth/sessions/${did}`,\n { method: \"DELETE\" },\n );\n\n if (!resp.ok && resp.status !== 404) {\n const body = await resp.json().catch(() => ({}));\n throw new ApiError(\n `Failed to delete session: ${resp.status} ${(body as any).message ?? resp.statusText}`,\n resp.status,\n body,\n );\n }\n }\n\n await this.storage.delete(`${STORAGE_PREFIX}${did}`);\n\n const lastActive = await this.storage.get(LAST_ACTIVE_KEY);\n if (lastActive === did) {\n await this.storage.delete(LAST_ACTIVE_KEY);\n }\n }\n\n async restoreSession(did: string): Promise<HappyViewSession | null> {\n const stored = await this.storage.get(`${STORAGE_PREFIX}${did}`);\n if (!stored) return null;\n\n const data: StoredSession = JSON.parse(stored);\n const dpopKey = await importJwk(data.dpopKey);\n return new HappyViewSession({\n did: data.did,\n dpopKey,\n accessToken: data.accessToken,\n clientKey: data.clientKey,\n instanceUrl: data.instanceUrl,\n fetch: this._fetch,\n });\n }\n\n async restore(): Promise<HappyViewSession | null> {\n const did = await this.storage.get(LAST_ACTIVE_KEY);\n if (!did) return null;\n return this.restoreSession(did);\n }\n}\n\n// PKCE helpers — inline since they're just a few lines of Web Crypto\nfunction generatePkceVerifier(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nasync function computePkceChallenge(verifier: string): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(verifier),\n );\n const bytes = new Uint8Array(hash);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":["body"]}
|