치지직 앱으로 연동하고, 연동 확인된 ID만 접속할 수 있도록 수정, 개발용으로는 채널ID를 수동으로 접속할 수 있도록 변경.
최초 추천 고정 배치 추가.
This commit is contained in:
89
lib/chzzk/oauth.ts
Normal file
89
lib/chzzk/oauth.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { getChzzkOAuthConfig } from '@/lib/auth/mode';
|
||||
|
||||
const OPENAPI = 'https://openapi.chzzk.naver.com';
|
||||
|
||||
export function buildChzzkLoginUrl(state: string) {
|
||||
const { clientId, redirectUri } = getChzzkOAuthConfig();
|
||||
const url = new URL('https://chzzk.naver.com/account-interlock');
|
||||
url.searchParams.set('clientId', clientId);
|
||||
url.searchParams.set('redirectUri', redirectUri);
|
||||
url.searchParams.set('state', state);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export async function exchangeAuthorizationCode(code: string, state: string) {
|
||||
const { clientId, clientSecret } = getChzzkOAuthConfig();
|
||||
const res = await fetch(`${OPENAPI}/auth/v1/token`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
grantType: 'authorization_code',
|
||||
clientId,
|
||||
clientSecret,
|
||||
code,
|
||||
state,
|
||||
}),
|
||||
cache: 'no-store',
|
||||
});
|
||||
const json = (await res.json().catch(() => ({}))) as {
|
||||
content?: {
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
tokenType?: string;
|
||||
expiresIn?: string | number;
|
||||
};
|
||||
message?: string;
|
||||
code?: number;
|
||||
};
|
||||
if (!res.ok || !json.content?.accessToken) {
|
||||
throw new Error(json.message || `토큰 발급 실패 (${res.status})`);
|
||||
}
|
||||
return {
|
||||
accessToken: json.content.accessToken,
|
||||
refreshToken: json.content.refreshToken ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchAuthedUser(accessToken: string) {
|
||||
const res = await fetch(`${OPENAPI}/open/v1/users/me`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
cache: 'no-store',
|
||||
});
|
||||
const json = (await res.json().catch(() => ({}))) as {
|
||||
content?: { channelId?: string; channelName?: string };
|
||||
message?: string;
|
||||
};
|
||||
if (!res.ok || !json.content?.channelId) {
|
||||
throw new Error(json.message || `유저 정보 조회 실패 (${res.status})`);
|
||||
}
|
||||
return {
|
||||
channelId: json.content.channelId.toLowerCase(),
|
||||
channelName: json.content.channelName ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/** 확인 후 치지직 토큰 폐기 (저장하지 않음) */
|
||||
export async function revokeToken(
|
||||
token: string,
|
||||
tokenTypeHint: 'access_token' | 'refresh_token' = 'access_token',
|
||||
) {
|
||||
const { clientId, clientSecret } = getChzzkOAuthConfig();
|
||||
try {
|
||||
await fetch(`${OPENAPI}/auth/v1/token/revoke`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
clientId,
|
||||
clientSecret,
|
||||
token,
|
||||
tokenTypeHint,
|
||||
}),
|
||||
cache: 'no-store',
|
||||
});
|
||||
} catch {
|
||||
// 폐기는 best-effort
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user