- Updated session management functions to include cookie handling for session creation and destruction. - Introduced applySessionCookie and clearSessionCookie functions for better cookie management in responses. - Enhanced error logging in the OAuth2 callback to improve debugging. - Revised PHASE-TRACKING.md to reflect changes in session handling and OAuth2 integration.
67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import type { SessionUser } from '@nexumi/shared';
|
|
import { type NextRequest, NextResponse } from 'next/server';
|
|
import { exchangeCodeForToken, fetchDiscordUser } from '@/lib/discord-oauth';
|
|
import { env } from '@/lib/env';
|
|
import { redis } from '@/lib/redis';
|
|
import { applySessionCookie, createSession } from '@/lib/session';
|
|
|
|
function oauthStateKey(state: string): string {
|
|
return `webui:oauth:state:${state}`;
|
|
}
|
|
|
|
function loginRedirect(error: string): NextResponse {
|
|
const url = new URL('/login', env.WEBUI_URL);
|
|
url.searchParams.set('error', error);
|
|
return NextResponse.redirect(url);
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const { searchParams } = new URL(request.url);
|
|
const code = searchParams.get('code');
|
|
const state = searchParams.get('state');
|
|
const oauthError = searchParams.get('error');
|
|
|
|
if (oauthError) {
|
|
return loginRedirect('access_denied');
|
|
}
|
|
|
|
if (!code || !state) {
|
|
return loginRedirect('invalid_request');
|
|
}
|
|
|
|
const stateKey = oauthStateKey(state);
|
|
const stateExists = await redis.get(stateKey);
|
|
if (!stateExists) {
|
|
return loginRedirect('invalid_state');
|
|
}
|
|
await redis.del(stateKey);
|
|
|
|
let cookieValue: string;
|
|
try {
|
|
const token = await exchangeCodeForToken(code);
|
|
const discordUser = await fetchDiscordUser(token.access_token);
|
|
|
|
const user: SessionUser = {
|
|
id: discordUser.id,
|
|
username: discordUser.username,
|
|
globalName: discordUser.global_name ?? null,
|
|
avatar: discordUser.avatar ?? null
|
|
};
|
|
|
|
cookieValue = await createSession({
|
|
user,
|
|
accessToken: token.access_token,
|
|
tokenExpiresAt: Date.now() + token.expires_in * 1000
|
|
});
|
|
} catch (error) {
|
|
console.error('OAuth callback failed', error);
|
|
return loginRedirect('oauth_failed');
|
|
}
|
|
|
|
// Cookie must be set on the redirect response — cookies().set() alone is not
|
|
// reliably attached when returning NextResponse.redirect() from a route handler.
|
|
const response = NextResponse.redirect(new URL('/dashboard', env.WEBUI_URL));
|
|
applySessionCookie(response, cookieValue);
|
|
return response;
|
|
}
|