Spaces:
Runtime error
Runtime error
| import { NextResponse } from 'next/server'; | |
| /** | |
| * GET /api/auth/callback | |
| * Handles the OAuth callback from HuggingFace. | |
| * Exchanges code for access token, fetches userinfo, sets session cookie. | |
| */ | |
| export async function GET(request) { | |
| const { searchParams } = new URL(request.url); | |
| const code = searchParams.get('code'); | |
| const state = searchParams.get('state'); | |
| if (!code) { | |
| return NextResponse.json({ error: 'Missing code parameter' }, { status: 400 }); | |
| } | |
| const clientId = process.env.OAUTH_CLIENT_ID; | |
| const clientSecret = process.env.OAUTH_CLIENT_SECRET; | |
| if (!clientId || !clientSecret) { | |
| return NextResponse.json({ error: 'OAuth not configured' }, { status: 500 }); | |
| } | |
| const host = process.env.SPACE_HOST | |
| ? `https://${process.env.SPACE_HOST}` | |
| : 'http://localhost:3000'; | |
| const redirectUri = `${host}/api/auth/callback`; | |
| try { | |
| // Exchange code for access token | |
| const tokenRes = await fetch('https://huggingface.co/oauth/token', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/x-www-form-urlencoded', | |
| 'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`, | |
| }, | |
| body: new URLSearchParams({ | |
| grant_type: 'authorization_code', | |
| code, | |
| redirect_uri: redirectUri, | |
| }), | |
| }); | |
| if (!tokenRes.ok) { | |
| const errText = await tokenRes.text(); | |
| console.error('Token exchange failed:', errText); | |
| return NextResponse.json({ error: 'Failed to exchange code for token' }, { status: 500 }); | |
| } | |
| const tokenData = await tokenRes.json(); | |
| // Fetch user info | |
| const userRes = await fetch('https://huggingface.co/oauth/userinfo', { | |
| headers: { 'Authorization': `Bearer ${tokenData.access_token}` }, | |
| }); | |
| if (!userRes.ok) { | |
| return NextResponse.json({ error: 'Failed to fetch user info' }, { status: 500 }); | |
| } | |
| const userInfo = await userRes.json(); | |
| const username = userInfo.preferred_username || userInfo.name || 'user'; | |
| // Check allowlist (if ALLOWED_USERS is set) | |
| const allowedUsers = process.env.ALLOWED_USERS; | |
| if (allowedUsers) { | |
| const allowlist = allowedUsers.split(',').map(u => u.trim().toLowerCase()); | |
| if (!allowlist.includes(username.toLowerCase())) { | |
| return NextResponse.json( | |
| { error: `Access denied. User "${username}" is not in the allowed list.` }, | |
| { status: 403 } | |
| ); | |
| } | |
| } | |
| // Set session cookie with username | |
| const response = NextResponse.redirect(host); | |
| response.cookies.set('hf_user', JSON.stringify({ | |
| username, | |
| name: userInfo.name, | |
| picture: userInfo.picture, | |
| }), { | |
| httpOnly: false, // readable by client JS | |
| secure: process.env.NODE_ENV === 'production', | |
| sameSite: 'lax', | |
| maxAge: 60 * 60 * 8, // 8 hours | |
| path: '/', | |
| }); | |
| return response; | |
| } catch (error) { | |
| console.error('OAuth callback error:', error); | |
| return NextResponse.json({ error: 'OAuth callback failed: ' + error.message }, { status: 500 }); | |
| } | |
| } | |