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>
53 lines
2.0 KiB
TypeScript
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));
|
|
}
|
|
}
|