Files
app/app/api/auth/login/route.ts
Albert 8e14395eaf feat: Complete Step 3 & 4 - OAuth + SurrealDB schema
Step 3: ATproto OAuth + SurrealDB JWT
- Implement database-backed OAuth state storage (lib/auth/oauth-state.ts)
- Add session helpers for JWT decoding (lib/auth/session.ts)
- Fix OAuth callback to properly handle state retrieval
- Create /chat page displaying authenticated user handle
- Configure headless mode for Magnitude testing

Step 4: SurrealDB Schema & Permissions
- Define JWT-based access control (HS512 algorithm)
- Create user table with DID-based identity
- Create node table with row-level security (users can only access their own data)
- Create links_to relation table for graph edges
- Define vector search index (1536 dimensions for gemini-embedding-001)
- Add Docker Compose for local SurrealDB development

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 23:51:19 +00:00

53 lines
2.0 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { getAuthEndpoints, resolveHandle } from '@/lib/auth/atproto';
import { storeOAuthState } from '@/lib/auth/oauth-state';
import { randomState, randomPKCECodeVerifier, calculatePKCECodeChallenge } from 'openid-client';
const CLIENT_ID = process.env.BLUESKY_CLIENT_ID;
const REDIRECT_URI = process.env.BLUESKY_REDIRECT_URI;
export async function GET(request: NextRequest) {
if (!CLIENT_ID || !REDIRECT_URI) {
throw new Error('Bluesky client configuration is missing.');
}
const { searchParams } = new URL(request.url);
const handle = searchParams.get('handle');
if (!handle) {
return NextResponse.redirect(new URL('/login?error=Handle missing', request.url));
}
try {
// 1. Resolve handle to get PDS
const { pdsUrl } = await resolveHandle(handle);
// 2. Discover PDS-specific auth endpoints
const { authorizationEndpoint } = await getAuthEndpoints(pdsUrl);
// 3. Generate PKCE challenge and state
const state = randomState();
const code_verifier = randomPKCECodeVerifier();
const code_challenge = await calculatePKCECodeChallenge(code_verifier);
// 4. Store OAuth state in SurrealDB (not cookies, as they don't survive external redirects)
await storeOAuthState(state, code_verifier, pdsUrl);
// 5. Construct the authorization URL
const authUrl = new URL(authorizationEndpoint);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('scope', 'atproto');
authUrl.searchParams.set('code_challenge', code_challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('state', state);
// 6. Redirect user to the PDS login screen
return NextResponse.redirect(authUrl);
} catch (error) {
console.error('Auth login error:', error);
return NextResponse.redirect(new URL('/login?error=Invalid handle or PDS', request.url));
}
}