This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

fixes

+165 -81
.DS_Store

This is a binary file and will not be displayed.

+27 -4
app/src/app/auth/callback/page.tsx
··· 58 58 const storedState = sessionStorage.getItem('oauth_state'); 59 59 const codeVerifier = sessionStorage.getItem('code_verifier'); 60 60 const serializedKeyPair = sessionStorage.getItem('key_pair'); 61 + 62 + console.log('Callback received state:', state?.substring(0, 5) + '...'); 63 + console.log('Stored state:', storedState?.substring(0, 5) + '...'); 64 + 65 + // Check if we have the stored values 66 + if (!storedState) { 67 + console.error('No stored OAuth state found. Session storage may have been cleared.'); 68 + setError('Session data lost. Please try logging in again.'); 69 + return; 70 + } 61 71 62 72 // Validate state 63 73 if (state !== storedState) { 64 - setError('Invalid state parameter'); 74 + console.error('State mismatch. Received:', state, 'Stored:', storedState); 75 + setError('Invalid state parameter. This may be due to an expired session or a security issue.'); 65 76 return; 66 77 } 67 78 ··· 163 174 const userDid = tokenResponse.sub; 164 175 console.log('User DID from token:', userDid); 165 176 166 - // Try to decode the access token to see if we can extract the audience 177 + // Extract PDS endpoint from the token 178 + let extractedPdsEndpoint = null; 179 + 180 + // Try to decode the access token to extract the audience 167 181 try { 168 182 if (tokenResponse.access_token) { 169 183 const parts = tokenResponse.access_token.split('.'); ··· 179 193 // Update the pdsEndpoint from the decoded token if available 180 194 if (payload.aud.startsWith('did:web:')) { 181 195 // Convert did:web:example.com to https://example.com 182 - pdsEndpoint = 'https://' + payload.aud.replace('did:web:', ''); 183 - console.log('Updated PDS endpoint from decoded token:', pdsEndpoint); 196 + extractedPdsEndpoint = 'https://' + payload.aud.replace('did:web:', ''); 197 + console.log('Extracted PDS endpoint from decoded token:', extractedPdsEndpoint); 198 + 199 + // Update our variable for use in the rest of the function 200 + pdsEndpoint = extractedPdsEndpoint; 184 201 } 185 202 } 186 203 } ··· 189 206 console.warn('Failed to decode token:', error); 190 207 } 191 208 209 + // If we couldn't extract the PDS endpoint, show an error 210 + if (!pdsEndpoint) { 211 + console.error('Failed to extract PDS endpoint from token. This will cause API calls to fail.'); 212 + } 213 + 192 214 // Try to resolve the user's own handle using the DID 193 215 let userHandle = profileResponse?.handle || 'unknown'; 194 216 ··· 196 218 console.log('Using PDS endpoint for API requests:', pdsEndpoint); 197 219 198 220 // Store auth data 221 + console.log('Saving PDS endpoint to auth context:', pdsEndpoint); 199 222 setAuth({ 200 223 accessToken: tokenResponse.access_token, 201 224 refreshToken: tokenResponse.refresh_token,
+32 -8
app/src/app/auth/login/page.tsx
··· 36 36 const { url, state, codeVerifier, keyPair } = await getAuthorizationUrl(); 37 37 38 38 // Store auth state in sessionStorage 39 - sessionStorage.setItem('oauth_state', state); 40 - sessionStorage.setItem('code_verifier', codeVerifier); 41 - 42 - // Serialize and store keyPair 43 - const publicJwk = await window.crypto.subtle.exportKey('jwk', keyPair.publicKey); 44 - const privateJwk = await window.crypto.subtle.exportKey('jwk', keyPair.privateKey); 45 - const serializedKeyPair = JSON.stringify({ publicKey: publicJwk, privateKey: privateJwk }); 46 - sessionStorage.setItem('key_pair', serializedKeyPair); 39 + try { 40 + // Clear any old values first 41 + sessionStorage.removeItem('oauth_state'); 42 + sessionStorage.removeItem('code_verifier'); 43 + sessionStorage.removeItem('key_pair'); 44 + 45 + // Set new values 46 + sessionStorage.setItem('oauth_state', state); 47 + sessionStorage.setItem('code_verifier', codeVerifier); 48 + 49 + // Serialize and store keyPair 50 + const publicJwk = await window.crypto.subtle.exportKey('jwk', keyPair.publicKey); 51 + const privateJwk = await window.crypto.subtle.exportKey('jwk', keyPair.privateKey); 52 + const serializedKeyPair = JSON.stringify({ publicKey: publicJwk, privateKey: privateJwk }); 53 + sessionStorage.setItem('key_pair', serializedKeyPair); 54 + 55 + // Double-check that values were stored correctly 56 + const storedState = sessionStorage.getItem('oauth_state'); 57 + const storedVerifier = sessionStorage.getItem('code_verifier'); 58 + const storedKeyPair = sessionStorage.getItem('key_pair'); 59 + 60 + if (!storedState || !storedVerifier || !storedKeyPair) { 61 + throw new Error('Failed to store authentication data'); 62 + } 63 + 64 + console.log('OAuth state stored successfully:', state.substring(0, 5) + '...'); 65 + } catch (storageError) { 66 + console.error('Error storing OAuth state:', storageError); 67 + setError('Failed to store login state. Please ensure cookies and storage are enabled.'); 68 + setIsLoading(false); 69 + return; 70 + } 47 71 48 72 // Redirect to Bluesky login 49 73 window.location.href = url;
+1
app/src/app/dashboard/page.tsx
··· 84 84 const isAuthValid = await checkAuth( 85 85 accessToken, 86 86 keyPair, 87 + did, 87 88 dpopNonce || null, 88 89 pdsEndpoint 89 90 );
+28 -6
app/src/lib/bluesky-api.ts
··· 18 18 export async function checkAuth( 19 19 accessToken: string, 20 20 keyPair: CryptoKeyPair, 21 + did: string, // Add DID parameter 21 22 dpopNonce: string | null = null, 22 23 pdsEndpoint: string | null = null 23 24 ): Promise<boolean> { ··· 27 28 return false; 28 29 } 29 30 31 + if (!did) { 32 + console.error('No DID provided for auth check'); 33 + return false; 34 + } 35 + 30 36 console.log('Checking auth with PDS endpoint:', pdsEndpoint); 31 37 32 38 // Use the PDS endpoint for auth check 33 39 const baseUrl = `${pdsEndpoint}/xrpc`; 34 - const endpoint = `${baseUrl}/com.atproto.repo.listRecords`; 40 + // We'll use the identity.resolveHandle endpoint which is simpler for auth checking 41 + const endpoint = `${baseUrl}/com.atproto.identity.resolveHandle`; 42 + 43 + // The handle param is required, but we're really just checking auth 44 + // Use handle=atproto.com as a safe default 45 + const url = `${endpoint}?handle=atproto.com`; 35 46 36 - // Generate DPoP token 47 + // Generate DPoP token with the full URL including query params 37 48 const publicKey = await exportJWK(keyPair.publicKey); 38 49 const dpopToken = await generateDPoPToken( 39 50 keyPair.privateKey, 40 51 publicKey, 41 52 'GET', 42 - `${endpoint}?limit=1`, 53 + url, 43 54 dpopNonce || undefined 44 55 ); 45 56 57 + console.log('Making auth check request to:', url); 58 + 46 59 // Make the request to check auth 47 - const response = await fetch(`${endpoint}?limit=1`, { 60 + const response = await fetch(url, { 48 61 method: 'GET', 49 62 headers: { 50 63 'Authorization': `DPoP ${accessToken}`, ··· 57 70 return true; 58 71 } 59 72 73 + // Log detailed error information 74 + try { 75 + console.error('Auth check response:', response.status, response.statusText); 76 + const errorData = await response.text(); 77 + console.error('Auth check error data:', errorData); 78 + } catch (parseError) { 79 + console.error('Could not parse error response:', parseError); 80 + } 81 + 60 82 if (response.status === 401) { 61 83 const nonce = response.headers.get('DPoP-Nonce'); 62 84 if (nonce) { 63 85 console.log('Got nonce during auth check:', nonce); 64 - // Try again with the nonce 65 - return checkAuth(accessToken, keyPair, nonce, pdsEndpoint); 86 + // Try again with the nonce, but prevent infinite recursion 87 + return checkAuth(accessToken, keyPair, did, nonce, pdsEndpoint); 66 88 } 67 89 } 68 90
contextual info for claude/.DS_Store

This is a binary file and will not be displayed.

+27 -63
contextual info for claude/app_errors.md
··· 1 1 Storage access automatically granted for Dynamic State Partitioning “https://bsky.social” on “https://flushing.im”. callback 2 2 GET 3 3 https://flushing.im/favicon.ico 4 - [HTTP/2 404 72ms] 4 + [HTTP/2 404 58ms] 5 5 6 - Exchanging code for token... page-976d4b27123490e1.js:1:1598 7 - No nonce provided, getting one from API... 481-7f188d0a0612edc0.js:1:6509 8 - Obtained nonce from API: BfLNboXnaX-9cCRDyFnwxGI7xm_9OF1syNTUUMM3A7c 481-7f188d0a0612edc0.js:1:6586 9 - Creating DPoP token with nonce: BfLNboXnaX-9cCRDyFnwxGI7xm_9OF1syNTUUMM3A7c 481-7f188d0a0612edc0.js:1:6698 10 - Making token request via proxy API 481-7f188d0a0612edc0.js:1:6837 11 - Token request successful 481-7f188d0a0612edc0.js:1:7330 12 - Token audience: undefined page-976d4b27123490e1.js:1:1950 13 - Token audience missing or not a string: undefined page-976d4b27123490e1.js:1:2208 14 - Getting profile via proxy API 481-7f188d0a0612edc0.js:1:2377 6 + Exchanging code for token... page-ae3301580eb0dbbc.js:1:1598 7 + No nonce provided, getting one from API... 481-5b7d3c6f8bc564a2.js:1:8088 8 + Obtained nonce from API: Dq3xrB2CbvGOdqtF5avAauCvScf1FYV_QDmpQ8wZqG4 481-5b7d3c6f8bc564a2.js:1:8165 9 + Creating DPoP token with nonce: Dq3xrB2CbvGOdqtF5avAauCvScf1FYV_QDmpQ8wZqG4 481-5b7d3c6f8bc564a2.js:1:8277 10 + Making token request via proxy API 481-5b7d3c6f8bc564a2.js:1:8416 11 + Token request successful 481-5b7d3c6f8bc564a2.js:1:8909 12 + Token audience: undefined page-ae3301580eb0dbbc.js:1:1950 13 + Token audience missing or not a string: undefined page-ae3301580eb0dbbc.js:1:2208 14 + Getting profile via proxy API 481-5b7d3c6f8bc564a2.js:1:3263 15 15 Token response: 16 - Object { access_token: "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFUzI1NksifQ.eyJhdWQiOiJkaWQ6d2ViOmVub2tpLnVzLWVhc3QuaG9zdC5ic2t5Lm5ldHdvcmsiLCJpYXQiOjE3NDEzODg5MTUsImV4cCI6MTc0MTM5MjUxNSwic3ViIjoiZGlkOnBsYzpncTRmbzN1NnRxenpka2psd3pwYjIzdGoiLCJqdGkiOiJ0b2stZjcwMzk1Yzk0MzFkYWU2NDBhZTk4OGRkNGRjNWY0YTkiLCJjbmYiOnsiamt0IjoiUV9iMXlsWEZuRGU0TlF1SVBFNm4xOU5qWkhMaFZwOGlZMjZpblJrT24tVSJ9LCJjbGllbnRfaWQiOiJodHRwczovL2ZsdXNoaW5nLmltL2NsaWVudC1tZXRhZGF0YS5qc29uIiwic2NvcGUiOiJhdHByb3RvIHRyYW5zaXRpb246Z2VuZXJpYyIsImlzcyI6Imh0dHBzOi8vYnNreS5zb2NpYWwifQ.3cGPid5H_yCIKzVxFKpCpwXxYS0dK4ZyP16lXflPekSqA8uRpMiLTnUh9XnmXjpW7xmzauzMmyUgR03wbk5l9w", token_type: "DPoP", refresh_token: "ref-8a27b644c89f22ad6056da6b33670266c745f35136c125a8117c392f82945d53", scope: "atproto transition:generic", expires_in: 3599, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj" } 17 - page-976d4b27123490e1.js:1:2465 18 - User DID from token: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-976d4b27123490e1.js:1:2510 16 + Object { access_token: "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFUzI1NksifQ.eyJhdWQiOiJkaWQ6d2ViOmVub2tpLnVzLWVhc3QuaG9zdC5ic2t5Lm5ldHdvcmsiLCJpYXQiOjE3NDEzODk4NjEsImV4cCI6MTc0MTM5MzQ2MSwic3ViIjoiZGlkOnBsYzpncTRmbzN1NnRxenpka2psd3pwYjIzdGoiLCJqdGkiOiJ0b2stYTRkNzdkNmZkZmExMmUwODkzNWJjNTQwYTJjMDBkNTEiLCJjbmYiOnsiamt0IjoieVlqUjRmb0JVSXY0RzZCX21Kb29abmR5ZFBHcmtwZEpBeXVpTDk0RGl6VSJ9LCJjbGllbnRfaWQiOiJodHRwczovL2ZsdXNoaW5nLmltL2NsaWVudC1tZXRhZGF0YS5qc29uIiwic2NvcGUiOiJhdHByb3RvIHRyYW5zaXRpb246Z2VuZXJpYyIsImlzcyI6Imh0dHBzOi8vYnNreS5zb2NpYWwifQ.gtS_fu7hSVR6DLgC-DD4yKGianrn6mBhSAwN14_TytGm2QMwbHXbGYDzV3c0cOCZ_Kw5SmJLUUZDp-y4gWwLXA", token_type: "DPoP", refresh_token: "ref-315fa3def8f4c3a0faa828188205642b078d126c8a22f5a4b68d262e6aceba1f", scope: "atproto transition:generic", expires_in: 3599, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj" } 17 + page-ae3301580eb0dbbc.js:1:2465 18 + User DID from token: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-ae3301580eb0dbbc.js:1:2510 19 19 Decoded token payload: 20 - Object { aud: "did:web:enoki.us-east.host.bsky.network", iat: 1741388915, exp: 1741392515, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj", jti: "tok-f70395c9431dae640ae988dd4dc5f4a9", cnf: {…}, client_id: "https://flushing.im/client-metadata.json", scope: "atproto transition:generic", iss: "https://bsky.social" } 21 - page-976d4b27123490e1.js:1:2649 22 - Audience from decoded token: did:web:enoki.us-east.host.bsky.network page-976d4b27123490e1.js:1:2721 23 - Updated PDS endpoint from decoded token: https://enoki.us-east.host.bsky.network page-976d4b27123490e1.js:1:2844 24 - Using PDS endpoint for API requests: https://enoki.us-east.host.bsky.network page-976d4b27123490e1.js:1:3000 25 - Submitting status update with DID: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-1700ee190f0631bf.js:1:1129 26 - Creating flushing status with DPoP nonce: null page-1700ee190f0631bf.js:1:1412 27 - User PDS endpoint: https://enoki.us-east.host.bsky.network page-1700ee190f0631bf.js:1:1471 28 - Creating flushing status with nonce: null 481-7f188d0a0612edc0.js:1:3187 29 - Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-7f188d0a0612edc0.js:1:3241 30 - Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-7f188d0a0612edc0.js:1:3423 31 - Using PDS endpoint for DPoP token generation 481-7f188d0a0612edc0.js:1:3469 32 - XHRPOST 33 - https://flushing.im/api/bluesky/flushing 34 - [HTTP/2 401 362ms] 35 - 36 - Status creation error: 37 - Object { error: "use_dpop_nonce", nonce: "sP6OuVAHR5DNt9ZvpNqeWSdUOf4vA7cZ_9QEKEpa3O4", originalError: {…} } 38 - 117-9ca478b5e944a826.js:1:4081 39 - Error creating flushing status: Error: Status creation failed: 401 40 - NextJS 31 41 - 117-9ca478b5e944a826.js:1:4081 42 - Failed to update status: Error: Status creation failed: 401 43 - NextJS 31 44 - 117-9ca478b5e944a826.js:1:4081 20 + Object { aud: "did:web:enoki.us-east.host.bsky.network", iat: 1741389861, exp: 1741393461, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj", jti: "tok-a4d77d6fdfa12e08935bc540a2c00d51", cnf: {…}, client_id: "https://flushing.im/client-metadata.json", scope: "atproto transition:generic", iss: "https://bsky.social" } 21 + page-ae3301580eb0dbbc.js:1:2649 22 + Audience from decoded token: did:web:enoki.us-east.host.bsky.network page-ae3301580eb0dbbc.js:1:2721 23 + Updated PDS endpoint from decoded token: https://enoki.us-east.host.bsky.network page-ae3301580eb0dbbc.js:1:2844 24 + Using PDS endpoint for API requests: https://enoki.us-east.host.bsky.network page-ae3301580eb0dbbc.js:1:3000 25 + Submitting status update with DID: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-d2f66577bcd815c7.js:1:1200 26 + Using PDS endpoint: https://enoki.us-east.host.bsky.network page-d2f66577bcd815c7.js:1:1252 27 + Checking auth with PDS endpoint: https://enoki.us-east.host.bsky.network 481-5b7d3c6f8bc564a2.js:1:2403 28 + XHRGET 29 + https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.listRecords?limit=1 30 + [HTTP/2 400 29ms] 45 31 32 + Auth check failed with status: 400 117-6f305e70e5d65397.js:1:4081 33 + NextJS 32 46 34 47 - Now, here is what I see in the network tab: 48 35 49 - GET https://bsky.social/.well-known/oauth-authorization-server 50 - {"issuer":"https://bsky.social","request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"scopes_supported":["atproto","transition:generic","transition:chat.bsky"],"subject_types_supported":["public"],"response_types_supported":["code"],"response_modes_supported":["query","fragment","form_post"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"ui_locales_supported":["en-US"],"display_values_supported":["page","popup","touch"],"request_object_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512","none"],"authorization_response_iss_parameter_supported":true,"request_object_encryption_alg_values_supported":[],"request_object_encryption_enc_values_supported":[],"jwks_uri":"https://bsky.social/oauth/jwks","authorization_endpoint":"https://bsky.social/oauth/authorize","token_endpoint":"https://bsky.social/oauth/token","token_endpoint_auth_methods_supported":["none","private_key_jwt"],"token_endpoint_auth_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512"],"revocation_endpoint":"https://bsky.social/oauth/revoke","introspection_endpoint":"https://bsky.social/oauth/introspect","pushed_authorization_request_endpoint":"https://bsky.social/oauth/par","require_pushed_authorization_requests":true,"dpop_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512"],"client_id_metadata_document_supported":true} 51 - 52 - HTTP/2 200 53 - 54 - OPTIONS https://bsky.social/.well-known/oauth-authorization-server 55 - 56 - GET https://bsky.social/oauth/authorize?client_id=https%3A%2F%2Fflushing.im%2Fclient-metadata.json&response_type=code&redirect_uri=https%3A%2F%2Fflushing.im%2Fauth%2Fcallback&scope=atproto%20transition%3Ageneric&state=Plb-nCCv9X5xXjTqxGxmP3nAWIS8knqN&code_challenge=PwBcKwsexA8CReQuf6-BVwE70V7f4CkfyM4MhFCTQl0&code_challenge_method=S256 57 - 58 - 303 GET https://bsky.social/oauth/authorize/accept?request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3Areq-2358ef13f161e15e8da7dc386dffa21d&account_sub=did%3Aplc%3Agq4fo3u6tqzzdkjlwzpb23tj&client_id=https%3A%2F%2Fflushing.im%2Fclient-metadata.json&csrf_token=32ae873cb793ec45 59 - 60 - 103 GET https://flushing.im/auth/callback?iss=https%3A%2F%2Fbsky.social&state=Plb-nCCv9X5xXjTqxGxmP3nAWIS8knqN&code=cod-1146b2a18bbd2613f2e252b25a15004d7866a1649c52b853ec456fbfa479a18a 61 - 62 - 200 GET https://flushing.im/api/auth/nonce 63 - 64 - 200 POST https://flushing.im/api/auth/token 65 - 66 - 200 POST https://flushing.im/api/bluesky/profile 67 - 68 - 200 GET https://flushing.im/dashboard?_rsc=1ayzj 69 - 70 - 200 GET https://flushing.im/_next/static/chunks/app/dashboard/page-1700ee190f0631bf.js?dpl=dpl_HqagqRoXy9zV9KaxVMTzrzqDHy9y 71 - 72 - 401 POST https://flushing.im/api/bluesky/flushing 36 + Authentication check failed. Your login may have expired.
+50
contextual info for claude/com.atproto.repo.listRecords Bluesky.md
··· 1 + --- 2 + title: "com.atproto.repo.listRecords | Bluesky" 3 + source: "https://docs.bsky.app/docs/api/com-atproto-repo-list-records" 4 + author: 5 + published: 6 + created: 2025-03-07 7 + description: "*This endpoint is part of the atproto PDS repository management APIs. Requests usually require authentication (unlike the `com.atproto.sync.*` endpoints), and are made directly to the user's own PDS instance.*" 8 + tags: 9 + - "clippings" 10 + --- 11 + ``` 12 + GET /xrpc/com.atproto.repo.listRecords 13 + ``` 14 + 15 + *This endpoint is part of the atproto PDS repository management APIs. Requests usually require authentication (unlike the `com.atproto.sync.*` endpoints), and are made directly to the user's own PDS instance.* 16 + 17 + *To learn more about calling atproto API endpoints like this one, see the [API Hosts and Auth](https://docs.bsky.app/docs/advanced-guides/api-directory) guide.* 18 + 19 + List a range of records in a repository, matching a specific collection. Does not require auth. 20 + 21 + ## Request​ 22 + 23 + ### Query Parameters 24 + 25 + **repo** at-identifierrequired 26 + 27 + The handle or DID of the repo. 28 + 29 + **collection** nsidrequired 30 + 31 + The NSID of the record type. 32 + 33 + **limit** integer 34 + 35 + **Possible values:** `>= 1` and `<= 100` 36 + 37 + The number of records to return. 38 + 39 + **Default value:** `50` 40 + 41 + **cursor** string 42 + 43 + **reverse** boolean 44 + 45 + Flag to reverse the order of the returned records. 46 + 47 + - application/json 48 + 49 + - Schema 50 + - Example (auto)