Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql

feat(client): add scope parameter support

- Add scope to LoginOptions interface and authorize URL params
- Add scope to QuicksliceClientOptions with per-login override
- Update README with scope documentation
- Bump version to 0.2.0

+219 -12
+6
CHANGELOG.md
··· 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 + ## quickslice-client-js v0.2.0 9 + 10 + ### Added 11 + - Add `scope` parameter to `QuicksliceClientOptions` for setting default OAuth scope 12 + - Add `scope` option to `loginWithRedirect()` for per-login scope override 13 + 8 14 ## v0.17.3 9 15 10 16 ### Fixed
+174
dev-docs/plans/2025-12-15-client-js-scope-support.md
··· 1 + # Add Scope Support to quickslice-client-js 2 + 3 + > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. 4 + 5 + **Goal:** Allow passing OAuth `scope` parameter in the authorize flow. 6 + 7 + **Architecture:** Add optional `scope` field to client options and login options. Pass through to authorize URL when provided. Server uses its default when omitted. 8 + 9 + **Tech Stack:** TypeScript, esbuild 10 + 11 + --- 12 + 13 + ## Task 1: Add scope to LoginOptions and authorize URL 14 + 15 + **Files:** 16 + - Modify: `quickslice-client-js/src/auth/oauth.ts:7-10` (LoginOptions interface) 17 + - Modify: `quickslice-client-js/src/auth/oauth.ts:34-41` (params building) 18 + 19 + **Step 1: Add scope to LoginOptions interface** 20 + 21 + In `quickslice-client-js/src/auth/oauth.ts`, update the `LoginOptions` interface: 22 + 23 + ```typescript 24 + export interface LoginOptions { 25 + handle?: string; 26 + redirectUri?: string; 27 + scope?: string; 28 + } 29 + ``` 30 + 31 + **Step 2: Add scope to authorize URL params** 32 + 33 + In the `initiateLogin` function, after line 45 (`if (options.handle)`), add: 34 + 35 + ```typescript 36 + if (options.scope) { 37 + params.set('scope', options.scope); 38 + } 39 + ``` 40 + 41 + **Step 3: Commit** 42 + 43 + ```bash 44 + git add quickslice-client-js/src/auth/oauth.ts 45 + git commit -m "feat(client): add scope parameter to LoginOptions" 46 + ``` 47 + 48 + --- 49 + 50 + ## Task 2: Add scope to QuicksliceClientOptions and pass through 51 + 52 + **Files:** 53 + - Modify: `quickslice-client-js/src/client.ts:8-12` (QuicksliceClientOptions interface) 54 + - Modify: `quickslice-client-js/src/client.ts:18-35` (constructor and fields) 55 + - Modify: `quickslice-client-js/src/client.ts:52-58` (loginWithRedirect) 56 + 57 + **Step 1: Add scope to QuicksliceClientOptions** 58 + 59 + Update the interface: 60 + 61 + ```typescript 62 + export interface QuicksliceClientOptions { 63 + server: string; 64 + clientId: string; 65 + redirectUri?: string; 66 + scope?: string; 67 + } 68 + ``` 69 + 70 + **Step 2: Add scope field to class** 71 + 72 + Add after line 21 (`private redirectUri?: string;`): 73 + 74 + ```typescript 75 + private scope?: string; 76 + ``` 77 + 78 + **Step 3: Store scope in constructor** 79 + 80 + Add after line 30 (`this.redirectUri = options.redirectUri;`): 81 + 82 + ```typescript 83 + this.scope = options.scope; 84 + ``` 85 + 86 + **Step 4: Pass scope through in loginWithRedirect** 87 + 88 + Update the `loginWithRedirect` method to pass scope: 89 + 90 + ```typescript 91 + async loginWithRedirect(options: LoginOptions = {}): Promise<void> { 92 + await this.init(); 93 + await initiateLogin(this.authorizeUrl, this.clientId, { 94 + ...options, 95 + redirectUri: options.redirectUri || this.redirectUri, 96 + scope: options.scope || this.scope, 97 + }); 98 + } 99 + ``` 100 + 101 + **Step 5: Commit** 102 + 103 + ```bash 104 + git add quickslice-client-js/src/client.ts 105 + git commit -m "feat(client): add scope to client options with per-login override" 106 + ``` 107 + 108 + --- 109 + 110 + ## Task 3: Build and verify 111 + 112 + **Step 1: Build the library** 113 + 114 + ```bash 115 + cd /Users/chadmiller/code/quickslice/quickslice-client-js && npm run build 116 + ``` 117 + 118 + Expected: `Build complete!` 119 + 120 + **Step 2: Generate TypeScript declarations** 121 + 122 + ```bash 123 + cd /Users/chadmiller/code/quickslice/quickslice-client-js && npx tsc 124 + ``` 125 + 126 + Expected: No errors 127 + 128 + **Step 3: Commit dist files** 129 + 130 + ```bash 131 + git add quickslice-client-js/dist/ 132 + git commit -m "chore(client): rebuild dist with scope support" 133 + ``` 134 + 135 + --- 136 + 137 + ## Task 4: Update README 138 + 139 + **Files:** 140 + - Modify: `quickslice-client-js/README.md` 141 + 142 + **Step 1: Add scope to usage examples** 143 + 144 + In the Usage section, update the initialization example to show scope: 145 + 146 + ```javascript 147 + // Initialize client with default scope 148 + const client = await QuicksliceClient.createQuicksliceClient({ 149 + server: 'https://api.example.com', 150 + clientId: 'client_abc123', 151 + scope: 'atproto', // optional - server uses default if omitted 152 + }); 153 + 154 + // Or specify scope per-login 155 + await client.loginWithRedirect({ 156 + handle: 'alice.bsky.social', 157 + scope: 'atproto transition:generic' 158 + }); 159 + ``` 160 + 161 + **Step 2: Update API docs** 162 + 163 + In the Options section under `createQuicksliceClient`, add: 164 + - `scope` (optional): OAuth scope string to request 165 + 166 + In the Auth Methods section under `loginWithRedirect(options?)`, add: 167 + - `options.scope`: Override scope for this login 168 + 169 + **Step 3: Commit** 170 + 171 + ```bash 172 + git add quickslice-client-js/README.md 173 + git commit -m "docs(client): document scope parameter" 174 + ```
+9 -4
quickslice-client-js/README.md
··· 19 19 ## Usage 20 20 21 21 ```javascript 22 - // Initialize client 22 + // Initialize client (with optional default scope) 23 23 const client = await QuicksliceClient.createQuicksliceClient({ 24 24 server: 'https://api.example.com', 25 25 clientId: 'client_abc123', 26 26 redirectUri: 'https://yourapp.com/oauth/callback', // optional 27 + scope: 'atproto', // optional - server uses default if omitted 27 28 }); 28 29 29 30 // Handle OAuth callback (on page load) ··· 41 42 const profile = await client.query(`query { viewer { handle } }`); 42 43 } 43 44 44 - // Login 45 + // Login (can override scope per-login) 45 46 document.getElementById('login').onclick = async () => { 46 - await client.loginWithRedirect({ handle: 'alice.bsky.social' }); 47 + await client.loginWithRedirect({ 48 + handle: 'alice.bsky.social', 49 + scope: 'atproto transition:generic', // optional override 50 + }); 47 51 }; 48 52 49 53 // Logout ··· 81 85 - `server` (required): Quickslice server URL 82 86 - `clientId` (required): Pre-registered client ID 83 87 - `redirectUri` (optional): OAuth callback URL. Defaults to current page URL if omitted. Useful when you have a dedicated callback route. 88 + - `scope` (optional): OAuth scope string to request. Server uses its default if omitted. 84 89 85 90 ### `QuicksliceClient` 86 91 87 92 #### Auth Methods 88 93 89 - - `loginWithRedirect(options?)` - Start OAuth login flow 94 + - `loginWithRedirect(options?)` - Start OAuth login flow. Options: `handle`, `redirectUri`, `scope` (overrides client default) 90 95 - `handleRedirectCallback()` - Process OAuth callback 91 96 - `logout(options?)` - Clear session and reload 92 97 - `isAuthenticated()` - Check if logged in
+1
quickslice-client-js/dist/auth/oauth.d.ts
··· 1 1 export interface LoginOptions { 2 2 handle?: string; 3 3 redirectUri?: string; 4 + scope?: string; 4 5 } 5 6 /** 6 7 * Initiate OAuth login flow with PKCE
+2
quickslice-client-js/dist/client.d.ts
··· 3 3 server: string; 4 4 clientId: string; 5 5 redirectUri?: string; 6 + scope?: string; 6 7 } 7 8 export interface User { 8 9 did: string; ··· 11 12 private server; 12 13 private clientId; 13 14 private redirectUri?; 15 + private scope?; 14 16 private graphqlUrl; 15 17 private authorizeUrl; 16 18 private tokenUrl;
+6 -1
quickslice-client-js/dist/quickslice-client.esm.js
··· 329 329 if (options.handle) { 330 330 params.set("login_hint", options.handle); 331 331 } 332 + if (options.scope) { 333 + params.set("scope", options.scope); 334 + } 332 335 window.location.href = `${authorizeUrl}?${params.toString()}`; 333 336 } 334 337 async function handleOAuthCallback(tokenUrl) { ··· 427 430 this.server = options.server.replace(/\/$/, ""); 428 431 this.clientId = options.clientId; 429 432 this.redirectUri = options.redirectUri; 433 + this.scope = options.scope; 430 434 this.graphqlUrl = `${this.server}/graphql`; 431 435 this.authorizeUrl = `${this.server}/oauth/authorize`; 432 436 this.tokenUrl = `${this.server}/oauth/token`; ··· 446 450 await this.init(); 447 451 await initiateLogin(this.authorizeUrl, this.clientId, { 448 452 ...options, 449 - redirectUri: options.redirectUri || this.redirectUri 453 + redirectUri: options.redirectUri || this.redirectUri, 454 + scope: options.scope || this.scope 450 455 }); 451 456 } 452 457 /**
+2 -2
quickslice-client-js/dist/quickslice-client.esm.js.map
··· 1 1 { 2 2 "version": 3, 3 3 "sources": ["../src/storage/keys.ts", "../src/storage/storage.ts", "../src/utils/base64url.ts", "../src/utils/crypto.ts", "../src/auth/dpop.ts", "../src/auth/pkce.ts", "../src/storage/lock.ts", "../src/auth/tokens.ts", "../src/auth/oauth.ts", "../src/graphql.ts", "../src/client.ts", "../src/errors.ts", "../src/index.ts"], 4 - "sourcesContent": ["/**\n * Storage key constants\n */\nexport const STORAGE_KEYS = {\n accessToken: 'quickslice_access_token',\n refreshToken: 'quickslice_refresh_token',\n tokenExpiresAt: 'quickslice_token_expires_at',\n clientId: 'quickslice_client_id',\n userDid: 'quickslice_user_did',\n codeVerifier: 'quickslice_code_verifier',\n oauthState: 'quickslice_oauth_state',\n redirectUri: 'quickslice_redirect_uri',\n} as const;\n\nexport type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];\n", "import { STORAGE_KEYS, StorageKey } from './keys';\n\n/**\n * Hybrid storage utility - sessionStorage for OAuth flow state,\n * localStorage for tokens (shared across tabs)\n */\nexport const storage = {\n get(key: StorageKey): string | null {\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n return sessionStorage.getItem(key);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(key);\n },\n\n set(key: StorageKey, value: string): void {\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n sessionStorage.setItem(key, value);\n } else {\n localStorage.setItem(key, value);\n }\n },\n\n remove(key: StorageKey): void {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n },\n\n clear(): void {\n Object.values(STORAGE_KEYS).forEach((key) => {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n });\n },\n};\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : 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\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_NAME = 'quickslice-oauth';\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\n\nfunction openDatabase(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise;\n\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n return dbPromise;\n}\n\nasync function getDPoPKey(): Promise<DPoPKeyData | null> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey();\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey();\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\nconst LOCK_PREFIX = 'quickslice_lock_';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = LOCK_PREFIX + key;\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(key: string, lockValue: string): void {\n const lockKey = LOCK_PREFIX + key;\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(tokenUrl: string): Promise<string> {\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(tokenUrl: string): Promise<string> {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const expiresAt = parseInt(storage.get(STORAGE_KEYS.tokenExpiresAt) || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const lockKey = `token_refresh_${clientId}`;\n const lockValue = await acquireLock(lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(tokenUrl);\n } finally {\n releaseLock(lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n}): void {\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n if (tokens.sub) {\n storage.set(STORAGE_KEYS.userDid, tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(): boolean {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n return !!(accessToken || refreshToken);\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set(STORAGE_KEYS.codeVerifier, codeVerifier);\n storage.set(STORAGE_KEYS.oauthState, state);\n storage.set(STORAGE_KEYS.clientId, clientId);\n storage.set(STORAGE_KEYS.redirectUri, redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(tokenUrl: string): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get(STORAGE_KEYS.oauthState);\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get(STORAGE_KEYS.codeVerifier);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const redirectUri = storage.get(STORAGE_KEYS.redirectUri);\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(tokens);\n\n // Clean up OAuth state\n storage.remove(STORAGE_KEYS.codeVerifier);\n storage.remove(STORAGE_KEYS.oauthState);\n storage.remove(STORAGE_KEYS.redirectUri);\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(options: { reload?: boolean } = {}): Promise<void> {\n storage.clear();\n await clearDPoPKeys();\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof('POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { storage } from './storage/storage';\nimport { STORAGE_KEYS } from './storage/keys';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey();\n\n this.initialized = true;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await doLogout(options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n return hasValidSession();\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n getUser(): User | null {\n if (!hasValidSession()) {\n return null;\n }\n\n const did = storage.get(STORAGE_KEYS.userDid);\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n", "export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n"], 5 - "mappings": "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", 4 + "sourcesContent": ["/**\n * Storage key constants\n */\nexport const STORAGE_KEYS = {\n accessToken: 'quickslice_access_token',\n refreshToken: 'quickslice_refresh_token',\n tokenExpiresAt: 'quickslice_token_expires_at',\n clientId: 'quickslice_client_id',\n userDid: 'quickslice_user_did',\n codeVerifier: 'quickslice_code_verifier',\n oauthState: 'quickslice_oauth_state',\n redirectUri: 'quickslice_redirect_uri',\n} as const;\n\nexport type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];\n", "import { STORAGE_KEYS, StorageKey } from './keys';\n\n/**\n * Hybrid storage utility - sessionStorage for OAuth flow state,\n * localStorage for tokens (shared across tabs)\n */\nexport const storage = {\n get(key: StorageKey): string | null {\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n return sessionStorage.getItem(key);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(key);\n },\n\n set(key: StorageKey, value: string): void {\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n sessionStorage.setItem(key, value);\n } else {\n localStorage.setItem(key, value);\n }\n },\n\n remove(key: StorageKey): void {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n },\n\n clear(): void {\n Object.values(STORAGE_KEYS).forEach((key) => {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n });\n },\n};\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : 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\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_NAME = 'quickslice-oauth';\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\n\nfunction openDatabase(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise;\n\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n return dbPromise;\n}\n\nasync function getDPoPKey(): Promise<DPoPKeyData | null> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey();\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey();\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\nconst LOCK_PREFIX = 'quickslice_lock_';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = LOCK_PREFIX + key;\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(key: string, lockValue: string): void {\n const lockKey = LOCK_PREFIX + key;\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(tokenUrl: string): Promise<string> {\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(tokenUrl: string): Promise<string> {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const expiresAt = parseInt(storage.get(STORAGE_KEYS.tokenExpiresAt) || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const lockKey = `token_refresh_${clientId}`;\n const lockValue = await acquireLock(lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(tokenUrl);\n } finally {\n releaseLock(lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n}): void {\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n if (tokens.sub) {\n storage.set(STORAGE_KEYS.userDid, tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(): boolean {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n return !!(accessToken || refreshToken);\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set(STORAGE_KEYS.codeVerifier, codeVerifier);\n storage.set(STORAGE_KEYS.oauthState, state);\n storage.set(STORAGE_KEYS.clientId, clientId);\n storage.set(STORAGE_KEYS.redirectUri, redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(tokenUrl: string): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get(STORAGE_KEYS.oauthState);\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get(STORAGE_KEYS.codeVerifier);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const redirectUri = storage.get(STORAGE_KEYS.redirectUri);\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(tokens);\n\n // Clean up OAuth state\n storage.remove(STORAGE_KEYS.codeVerifier);\n storage.remove(STORAGE_KEYS.oauthState);\n storage.remove(STORAGE_KEYS.redirectUri);\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(options: { reload?: boolean } = {}): Promise<void> {\n storage.clear();\n await clearDPoPKeys();\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof('POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { storage } from './storage/storage';\nimport { STORAGE_KEYS } from './storage/keys';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey();\n\n this.initialized = true;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await doLogout(options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n return hasValidSession();\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n getUser(): User | null {\n if (!hasValidSession()) {\n return null;\n }\n\n const did = storage.get(STORAGE_KEYS.userDid);\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n", "export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n"], 5 + "mappings": "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", 6 6 "names": ["sleep"] 7 7 }
+6 -1
quickslice-client-js/dist/quickslice-client.js
··· 360 360 if (options.handle) { 361 361 params.set("login_hint", options.handle); 362 362 } 363 + if (options.scope) { 364 + params.set("scope", options.scope); 365 + } 363 366 window.location.href = `${authorizeUrl}?${params.toString()}`; 364 367 } 365 368 async function handleOAuthCallback(tokenUrl) { ··· 458 461 this.server = options.server.replace(/\/$/, ""); 459 462 this.clientId = options.clientId; 460 463 this.redirectUri = options.redirectUri; 464 + this.scope = options.scope; 461 465 this.graphqlUrl = `${this.server}/graphql`; 462 466 this.authorizeUrl = `${this.server}/oauth/authorize`; 463 467 this.tokenUrl = `${this.server}/oauth/token`; ··· 477 481 await this.init(); 478 482 await initiateLogin(this.authorizeUrl, this.clientId, { 479 483 ...options, 480 - redirectUri: options.redirectUri || this.redirectUri 484 + redirectUri: options.redirectUri || this.redirectUri, 485 + scope: options.scope || this.scope 481 486 }); 482 487 } 483 488 /**
+2 -2
quickslice-client-js/dist/quickslice-client.js.map
··· 1 1 { 2 2 "version": 3, 3 3 "sources": ["../src/index.ts", "../src/storage/keys.ts", "../src/storage/storage.ts", "../src/utils/base64url.ts", "../src/utils/crypto.ts", "../src/auth/dpop.ts", "../src/auth/pkce.ts", "../src/storage/lock.ts", "../src/auth/tokens.ts", "../src/auth/oauth.ts", "../src/graphql.ts", "../src/client.ts", "../src/errors.ts"], 4 - "sourcesContent": ["export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n", "/**\n * Storage key constants\n */\nexport const STORAGE_KEYS = {\n accessToken: 'quickslice_access_token',\n refreshToken: 'quickslice_refresh_token',\n tokenExpiresAt: 'quickslice_token_expires_at',\n clientId: 'quickslice_client_id',\n userDid: 'quickslice_user_did',\n codeVerifier: 'quickslice_code_verifier',\n oauthState: 'quickslice_oauth_state',\n redirectUri: 'quickslice_redirect_uri',\n} as const;\n\nexport type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];\n", "import { STORAGE_KEYS, StorageKey } from './keys';\n\n/**\n * Hybrid storage utility - sessionStorage for OAuth flow state,\n * localStorage for tokens (shared across tabs)\n */\nexport const storage = {\n get(key: StorageKey): string | null {\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n return sessionStorage.getItem(key);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(key);\n },\n\n set(key: StorageKey, value: string): void {\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n sessionStorage.setItem(key, value);\n } else {\n localStorage.setItem(key, value);\n }\n },\n\n remove(key: StorageKey): void {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n },\n\n clear(): void {\n Object.values(STORAGE_KEYS).forEach((key) => {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n });\n },\n};\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : 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\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_NAME = 'quickslice-oauth';\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\n\nfunction openDatabase(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise;\n\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n return dbPromise;\n}\n\nasync function getDPoPKey(): Promise<DPoPKeyData | null> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey();\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey();\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\nconst LOCK_PREFIX = 'quickslice_lock_';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = LOCK_PREFIX + key;\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(key: string, lockValue: string): void {\n const lockKey = LOCK_PREFIX + key;\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(tokenUrl: string): Promise<string> {\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(tokenUrl: string): Promise<string> {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const expiresAt = parseInt(storage.get(STORAGE_KEYS.tokenExpiresAt) || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const lockKey = `token_refresh_${clientId}`;\n const lockValue = await acquireLock(lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(tokenUrl);\n } finally {\n releaseLock(lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n}): void {\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n if (tokens.sub) {\n storage.set(STORAGE_KEYS.userDid, tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(): boolean {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n return !!(accessToken || refreshToken);\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set(STORAGE_KEYS.codeVerifier, codeVerifier);\n storage.set(STORAGE_KEYS.oauthState, state);\n storage.set(STORAGE_KEYS.clientId, clientId);\n storage.set(STORAGE_KEYS.redirectUri, redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(tokenUrl: string): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get(STORAGE_KEYS.oauthState);\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get(STORAGE_KEYS.codeVerifier);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const redirectUri = storage.get(STORAGE_KEYS.redirectUri);\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(tokens);\n\n // Clean up OAuth state\n storage.remove(STORAGE_KEYS.codeVerifier);\n storage.remove(STORAGE_KEYS.oauthState);\n storage.remove(STORAGE_KEYS.redirectUri);\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(options: { reload?: boolean } = {}): Promise<void> {\n storage.clear();\n await clearDPoPKeys();\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof('POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { storage } from './storage/storage';\nimport { STORAGE_KEYS } from './storage/keys';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey();\n\n this.initialized = true;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await doLogout(options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n return hasValidSession();\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n getUser(): User | null {\n if (!hasValidSession()) {\n return null;\n }\n\n const did = storage.get(STORAGE_KEYS.userDid);\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n"], 5 - "mappings": "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", 4 + "sourcesContent": ["export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n", "/**\n * Storage key constants\n */\nexport const STORAGE_KEYS = {\n accessToken: 'quickslice_access_token',\n refreshToken: 'quickslice_refresh_token',\n tokenExpiresAt: 'quickslice_token_expires_at',\n clientId: 'quickslice_client_id',\n userDid: 'quickslice_user_did',\n codeVerifier: 'quickslice_code_verifier',\n oauthState: 'quickslice_oauth_state',\n redirectUri: 'quickslice_redirect_uri',\n} as const;\n\nexport type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];\n", "import { STORAGE_KEYS, StorageKey } from './keys';\n\n/**\n * Hybrid storage utility - sessionStorage for OAuth flow state,\n * localStorage for tokens (shared across tabs)\n */\nexport const storage = {\n get(key: StorageKey): string | null {\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n return sessionStorage.getItem(key);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(key);\n },\n\n set(key: StorageKey, value: string): void {\n if (key === STORAGE_KEYS.codeVerifier || key === STORAGE_KEYS.oauthState) {\n sessionStorage.setItem(key, value);\n } else {\n localStorage.setItem(key, value);\n }\n },\n\n remove(key: StorageKey): void {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n },\n\n clear(): void {\n Object.values(STORAGE_KEYS).forEach((key) => {\n sessionStorage.removeItem(key);\n localStorage.removeItem(key);\n });\n },\n};\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : 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\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_NAME = 'quickslice-oauth';\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\n\nfunction openDatabase(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise;\n\n dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n return dbPromise;\n}\n\nasync function getDPoPKey(): Promise<DPoPKeyData | null> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey();\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey();\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(): Promise<void> {\n const db = await openDatabase();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\nconst LOCK_PREFIX = 'quickslice_lock_';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = LOCK_PREFIX + key;\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(key: string, lockValue: string): void {\n const lockKey = LOCK_PREFIX + key;\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(tokenUrl: string): Promise<string> {\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(tokenUrl: string): Promise<string> {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const expiresAt = parseInt(storage.get(STORAGE_KEYS.tokenExpiresAt) || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const lockKey = `token_refresh_${clientId}`;\n const lockValue = await acquireLock(lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get(STORAGE_KEYS.accessToken);\n const freshExpiry = parseInt(\n storage.get(STORAGE_KEYS.tokenExpiresAt) || '0'\n );\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(tokenUrl);\n } finally {\n releaseLock(lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n}): void {\n storage.set(STORAGE_KEYS.accessToken, tokens.access_token);\n if (tokens.refresh_token) {\n storage.set(STORAGE_KEYS.refreshToken, tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set(STORAGE_KEYS.tokenExpiresAt, expiresAt.toString());\n\n if (tokens.sub) {\n storage.set(STORAGE_KEYS.userDid, tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(): boolean {\n const accessToken = storage.get(STORAGE_KEYS.accessToken);\n const refreshToken = storage.get(STORAGE_KEYS.refreshToken);\n return !!(accessToken || refreshToken);\n}\n", "import { storage } from '../storage/storage';\nimport { STORAGE_KEYS } from '../storage/keys';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set(STORAGE_KEYS.codeVerifier, codeVerifier);\n storage.set(STORAGE_KEYS.oauthState, state);\n storage.set(STORAGE_KEYS.clientId, clientId);\n storage.set(STORAGE_KEYS.redirectUri, redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(tokenUrl: string): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get(STORAGE_KEYS.oauthState);\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get(STORAGE_KEYS.codeVerifier);\n const clientId = storage.get(STORAGE_KEYS.clientId);\n const redirectUri = storage.get(STORAGE_KEYS.redirectUri);\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof('POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(tokens);\n\n // Clean up OAuth state\n storage.remove(STORAGE_KEYS.codeVerifier);\n storage.remove(STORAGE_KEYS.oauthState);\n storage.remove(STORAGE_KEYS.redirectUri);\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(options: { reload?: boolean } = {}): Promise<void> {\n storage.clear();\n await clearDPoPKeys();\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof('POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { storage } from './storage/storage';\nimport { STORAGE_KEYS } from './storage/keys';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey();\n\n this.initialized = true;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await doLogout(options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n return hasValidSession();\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n getUser(): User | null {\n if (!hasValidSession()) {\n return null;\n }\n\n const did = storage.get(STORAGE_KEYS.userDid);\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n"], 5 + "mappings": "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", 6 6 "names": ["sleep"] 7 7 }
+1 -1
quickslice-client-js/dist/quickslice-client.min.js
··· 1 - "use strict";var QuicksliceClient=(()=>{var x=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var H=(t,e)=>{for(var r in e)x(t,r,{get:e[r],enumerable:!0})},W=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of M(e))!F.call(t,a)&&a!==r&&x(t,a,{get:()=>e[a],enumerable:!(n=Y(e,a))||n.enumerable});return t};var X=t=>W(x({},"__esModule",{value:!0}),t);var se={};H(se,{LoginRequiredError:()=>S,NetworkError:()=>T,OAuthError:()=>_,QuicksliceClient:()=>m,QuicksliceError:()=>h,createQuicksliceClient:()=>ie});var o={accessToken:"quickslice_access_token",refreshToken:"quickslice_refresh_token",tokenExpiresAt:"quickslice_token_expires_at",clientId:"quickslice_client_id",userDid:"quickslice_user_did",codeVerifier:"quickslice_code_verifier",oauthState:"quickslice_oauth_state",redirectUri:"quickslice_redirect_uri"};var s={get(t){return t===o.codeVerifier||t===o.oauthState?sessionStorage.getItem(t):localStorage.getItem(t)},set(t,e){t===o.codeVerifier||t===o.oauthState?sessionStorage.setItem(t,e):localStorage.setItem(t,e)},remove(t){sessionStorage.removeItem(t),localStorage.removeItem(t)},clear(){Object.values(o).forEach(t=>{sessionStorage.removeItem(t),localStorage.removeItem(t)})}};function d(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),r="";for(let n=0;n<e.length;n++)r+=String.fromCharCode(e[n]);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function k(t){let e=new Uint8Array(t);return crypto.getRandomValues(e),d(e)}async function A(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return d(r)}async function K(t,e,r){let n=new TextEncoder,a=d(n.encode(JSON.stringify(t))),i=d(n.encode(JSON.stringify(e))),c=`${a}.${i}`,l=await crypto.subtle.sign({name:"ECDSA",hash:"SHA-256"},r,n.encode(c)),u=d(l);return`${c}.${u}`}var Z="quickslice-oauth",ee=1,g="dpop-keys",E="dpop-key",y=null;function b(){return y||(y=new Promise((t,e)=>{let r=indexedDB.open(Z,ee);r.onerror=()=>e(r.error),r.onsuccess=()=>t(r.result),r.onupgradeneeded=n=>{let a=n.target.result;a.objectStoreNames.contains(g)||a.createObjectStore(g,{keyPath:"id"})}}),y)}async function te(){let t=await b();return new Promise((e,r)=>{let i=t.transaction(g,"readonly").objectStore(g).get(E);i.onerror=()=>r(i.error),i.onsuccess=()=>e(i.result||null)})}async function re(t,e){let r=await b();return new Promise((n,a)=>{let l=r.transaction(g,"readwrite").objectStore(g).put({id:E,privateKey:t,publicJwk:e,createdAt:Date.now()});l.onerror=()=>a(l.error),l.onsuccess=()=>n()})}async function D(){let t=await te();if(t)return t;let e=await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"]),r=await crypto.subtle.exportKey("jwk",e.publicKey);return await re(e.privateKey,r),{id:E,privateKey:e.privateKey,publicJwk:r,createdAt:Date.now()}}async function f(t,e,r=null){let n=await D(),{kty:a,crv:i,x:c,y:l}=n.publicJwk,w={alg:"ES256",typ:"dpop+jwt",jwk:{kty:a,crv:i,x:c,y:l}},p={jti:k(16),htm:t,htu:e,iat:Math.floor(Date.now()/1e3)};return r&&(p.ath=await A(r)),await K(w,p,n.privateKey)}async function I(){let t=await b();return new Promise((e,r)=>{let i=t.transaction(g,"readwrite").objectStore(g).clear();i.onerror=()=>r(i.error),i.onsuccess=()=>e()})}function R(){return k(32)}async function C(t){let r=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",r);return d(n)}function q(){return k(16)}var $="quickslice_lock_";function L(t){return new Promise(e=>setTimeout(e,t))}async function V(t,e=5e3){let r=$+t,n=`${Date.now()}_${Math.random()}`,a=Date.now()+e;for(;Date.now()<a;){let i=localStorage.getItem(r);if(i){let[c]=i.split("_");if(Date.now()-parseInt(c)>5e3)localStorage.removeItem(r);else{await L(50);continue}}if(localStorage.setItem(r,n),await L(10),localStorage.getItem(r)===n)return n}return null}function j(t,e){let r=$+t;localStorage.getItem(r)===e&&localStorage.removeItem(r)}var v=6e4;function oe(t){return new Promise(e=>setTimeout(e,t))}async function ne(t){let e=s.get(o.refreshToken),r=s.get(o.clientId);if(!e||!r)throw new Error("No refresh token available");let n=await f("POST",t),a=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:n},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:r})});if(!a.ok){let l=await a.json().catch(()=>({}));throw new Error(`Token refresh failed: ${l.error_description||a.statusText}`)}let i=await a.json();s.set(o.accessToken,i.access_token),i.refresh_token&&s.set(o.refreshToken,i.refresh_token);let c=Date.now()+i.expires_in*1e3;return s.set(o.tokenExpiresAt,c.toString()),i.access_token}async function P(t){let e=s.get(o.accessToken),r=parseInt(s.get(o.tokenExpiresAt)||"0");if(e&&Date.now()<r-v)return e;let a=`token_refresh_${s.get(o.clientId)}`,i=await V(a);if(!i){await oe(100);let c=s.get(o.accessToken),l=parseInt(s.get(o.tokenExpiresAt)||"0");if(c&&Date.now()<l-v)return c;throw new Error("Failed to refresh token")}try{let c=s.get(o.accessToken),l=parseInt(s.get(o.tokenExpiresAt)||"0");return c&&Date.now()<l-v?c:await ne(t)}finally{j(a,i)}}function B(t){s.set(o.accessToken,t.access_token),t.refresh_token&&s.set(o.refreshToken,t.refresh_token);let e=Date.now()+t.expires_in*1e3;s.set(o.tokenExpiresAt,e.toString()),t.sub&&s.set(o.userDid,t.sub)}function O(){let t=s.get(o.accessToken),e=s.get(o.refreshToken);return!!(t||e)}async function Q(t,e,r={}){let n=R(),a=await C(n),i=q(),c=r.redirectUri||window.location.origin+window.location.pathname;s.set(o.codeVerifier,n),s.set(o.oauthState,i),s.set(o.clientId,e),s.set(o.redirectUri,c);let l=new URLSearchParams({client_id:e,redirect_uri:c,response_type:"code",code_challenge:a,code_challenge_method:"S256",state:i});r.handle&&l.set("login_hint",r.handle),window.location.href=`${t}?${l.toString()}`}async function J(t){let e=new URLSearchParams(window.location.search),r=e.get("code"),n=e.get("state"),a=e.get("error");if(a)throw new Error(`OAuth error: ${a} - ${e.get("error_description")||""}`);if(!r||!n)return!1;let i=s.get(o.oauthState);if(n!==i)throw new Error("OAuth state mismatch - possible CSRF attack");let c=s.get(o.codeVerifier),l=s.get(o.clientId),u=s.get(o.redirectUri);if(!c||!l||!u)throw new Error("Missing OAuth session data");let w=await f("POST",t),p=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:w},body:new URLSearchParams({grant_type:"authorization_code",code:r,redirect_uri:u,client_id:l,code_verifier:c})});if(!p.ok){let z=await p.json().catch(()=>({}));throw new Error(`Token exchange failed: ${z.error_description||p.statusText}`)}let N=await p.json();return B(N),s.remove(o.codeVerifier),s.remove(o.oauthState),s.remove(o.redirectUri),window.history.replaceState({},document.title,window.location.pathname),!0}async function G(t={}){s.clear(),await I(),t.reload!==!1&&window.location.reload()}async function U(t,e,r,n={},a=!1){let i={"Content-Type":"application/json"};if(a){let u=await P(e);if(!u)throw new Error("Not authenticated");let w=await f("POST",t,u);i.Authorization=`DPoP ${u}`,i.DPoP=w}let c=await fetch(t,{method:"POST",headers:i,body:JSON.stringify({query:r,variables:n})});if(!c.ok)throw new Error(`GraphQL request failed: ${c.statusText}`);let l=await c.json();if(l.errors&&l.errors.length>0)throw new Error(`GraphQL error: ${l.errors[0].message}`);return l.data}var m=class{constructor(e){this.initialized=!1;this.server=e.server.replace(/\/$/,""),this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.graphqlUrl=`${this.server}/graphql`,this.authorizeUrl=`${this.server}/oauth/authorize`,this.tokenUrl=`${this.server}/oauth/token`}async init(){this.initialized||(await D(),this.initialized=!0)}async loginWithRedirect(e={}){await this.init(),await Q(this.authorizeUrl,this.clientId,{...e,redirectUri:e.redirectUri||this.redirectUri})}async handleRedirectCallback(){return await this.init(),await J(this.tokenUrl)}async logout(e={}){await G(e)}async isAuthenticated(){return O()}getUser(){if(!O())return null;let e=s.get(o.userDid);return e?{did:e}:null}async getAccessToken(){return await this.init(),await P(this.tokenUrl)}async query(e,r={}){return await this.init(),await U(this.graphqlUrl,this.tokenUrl,e,r,!0)}async mutate(e,r={}){return this.query(e,r)}async publicQuery(e,r={}){return await this.init(),await U(this.graphqlUrl,this.tokenUrl,e,r,!1)}};var h=class extends Error{constructor(e){super(e),this.name="QuicksliceError"}},S=class extends h{constructor(e="Login required"){super(e),this.name="LoginRequiredError"}},T=class extends h{constructor(e){super(e),this.name="NetworkError"}},_=class extends h{constructor(e,r){super(`OAuth error: ${e}${r?` - ${r}`:""}`),this.name="OAuthError",this.code=e,this.description=r}};async function ie(t){let e=new m(t);return await e.init(),e}return X(se);})(); 1 + "use strict";var QuicksliceClient=(()=>{var x=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var H=(t,e)=>{for(var r in e)x(t,r,{get:e[r],enumerable:!0})},W=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of M(e))!F.call(t,a)&&a!==r&&x(t,a,{get:()=>e[a],enumerable:!(n=Y(e,a))||n.enumerable});return t};var X=t=>W(x({},"__esModule",{value:!0}),t);var se={};H(se,{LoginRequiredError:()=>S,NetworkError:()=>T,OAuthError:()=>_,QuicksliceClient:()=>m,QuicksliceError:()=>h,createQuicksliceClient:()=>ie});var o={accessToken:"quickslice_access_token",refreshToken:"quickslice_refresh_token",tokenExpiresAt:"quickslice_token_expires_at",clientId:"quickslice_client_id",userDid:"quickslice_user_did",codeVerifier:"quickslice_code_verifier",oauthState:"quickslice_oauth_state",redirectUri:"quickslice_redirect_uri"};var s={get(t){return t===o.codeVerifier||t===o.oauthState?sessionStorage.getItem(t):localStorage.getItem(t)},set(t,e){t===o.codeVerifier||t===o.oauthState?sessionStorage.setItem(t,e):localStorage.setItem(t,e)},remove(t){sessionStorage.removeItem(t),localStorage.removeItem(t)},clear(){Object.values(o).forEach(t=>{sessionStorage.removeItem(t),localStorage.removeItem(t)})}};function d(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),r="";for(let n=0;n<e.length;n++)r+=String.fromCharCode(e[n]);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function k(t){let e=new Uint8Array(t);return crypto.getRandomValues(e),d(e)}async function A(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return d(r)}async function K(t,e,r){let n=new TextEncoder,a=d(n.encode(JSON.stringify(t))),i=d(n.encode(JSON.stringify(e))),c=`${a}.${i}`,l=await crypto.subtle.sign({name:"ECDSA",hash:"SHA-256"},r,n.encode(c)),u=d(l);return`${c}.${u}`}var Z="quickslice-oauth",ee=1,g="dpop-keys",E="dpop-key",y=null;function b(){return y||(y=new Promise((t,e)=>{let r=indexedDB.open(Z,ee);r.onerror=()=>e(r.error),r.onsuccess=()=>t(r.result),r.onupgradeneeded=n=>{let a=n.target.result;a.objectStoreNames.contains(g)||a.createObjectStore(g,{keyPath:"id"})}}),y)}async function te(){let t=await b();return new Promise((e,r)=>{let i=t.transaction(g,"readonly").objectStore(g).get(E);i.onerror=()=>r(i.error),i.onsuccess=()=>e(i.result||null)})}async function re(t,e){let r=await b();return new Promise((n,a)=>{let l=r.transaction(g,"readwrite").objectStore(g).put({id:E,privateKey:t,publicJwk:e,createdAt:Date.now()});l.onerror=()=>a(l.error),l.onsuccess=()=>n()})}async function D(){let t=await te();if(t)return t;let e=await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"]),r=await crypto.subtle.exportKey("jwk",e.publicKey);return await re(e.privateKey,r),{id:E,privateKey:e.privateKey,publicJwk:r,createdAt:Date.now()}}async function f(t,e,r=null){let n=await D(),{kty:a,crv:i,x:c,y:l}=n.publicJwk,w={alg:"ES256",typ:"dpop+jwt",jwk:{kty:a,crv:i,x:c,y:l}},p={jti:k(16),htm:t,htu:e,iat:Math.floor(Date.now()/1e3)};return r&&(p.ath=await A(r)),await K(w,p,n.privateKey)}async function I(){let t=await b();return new Promise((e,r)=>{let i=t.transaction(g,"readwrite").objectStore(g).clear();i.onerror=()=>r(i.error),i.onsuccess=()=>e()})}function R(){return k(32)}async function C(t){let r=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",r);return d(n)}function q(){return k(16)}var $="quickslice_lock_";function L(t){return new Promise(e=>setTimeout(e,t))}async function V(t,e=5e3){let r=$+t,n=`${Date.now()}_${Math.random()}`,a=Date.now()+e;for(;Date.now()<a;){let i=localStorage.getItem(r);if(i){let[c]=i.split("_");if(Date.now()-parseInt(c)>5e3)localStorage.removeItem(r);else{await L(50);continue}}if(localStorage.setItem(r,n),await L(10),localStorage.getItem(r)===n)return n}return null}function j(t,e){let r=$+t;localStorage.getItem(r)===e&&localStorage.removeItem(r)}var v=6e4;function oe(t){return new Promise(e=>setTimeout(e,t))}async function ne(t){let e=s.get(o.refreshToken),r=s.get(o.clientId);if(!e||!r)throw new Error("No refresh token available");let n=await f("POST",t),a=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:n},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:r})});if(!a.ok){let l=await a.json().catch(()=>({}));throw new Error(`Token refresh failed: ${l.error_description||a.statusText}`)}let i=await a.json();s.set(o.accessToken,i.access_token),i.refresh_token&&s.set(o.refreshToken,i.refresh_token);let c=Date.now()+i.expires_in*1e3;return s.set(o.tokenExpiresAt,c.toString()),i.access_token}async function P(t){let e=s.get(o.accessToken),r=parseInt(s.get(o.tokenExpiresAt)||"0");if(e&&Date.now()<r-v)return e;let a=`token_refresh_${s.get(o.clientId)}`,i=await V(a);if(!i){await oe(100);let c=s.get(o.accessToken),l=parseInt(s.get(o.tokenExpiresAt)||"0");if(c&&Date.now()<l-v)return c;throw new Error("Failed to refresh token")}try{let c=s.get(o.accessToken),l=parseInt(s.get(o.tokenExpiresAt)||"0");return c&&Date.now()<l-v?c:await ne(t)}finally{j(a,i)}}function B(t){s.set(o.accessToken,t.access_token),t.refresh_token&&s.set(o.refreshToken,t.refresh_token);let e=Date.now()+t.expires_in*1e3;s.set(o.tokenExpiresAt,e.toString()),t.sub&&s.set(o.userDid,t.sub)}function O(){let t=s.get(o.accessToken),e=s.get(o.refreshToken);return!!(t||e)}async function Q(t,e,r={}){let n=R(),a=await C(n),i=q(),c=r.redirectUri||window.location.origin+window.location.pathname;s.set(o.codeVerifier,n),s.set(o.oauthState,i),s.set(o.clientId,e),s.set(o.redirectUri,c);let l=new URLSearchParams({client_id:e,redirect_uri:c,response_type:"code",code_challenge:a,code_challenge_method:"S256",state:i});r.handle&&l.set("login_hint",r.handle),r.scope&&l.set("scope",r.scope),window.location.href=`${t}?${l.toString()}`}async function J(t){let e=new URLSearchParams(window.location.search),r=e.get("code"),n=e.get("state"),a=e.get("error");if(a)throw new Error(`OAuth error: ${a} - ${e.get("error_description")||""}`);if(!r||!n)return!1;let i=s.get(o.oauthState);if(n!==i)throw new Error("OAuth state mismatch - possible CSRF attack");let c=s.get(o.codeVerifier),l=s.get(o.clientId),u=s.get(o.redirectUri);if(!c||!l||!u)throw new Error("Missing OAuth session data");let w=await f("POST",t),p=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:w},body:new URLSearchParams({grant_type:"authorization_code",code:r,redirect_uri:u,client_id:l,code_verifier:c})});if(!p.ok){let z=await p.json().catch(()=>({}));throw new Error(`Token exchange failed: ${z.error_description||p.statusText}`)}let N=await p.json();return B(N),s.remove(o.codeVerifier),s.remove(o.oauthState),s.remove(o.redirectUri),window.history.replaceState({},document.title,window.location.pathname),!0}async function G(t={}){s.clear(),await I(),t.reload!==!1&&window.location.reload()}async function U(t,e,r,n={},a=!1){let i={"Content-Type":"application/json"};if(a){let u=await P(e);if(!u)throw new Error("Not authenticated");let w=await f("POST",t,u);i.Authorization=`DPoP ${u}`,i.DPoP=w}let c=await fetch(t,{method:"POST",headers:i,body:JSON.stringify({query:r,variables:n})});if(!c.ok)throw new Error(`GraphQL request failed: ${c.statusText}`);let l=await c.json();if(l.errors&&l.errors.length>0)throw new Error(`GraphQL error: ${l.errors[0].message}`);return l.data}var m=class{constructor(e){this.initialized=!1;this.server=e.server.replace(/\/$/,""),this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.scope=e.scope,this.graphqlUrl=`${this.server}/graphql`,this.authorizeUrl=`${this.server}/oauth/authorize`,this.tokenUrl=`${this.server}/oauth/token`}async init(){this.initialized||(await D(),this.initialized=!0)}async loginWithRedirect(e={}){await this.init(),await Q(this.authorizeUrl,this.clientId,{...e,redirectUri:e.redirectUri||this.redirectUri,scope:e.scope||this.scope})}async handleRedirectCallback(){return await this.init(),await J(this.tokenUrl)}async logout(e={}){await G(e)}async isAuthenticated(){return O()}getUser(){if(!O())return null;let e=s.get(o.userDid);return e?{did:e}:null}async getAccessToken(){return await this.init(),await P(this.tokenUrl)}async query(e,r={}){return await this.init(),await U(this.graphqlUrl,this.tokenUrl,e,r,!0)}async mutate(e,r={}){return this.query(e,r)}async publicQuery(e,r={}){return await this.init(),await U(this.graphqlUrl,this.tokenUrl,e,r,!1)}};var h=class extends Error{constructor(e){super(e),this.name="QuicksliceError"}},S=class extends h{constructor(e="Login required"){super(e),this.name="LoginRequiredError"}},T=class extends h{constructor(e){super(e),this.name="NetworkError"}},_=class extends h{constructor(e,r){super(`OAuth error: ${e}${r?` - ${r}`:""}`),this.name="OAuthError",this.code=e,this.description=r}};async function ie(t){let e=new m(t);return await e.init(),e}return X(se);})();
+1 -1
quickslice-client-js/package.json
··· 1 1 { 2 2 "name": "quickslice-client-js", 3 - "version": "0.1.1", 3 + "version": "0.2.0", 4 4 "description": "Quickslice client SDK for browser SPAs", 5 5 "type": "module", 6 6 "main": "dist/quickslice-client.js",
+5
quickslice-client-js/src/auth/oauth.ts
··· 7 7 export interface LoginOptions { 8 8 handle?: string; 9 9 redirectUri?: string; 10 + scope?: string; 10 11 } 11 12 12 13 /** ··· 42 43 43 44 if (options.handle) { 44 45 params.set('login_hint', options.handle); 46 + } 47 + 48 + if (options.scope) { 49 + params.set('scope', options.scope); 45 50 } 46 51 47 52 window.location.href = `${authorizeUrl}?${params.toString()}`;
+4
quickslice-client-js/src/client.ts
··· 9 9 server: string; 10 10 clientId: string; 11 11 redirectUri?: string; 12 + scope?: string; 12 13 } 13 14 14 15 export interface User { ··· 19 20 private server: string; 20 21 private clientId: string; 21 22 private redirectUri?: string; 23 + private scope?: string; 22 24 private graphqlUrl: string; 23 25 private authorizeUrl: string; 24 26 private tokenUrl: string; ··· 28 30 this.server = options.server.replace(/\/$/, ''); // Remove trailing slash 29 31 this.clientId = options.clientId; 30 32 this.redirectUri = options.redirectUri; 33 + this.scope = options.scope; 31 34 32 35 this.graphqlUrl = `${this.server}/graphql`; 33 36 this.authorizeUrl = `${this.server}/oauth/authorize`; ··· 54 57 await initiateLogin(this.authorizeUrl, this.clientId, { 55 58 ...options, 56 59 redirectUri: options.redirectUri || this.redirectUri, 60 + scope: options.scope || this.scope, 57 61 }); 58 62 } 59 63