This repository has no description
1import { NextRequest, NextResponse } from 'next/server';
2
3// Configure this route as dynamic to fix static generation issues
4export const dynamic = 'force-dynamic';
5
6const DEFAULT_AUTH_SERVER = 'https://public.api.bsky.app';
7
8export async function POST(request: NextRequest) {
9 try {
10 // Parse request body to get PDS endpoint
11 const body = await request.json();
12 let pdsEndpoint = body.pdsEndpoint || DEFAULT_AUTH_SERVER;
13
14 // CRITICAL FIX: Third-party PDS servers don't implement OAuth endpoints
15 // Always use public.api.bsky.app for OAuth operations
16 let authServer = pdsEndpoint;
17 if (!pdsEndpoint.includes('public.api.bsky.app')) {
18 console.log('[NONCE API] Redirecting to public.api.bsky.app for OAuth on third-party PDS');
19 authServer = DEFAULT_AUTH_SERVER;
20 }
21
22 // Try to get a nonce from the auth server, not the PDS itself
23 const tokenEndpoint = `${authServer}/oauth/token`;
24 console.log(`[NONCE API] Attempting to get nonce from: ${tokenEndpoint}`);
25
26 // Try multiple methods to get a nonce
27 let nonce = null;
28
29 // Method 1: HEAD request (most efficient)
30 try {
31 console.log(`[NONCE API] Trying HEAD request to ${tokenEndpoint}`);
32 const headResponse = await fetch(tokenEndpoint, {
33 method: 'HEAD',
34 headers: {
35 'Accept': '*/*'
36 }
37 });
38
39 nonce = headResponse.headers.get('DPoP-Nonce');
40 if (nonce) {
41 console.log(`[NONCE API] Got nonce via HEAD request: ${nonce}`);
42 }
43 } catch (headError) {
44 console.warn(`[NONCE API] HEAD request failed:`, headError);
45 }
46
47 // Method 2: OPTIONS request if HEAD fails
48 if (!nonce) {
49 try {
50 console.log(`[NONCE API] Trying OPTIONS request to ${tokenEndpoint}`);
51 const optionsResponse = await fetch(tokenEndpoint, {
52 method: 'OPTIONS',
53 headers: {
54 'Accept': '*/*'
55 }
56 });
57
58 nonce = optionsResponse.headers.get('DPoP-Nonce');
59 if (nonce) {
60 console.log(`[NONCE API] Got nonce via OPTIONS request: ${nonce}`);
61 }
62 } catch (optionsError) {
63 console.warn(`[NONCE API] OPTIONS request failed:`, optionsError);
64 }
65 }
66
67 // Method 3: POST probe (last resort)
68 if (!nonce) {
69 try {
70 console.log(`[NONCE API] Trying POST probe to ${tokenEndpoint}`);
71 const probeResponse = await fetch(tokenEndpoint, {
72 method: 'POST',
73 headers: {
74 'Content-Type': 'application/x-www-form-urlencoded'
75 },
76 // Empty body to trigger an error response that might contain a nonce
77 body: new URLSearchParams({})
78 });
79
80 nonce = probeResponse.headers.get('DPoP-Nonce');
81 if (nonce) {
82 console.log(`[NONCE API] Got nonce via POST probe: ${nonce}`);
83 }
84 } catch (probeError) {
85 console.warn(`[NONCE API] POST probe failed:`, probeError);
86 }
87 }
88
89 // If we got a nonce through any method, return it
90 if (nonce) {
91 return NextResponse.json({ nonce });
92 }
93
94 // If all methods failed, return an error
95 console.log(`[NONCE API] All methods failed to get a nonce from ${tokenEndpoint}`);
96 return NextResponse.json(
97 { error: 'Could not retrieve nonce', endpoint: tokenEndpoint },
98 { status: 404 }
99 );
100 } catch (error: any) {
101 console.error('[NONCE API] Nonce retrieval error:', error);
102 return NextResponse.json(
103 { error: 'Nonce retrieval error', message: error.message },
104 { status: 500 }
105 );
106 }
107}
108
109// Maintain backward compatibility
110export async function GET() {
111 try {
112 // Use the default Bluesky server
113 const tokenEndpoint = `${DEFAULT_AUTH_SERVER}/oauth/token`;
114 console.log(`[NONCE API] GET: Attempting to get nonce from: ${tokenEndpoint}`);
115
116 // Try multiple methods to get a nonce
117 let nonce = null;
118
119 // Method 1: HEAD request (most efficient)
120 try {
121 console.log(`[NONCE API] GET: Trying HEAD request to ${tokenEndpoint}`);
122 const headResponse = await fetch(tokenEndpoint, {
123 method: 'HEAD',
124 headers: {
125 'Accept': '*/*'
126 }
127 });
128
129 nonce = headResponse.headers.get('DPoP-Nonce');
130 if (nonce) {
131 console.log(`[NONCE API] GET: Got nonce via HEAD request: ${nonce}`);
132 }
133 } catch (headError) {
134 console.warn(`[NONCE API] GET: HEAD request failed:`, headError);
135 }
136
137 // Method 2: OPTIONS request if HEAD fails
138 if (!nonce) {
139 try {
140 console.log(`[NONCE API] GET: Trying OPTIONS request to ${tokenEndpoint}`);
141 const optionsResponse = await fetch(tokenEndpoint, {
142 method: 'OPTIONS',
143 headers: {
144 'Accept': '*/*'
145 }
146 });
147
148 nonce = optionsResponse.headers.get('DPoP-Nonce');
149 if (nonce) {
150 console.log(`[NONCE API] GET: Got nonce via OPTIONS request: ${nonce}`);
151 }
152 } catch (optionsError) {
153 console.warn(`[NONCE API] GET: OPTIONS request failed:`, optionsError);
154 }
155 }
156
157 // Method 3: POST probe (last resort)
158 if (!nonce) {
159 try {
160 console.log(`[NONCE API] GET: Trying POST probe to ${tokenEndpoint}`);
161 const probeResponse = await fetch(tokenEndpoint, {
162 method: 'POST',
163 headers: {
164 'Content-Type': 'application/x-www-form-urlencoded'
165 },
166 // Empty body to trigger an error response that might contain a nonce
167 body: new URLSearchParams({})
168 });
169
170 nonce = probeResponse.headers.get('DPoP-Nonce');
171 if (nonce) {
172 console.log(`[NONCE API] GET: Got nonce via POST probe: ${nonce}`);
173 }
174 } catch (probeError) {
175 console.warn(`[NONCE API] GET: POST probe failed:`, probeError);
176 }
177 }
178
179 // If we got a nonce through any method, return it
180 if (nonce) {
181 return NextResponse.json({ nonce });
182 }
183
184 // If all methods failed, return an error
185 console.log(`[NONCE API] GET: All methods failed to get a nonce from ${tokenEndpoint}`);
186 return NextResponse.json(
187 { error: 'Could not retrieve nonce', endpoint: tokenEndpoint },
188 { status: 404 }
189 );
190 } catch (error: any) {
191 console.error('[NONCE API] GET: Nonce retrieval error:', error);
192 return NextResponse.json(
193 { error: 'Nonce retrieval error', message: error.message },
194 { status: 500 }
195 );
196 }
197}
198
199// Handle OPTIONS requests for CORS
200export async function OPTIONS() {
201 return new NextResponse(null, {
202 status: 200,
203 headers: {
204 'Access-Control-Allow-Origin': '*',
205 'Access-Control-Allow-Methods': 'GET, OPTIONS',
206 'Access-Control-Allow-Headers': 'Content-Type',
207 },
208 });
209}