Files
LetsBeBiz-Redesign/letsbe-hub/src/app/api/v1/admin/netcup/auth/route.ts

186 lines
5.0 KiB
TypeScript
Raw Normal View History

import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { netcupService, NetcupAuthError } from '@/lib/services/netcup-service'
// Store pending device auth sessions (in-memory for simplicity)
// In production, consider storing in Redis or database
const pendingAuthSessions = new Map<
string,
{
deviceCode: string
expiresAt: number
interval: number
}
>()
/**
* GET /api/v1/admin/netcup/auth
* Get current authentication status
*/
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session || session.user.userType !== 'staff') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const status = await netcupService.getAuthStatus()
return NextResponse.json(status)
} catch (error) {
console.error('Error getting Netcup auth status:', error)
return NextResponse.json(
{ error: 'Failed to get auth status' },
{ status: 500 }
)
}
}
/**
* POST /api/v1/admin/netcup/auth
* Initiate device auth flow or poll for token
*
* Body:
* - action: 'initiate' | 'poll' | 'disconnect'
* - sessionId?: string (for poll action)
*/
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session || session.user.userType !== 'staff') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { action, sessionId } = body as {
action: 'initiate' | 'poll' | 'disconnect'
sessionId?: string
}
if (!action || !['initiate', 'poll', 'disconnect'].includes(action)) {
return NextResponse.json(
{ error: 'Invalid action. Must be: initiate, poll, or disconnect' },
{ status: 400 }
)
}
switch (action) {
case 'initiate': {
// Start device auth flow
const deviceAuth = await netcupService.initiateDeviceAuth()
// Store session for polling
const newSessionId = crypto.randomUUID()
pendingAuthSessions.set(newSessionId, {
deviceCode: deviceAuth.device_code,
expiresAt: Date.now() + deviceAuth.expires_in * 1000,
interval: deviceAuth.interval,
})
// Clean up expired sessions
for (const [id, sess] of pendingAuthSessions) {
if (sess.expiresAt < Date.now()) {
pendingAuthSessions.delete(id)
}
}
return NextResponse.json({
success: true,
sessionId: newSessionId,
userCode: deviceAuth.user_code,
verificationUri: deviceAuth.verification_uri,
verificationUriComplete: deviceAuth.verification_uri_complete,
expiresIn: deviceAuth.expires_in,
interval: deviceAuth.interval,
})
}
case 'poll': {
if (!sessionId) {
return NextResponse.json(
{ error: 'Session ID required for polling' },
{ status: 400 }
)
}
const pendingSession = pendingAuthSessions.get(sessionId)
if (!pendingSession) {
return NextResponse.json(
{ error: 'Session not found or expired' },
{ status: 404 }
)
}
if (pendingSession.expiresAt < Date.now()) {
pendingAuthSessions.delete(sessionId)
return NextResponse.json(
{ error: 'Session expired' },
{ status: 410 }
)
}
try {
const tokens = await netcupService.pollForToken(pendingSession.deviceCode)
if (!tokens) {
// Still waiting for user authorization
return NextResponse.json({
success: false,
status: 'pending',
message: 'Waiting for user authorization',
})
}
// Success! Clean up session
pendingAuthSessions.delete(sessionId)
return NextResponse.json({
success: true,
status: 'authenticated',
message: 'Successfully authenticated with Netcup',
})
} catch (error) {
if (error instanceof NetcupAuthError) {
pendingAuthSessions.delete(sessionId)
return NextResponse.json(
{ error: error.message },
{ status: 400 }
)
}
throw error
}
}
case 'disconnect': {
// Clear stored tokens
await netcupService.clearTokens()
return NextResponse.json({
success: true,
message: 'Disconnected from Netcup',
})
}
default:
return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
}
} catch (error) {
console.error('Error in Netcup auth:', error)
if (error instanceof NetcupAuthError) {
return NextResponse.json(
{ error: error.message },
{ status: 400 }
)
}
return NextResponse.json(
{ error: 'Failed to process auth request' },
{ status: 500 }
)
}
}