Files
drinkmarble/lib/auth/room-access.ts

69 lines
2.3 KiB
TypeScript

import { NextResponse } from 'next/server';
import {
allowChannelIdJoin,
isChzzkOAuthConfigured,
requireChzzkLoginForChannelRooms,
} from '@/lib/auth/mode';
import { readAuthSession, type AuthSession } from '@/lib/auth/session';
import type { RoomRecord } from '@/lib/rooms/store';
export async function getRequestAuthSession(
request?: Request,
): Promise<AuthSession | null> {
// Route Handler에서는 cookies()가 기본. request Cookie 헤더도 보조 파싱.
const fromCookies = await readAuthSession();
if (fromCookies) return fromCookies;
if (!request) return null;
const header = request.headers.get('cookie') || '';
const match = /(?:^|;\s*)drinkmarble_auth=([^;]+)/.exec(header);
if (!match?.[1]) return null;
const { parseAuthSession } = await import('@/lib/auth/session');
return parseAuthSession(decodeURIComponent(match[1]));
}
/**
* 치지직 연동 룸 접근 검사.
* - 개발(채널 ID 허용): 세션 없어도 OK
* - 릴리즈: 세션의 channelId가 룸 채널과 일치해야 함
*/
export async function assertChannelRoomAccess(
room: RoomRecord,
request?: Request,
): Promise<{ ok: true; session: AuthSession | null } | { ok: false; response: NextResponse }> {
if (!room.settings.chzzkEnabled) {
return { ok: true, session: null };
}
if (!requireChzzkLoginForChannelRooms()) {
return { ok: true, session: await getRequestAuthSession(request) };
}
const session = await getRequestAuthSession(request);
const roomChannel = room.settings.channelId?.toLowerCase() || '';
if (!session || !roomChannel || session.channelId !== roomChannel) {
return {
ok: false,
response: NextResponse.json(
{
error:
'릴리즈 환경에서는 치지직 로그인으로 본인 채널만 입장할 수 있습니다. 홈에서 다시 로그인해 주세요.',
code: 'CHZZK_AUTH_REQUIRED',
allowChannelIdJoin: allowChannelIdJoin(),
oauthConfigured: isChzzkOAuthConfigured(),
},
{ status: 401 },
),
};
}
return { ok: true, session };
}
export function authModePublic() {
return {
allowChannelIdJoin: allowChannelIdJoin(),
requireChzzkLogin: requireChzzkLoginForChannelRooms(),
oauthConfigured: isChzzkOAuthConfigured(),
nodeEnv: process.env.NODE_ENV,
};
}