···1313 );
1414 }
15151616- // Check if handle is provided, use a default otherwise
1717- const userHandle = handle || 'atproto.com';
1818-1916 // Use the PDS endpoint if provided, otherwise use the default
2017 const apiUrl = pdsEndpoint ? `${pdsEndpoint}/xrpc` : DEFAULT_API_URL;
21182222- // Make the request to the user's PDS
2323- const url = `${apiUrl}/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(userHandle)}`;
1919+ // We can resolve either a handle or a DID
2020+ let url;
2121+ if (handle && handle.startsWith('did:')) {
2222+ // If it's a DID, use describeRepo to get details
2323+ url = `${apiUrl}/com.atproto.repo.describeRepo?repo=${encodeURIComponent(handle)}`;
2424+ console.log('Looking up account info by DID:', handle);
2525+ } else {
2626+ // Otherwise treat it as a handle to resolve
2727+ const userHandle = handle || 'atproto.com';
2828+ url = `${apiUrl}/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(userHandle)}`;
2929+ console.log('Looking up account info by handle:', userHandle);
3030+ }
2431 console.log('Making request to:', url);
25322633 const response = await fetch(url, {
+36-7
app/src/app/auth/callback/page.tsx
···171171 });
172172173173 // Extract the DID from the token response
174174- console.log('Token response:', tokenResponse);
175174 const userDid = tokenResponse.sub;
176175 console.log('User DID from token:', userDid);
177176···185184 if (parts.length === 3) {
186185 // Decode the payload (second part)
187186 const payload = JSON.parse(atob(parts[1]));
188188- console.log('Decoded token payload:', payload);
189187190188 // Extract audience from the decoded token
191189 if (payload.aud && typeof payload.aud === 'string') {
192192- console.log('Audience from decoded token:', payload.aud);
193193-194190 // Update the pdsEndpoint from the decoded token if available
195191 if (payload.aud.startsWith('did:web:')) {
196192 // Convert did:web:example.com to https://example.com
···212208 console.error('Failed to extract PDS endpoint from token. This will cause API calls to fail.');
213209 }
214210215215- // Try to resolve the user's own handle using the DID
216216- let userHandle = profileResponse?.handle || 'unknown';
211211+ // Now that we have the PDS endpoint, try to get the user's handle directly
212212+ // First try to use the profileResponse we already have
213213+ let userHandle = profileResponse?.handle;
214214+215215+ // If we don't have a handle yet, try to resolve it using the user's DID
216216+ if (!userHandle || userHandle === 'unknown' || userHandle === 'unknown_user') {
217217+ try {
218218+ console.log('Getting user handle from DID...');
219219+ // Try to make a direct call to resolve the handle from the DID
220220+ const handleResponse = await getProfile(
221221+ tokenResponse.access_token,
222222+ keyPair,
223223+ dpopNonce,
224224+ userDid, // Use the user's DID instead of default
225225+ pdsEndpoint
226226+ );
227227+228228+ if (handleResponse && handleResponse.handle) {
229229+ userHandle = handleResponse.handle;
230230+ console.log('Successfully resolved user handle:', userHandle);
231231+ } else {
232232+ userHandle = 'unknown';
233233+ }
234234+ } catch (error) {
235235+ console.error('Failed to resolve user handle:', error);
236236+ userHandle = 'unknown';
237237+ }
238238+ }
217239218240 // Log the PDS endpoint that will be used
219241 console.log('Using PDS endpoint for API requests:', pdsEndpoint);
···230252 pdsEndpoint: pdsEndpoint // Store the PDS endpoint for later use
231253 });
232254233233- // Clear storage
255255+ // Clear all auth-related storage items
234256 clearAuthData('oauth_state');
235257 clearAuthData('code_verifier');
236258 clearAuthData('key_pair');
259259+260260+ // Also try to clear any leftover sessionStorage items
261261+ try {
262262+ sessionStorage.clear();
263263+ } catch (e) {
264264+ console.warn('Failed to clear session storage:', e);
265265+ }
237266238267 // Redirect to dashboard
239268 router.push('/dashboard');
+13-4
app/src/lib/bluesky-api.ts
···231231 const publicKey = await exportJWK(keyPair.publicKey);
232232 // Use the PDS endpoint if available
233233 const baseUrl = pdsEndpoint ? `${pdsEndpoint}/xrpc` : 'https://bsky.social/xrpc';
234234- // Include the handle parameter in the URL for token creation
235235- const endpoint = `${baseUrl}/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`;
234234+235235+ // Determine the endpoint based on whether we're looking up a DID or handle
236236+ let endpoint;
237237+ if (handle && handle.startsWith('did:')) {
238238+ // If it's a DID, use the describeRepo endpoint
239239+ endpoint = `${baseUrl}/com.atproto.repo.describeRepo?repo=${encodeURIComponent(handle)}`;
240240+ console.log('Looking up profile by DID:', handle);
241241+ } else {
242242+ // Otherwise use resolveHandle for handles
243243+ endpoint = `${baseUrl}/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`;
244244+ console.log('Looking up profile by handle:', handle);
245245+ }
236246237247 // Generate the DPoP token with the access token for the ath claim
238248 const dpopToken = await generateDPoPToken(
···315325 // Generate a DPoP token for the create request
316326 const publicKey = await exportJWK(keyPair.publicKey);
317327318318- console.log('Generating DPoP token for:', endpoint, 'with nonce:', dpopNonce || 'none');
319319- console.log('Including access token hash (ath) in DPoP token');
328328+ // Generate token with appropriate claims for the request
320329321330 const dpopToken = await generateDPoPToken(
322331 keyPair.privateKey,
+1-1
app/src/lib/bluesky-auth.ts
···105105106106 // Add access token hash (ath) if access token is provided
107107 if (accessToken) {
108108- console.log('Adding ath claim to DPoP token');
108108+ // Adding ath claim is required when using access tokens with DPoP
109109 const tokenHash = await sha256(accessToken);
110110 payload.ath = arrayBufferToBase64Url(tokenHash);
111111 }
+52-65
contextual info for claude/app_errors.md
···11Storage access automatically granted for Dynamic State Partitioning “https://bsky.social” on “https://flushing.im”. callback
22GET
33https://flushing.im/favicon.ico
44-[HTTP/2 404 61ms]
44+[HTTP/2 404 0ms]
5566-Callback received state: 9StyI... page-099e80a200860a21.js:1:1095
77-Stored state: 9StyI... page-099e80a200860a21.js:1:1175
88-Exchanging code for token... page-099e80a200860a21.js:1:2016
99-No nonce provided, getting one from API... 481-d3c58c35fd99af50.js:1:8385
1010-Obtained nonce from API: nnKOYi3GWF5VHcaGl3ZfUAQeplTiEeWEi6_FH2BGrLI 481-d3c58c35fd99af50.js:1:8462
1111-Creating DPoP token with nonce: nnKOYi3GWF5VHcaGl3ZfUAQeplTiEeWEi6_FH2BGrLI 481-d3c58c35fd99af50.js:1:8574
1212-Making token request via proxy API 481-d3c58c35fd99af50.js:1:8713
1313-Token request successful 481-d3c58c35fd99af50.js:1:9206
1414-Token audience: undefined page-099e80a200860a21.js:1:2368
1515-Token audience missing or not a string: undefined page-099e80a200860a21.js:1:2626
1616-Getting profile via proxy API 481-d3c58c35fd99af50.js:1:3560
66+Callback received state: sKbxR... page-a2b4c23153d80106.js:1:993
77+Stored state: sKbxR... page-a2b4c23153d80106.js:1:1073
88+Exchanging code for token... page-a2b4c23153d80106.js:1:1914
99+No nonce provided, getting one from API... 481-38cee1d3cffbeb60.js:1:8815
1010+Obtained nonce from API: jEDudu85YaVxa83p3YzTLY4VYmRv2O6VpyOwSkZtXu8 481-38cee1d3cffbeb60.js:1:8892
1111+Creating DPoP token with nonce: jEDudu85YaVxa83p3YzTLY4VYmRv2O6VpyOwSkZtXu8 481-38cee1d3cffbeb60.js:1:9004
1212+Making token request via proxy API 481-38cee1d3cffbeb60.js:1:9143
1313+Token request successful 481-38cee1d3cffbeb60.js:1:9636
1414+Token audience: undefined page-a2b4c23153d80106.js:1:2266
1515+Token audience missing or not a string: undefined page-a2b4c23153d80106.js:1:2524
1616+Getting profile via proxy API 481-38cee1d3cffbeb60.js:1:3562
1717+Adding ath claim to DPoP token 481-38cee1d3cffbeb60.js:1:6994
1718Token response:
1818-Object { access_token: "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFUzI1NksifQ.eyJhdWQiOiJkaWQ6d2ViOmVub2tpLnVzLWVhc3QuaG9zdC5ic2t5Lm5ldHdvcmsiLCJpYXQiOjE3NDEzOTAzMzcsImV4cCI6MTc0MTM5MzkzNywic3ViIjoiZGlkOnBsYzpncTRmbzN1NnRxenpka2psd3pwYjIzdGoiLCJqdGkiOiJ0b2stODAwZjEwZTEzOTEyYjA0MTljOGYyZTc4ZDkzZWRkZjIiLCJjbmYiOnsiamt0IjoiR3lXWnA3cUZOajFiaWttMHhRX1pMTGR4WnhMVTdCNmRqaEZSTU1HR1o0TSJ9LCJjbGllbnRfaWQiOiJodHRwczovL2ZsdXNoaW5nLmltL2NsaWVudC1tZXRhZGF0YS5qc29uIiwic2NvcGUiOiJhdHByb3RvIHRyYW5zaXRpb246Z2VuZXJpYyIsImlzcyI6Imh0dHBzOi8vYnNreS5zb2NpYWwifQ.fIVsf2SJRsaPDCRpaJhYyFsXOpGcvxxutZhqHfLwS97XtMxh63yPZZq26vhC2aHJ_7omR1a6MJjEptDZfvTrVQ", token_type: "DPoP", refresh_token: "ref-2dda4b2d21fbb21df5b02d34f3dafd35ccef542b4a4fce652b3ce1958c7571cd", scope: "atproto transition:generic", expires_in: 3599, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj" }
1919-page-099e80a200860a21.js:1:2883
2020-User DID from token: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-099e80a200860a21.js:1:2928
1919+Object { access_token: "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFUzI1NksifQ.eyJhdWQiOiJkaWQ6d2ViOmVub2tpLnVzLWVhc3QuaG9zdC5ic2t5Lm5ldHdvcmsiLCJpYXQiOjE3NDEzOTEwMzUsImV4cCI6MTc0MTM5NDYzNSwic3ViIjoiZGlkOnBsYzpncTRmbzN1NnRxenpka2psd3pwYjIzdGoiLCJqdGkiOiJ0b2stMDU0ZTAyMjI4MTcxYTNlMDcyYTNhODQ0Y2I4Njk1YTciLCJjbmYiOnsiamt0IjoiNVltV1k3SE5zM3BGdFVEd1VlaGlpbkxEYXdXR1Z3VXU5dUNEZUxmVURhSSJ9LCJjbGllbnRfaWQiOiJodHRwczovL2ZsdXNoaW5nLmltL2NsaWVudC1tZXRhZGF0YS5qc29uIiwic2NvcGUiOiJhdHByb3RvIHRyYW5zaXRpb246Z2VuZXJpYyIsImlzcyI6Imh0dHBzOi8vYnNreS5zb2NpYWwifQ.k6Z977hSzPqhljN1wje06apxn_uBxZajHWiSCulboe4kxCOrn_iHUd7-ANMXo2YwSGz5rQL-t-KeZv-IKyBzlw", token_type: "DPoP", refresh_token: "ref-d556158ae379dd4dae168f84b27a214c2a8e044d506d95c9f4c5ec8c25dca656", scope: "atproto transition:generic", expires_in: 3599, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj" }
2020+page-a2b4c23153d80106.js:1:2781
2121+User DID from token: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-a2b4c23153d80106.js:1:2826
2122Decoded token payload:
2222-Object { aud: "did:web:enoki.us-east.host.bsky.network", iat: 1741390337, exp: 1741393937, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj", jti: "tok-800f10e13912b0419c8f2e78d93eddf2", cnf: {…}, client_id: "https://flushing.im/client-metadata.json", scope: "atproto transition:generic", iss: "https://bsky.social" }
2323-page-099e80a200860a21.js:1:3078
2424-Audience from decoded token: did:web:enoki.us-east.host.bsky.network page-099e80a200860a21.js:1:3150
2525-Extracted PDS endpoint from decoded token: https://enoki.us-east.host.bsky.network page-099e80a200860a21.js:1:3273
2626-Using PDS endpoint for API requests: https://enoki.us-east.host.bsky.network page-099e80a200860a21.js:1:3533
2727-Saving PDS endpoint to auth context: https://enoki.us-east.host.bsky.network page-099e80a200860a21.js:1:3587
2828-Callback received state: 9StyI... page-099e80a200860a21.js:1:1095
2929-Stored state: undefined... page-099e80a200860a21.js:1:1175
3030-No stored OAuth state found. Session storage may have been cleared. 117-89c59c874aec3528.js:1:4081
2323+Object { aud: "did:web:enoki.us-east.host.bsky.network", iat: 1741391035, exp: 1741394635, sub: "did:plc:gq4fo3u6tqzzdkjlwzpb23tj", jti: "tok-054e02228171a3e072a3a844cb8695a7", cnf: {…}, client_id: "https://flushing.im/client-metadata.json", scope: "atproto transition:generic", iss: "https://bsky.social" }
2424+page-a2b4c23153d80106.js:1:2976
2525+Audience from decoded token: did:web:enoki.us-east.host.bsky.network page-a2b4c23153d80106.js:1:3048
2626+Extracted PDS endpoint from decoded token: https://enoki.us-east.host.bsky.network page-a2b4c23153d80106.js:1:3171
2727+Using PDS endpoint for API requests: https://enoki.us-east.host.bsky.network page-a2b4c23153d80106.js:1:3431
2828+Saving PDS endpoint to auth context: https://enoki.us-east.host.bsky.network page-a2b4c23153d80106.js:1:3485
2929+Callback received state: sKbxR... page-a2b4c23153d80106.js:1:993
3030+Stored state: undefined... page-a2b4c23153d80106.js:1:1073
3131+No stored OAuth state found. Browser storage may have been cleared. 117-9a2b9731dac7965d.js:1:4081
3132Submitting status update with DID: did:plc:gq4fo3u6tqzzdkjlwzpb23tj page-1f5ea258c75e5bac.js:1:1200
3233Using PDS endpoint: https://enoki.us-east.host.bsky.network page-1f5ea258c75e5bac.js:1:1252
3333-Checking auth with PDS endpoint: https://enoki.us-east.host.bsky.network 481-d3c58c35fd99af50.js:1:2469
3434-Making auth check request to: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.identity.resolveHandle?handle=atproto.com 481-d3c58c35fd99af50.js:1:2709
3535-Auth check successful! 481-d3c58c35fd99af50.js:1:2856
3434+Checking auth with PDS endpoint: https://enoki.us-east.host.bsky.network 481-38cee1d3cffbeb60.js:1:2469
3535+Adding ath claim to DPoP token 481-38cee1d3cffbeb60.js:1:6994
3636+Making auth check request to: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.identity.resolveHandle?handle=atproto.com 481-38cee1d3cffbeb60.js:1:2711
3737+Auth check successful! 481-38cee1d3cffbeb60.js:1:2858
3638Authentication verified, creating status... page-1f5ea258c75e5bac.js:1:1662
3737-Creating flushing status (attempt 1/3) with nonce: null 481-d3c58c35fd99af50.js:1:4526
3838-Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-d3c58c35fd99af50.js:1:4608
3939-API endpoint: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-d3c58c35fd99af50.js:1:4985
4040-Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord with nonce: none 481-d3c58c35fd99af50.js:1:5050
4141-Sending request to proxy API... 481-d3c58c35fd99af50.js:1:5174
4242-XHRPOST
4343-https://flushing.im/api/bluesky/flushing
4444-[HTTP/2 401 200ms]
4545-4646-Status creation error:
4747-Object { error: "use_dpop_nonce", nonce: "Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA", originalError: {…} }
4848-117-89c59c874aec3528.js:1:4081
4949-Received DPoP nonce error, retrying with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:5620
5050-Creating flushing status (attempt 2/3) with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:4526
5151-Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-d3c58c35fd99af50.js:1:4608
5252-API endpoint: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-d3c58c35fd99af50.js:1:4985
5353-Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:5050
5454-Sending request to proxy API... 481-d3c58c35fd99af50.js:1:5174
5555-XHRPOST
5656-https://flushing.im/api/bluesky/flushing
5757-[HTTP/2 401 63ms]
5858-5959-Status creation error:
6060-Object { error: "use_dpop_nonce", nonce: "Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA", originalError: {…} }
6161-117-89c59c874aec3528.js:1:4081
6262-Received DPoP nonce error, retrying with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:5620
6363-Creating flushing status (attempt 3/3) with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:4526
6464-Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-d3c58c35fd99af50.js:1:4608
6565-API endpoint: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-d3c58c35fd99af50.js:1:4985
6666-Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:5050
6767-Sending request to proxy API... 481-d3c58c35fd99af50.js:1:5174
3939+Creating flushing status (attempt 1/3) with nonce: null 481-38cee1d3cffbeb60.js:1:4530
4040+Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-38cee1d3cffbeb60.js:1:4612
4141+API endpoint: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-38cee1d3cffbeb60.js:1:4989
4242+Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord with nonce: none 481-38cee1d3cffbeb60.js:1:5054
4343+Including access token hash (ath) in DPoP token 481-38cee1d3cffbeb60.js:1:5122
4444+Adding ath claim to DPoP token 481-38cee1d3cffbeb60.js:1:6994
4545+Sending request to proxy API... 481-38cee1d3cffbeb60.js:1:5243
6846XHRPOST
6947https://flushing.im/api/bluesky/flushing
7070-[HTTP/2 401 102ms]
4848+[HTTP/2 401 203ms]
71497250Status creation error:
7373-Object { error: "use_dpop_nonce", nonce: "Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA", originalError: {…} }
7474-117-89c59c874aec3528.js:1:4081
7575-Received DPoP nonce error, retrying with nonce: Cu3Gn9OQuKUyKfqV9bGK0pqqeHPvm-tWhsvc9-6cmkA 481-d3c58c35fd99af50.js:1:5620
7676-Failed to update status: Error: Maximum retry attempts reached. Could not create status after 3 attempts.
7777- NextJS 34
7878-117-89c59c874aec3528.js:1:4081
5151+Object { error: "use_dpop_nonce", nonce: "nceSKdZmf25ZbombMTLHUIw9I0Ss1PobvE6nqZOKAR0", originalError: {…} }
5252+117-9a2b9731dac7965d.js:1:4081
5353+ NextJS 32
5454+Received DPoP nonce error, retrying with nonce: nceSKdZmf25ZbombMTLHUIw9I0Ss1PobvE6nqZOKAR0 481-38cee1d3cffbeb60.js:1:5689
5555+Creating flushing status (attempt 2/3) with nonce: nceSKdZmf25ZbombMTLHUIw9I0Ss1PobvE6nqZOKAR0 481-38cee1d3cffbeb60.js:1:4530
5656+Using PDS endpoint: https://enoki.us-east.host.bsky.network 481-38cee1d3cffbeb60.js:1:4612
5757+API endpoint: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord 481-38cee1d3cffbeb60.js:1:4989
5858+Generating DPoP token for: https://enoki.us-east.host.bsky.network/xrpc/com.atproto.repo.createRecord with nonce: nceSKdZmf25ZbombMTLHUIw9I0Ss1PobvE6nqZOKAR0 481-38cee1d3cffbeb60.js:1:5054
5959+Including access token hash (ath) in DPoP token 481-38cee1d3cffbeb60.js:1:5122
6060+Adding ath claim to DPoP token 481-38cee1d3cffbeb60.js:1:6994
6161+Sending request to proxy API... 481-38cee1d3cffbeb60.js:1:5243
6262+Status update successful! 481-38cee1d3cffbeb60.js:1:5492
6363+Status update result:
6464+Object { uri: "at://did:plc:gq4fo3u6tqzzdkjlwzpb23tj/im.flushing.right.now/3ljt6tf5x4l2h", cid: "bafyreihsj6n5u72js5zfibc56ng2qpmglrljmjfx7aabyr3sogwwx5jm4q", commit: {…}, validationStatus: "unknown" }
6565+page-1f5ea258c75e5bac.js:1:1763