feat: implement TikTok OAuth flow and integrate profile data into home page
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
/**
|
||||
* GET /api/auth/tiktok/callback
|
||||
* Handles the OAuth callback from TikTok.
|
||||
* Exchanges the authorization code for access + refresh tokens.
|
||||
* Displays the tokens so you can copy them to your .env file.
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const code = searchParams.get("code");
|
||||
const error = searchParams.get("error");
|
||||
|
||||
if (error) {
|
||||
return NextResponse.json(
|
||||
{ error, description: searchParams.get("error_description") },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
return NextResponse.json(
|
||||
{ error: "No authorization code received" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const clientKey = process.env.TIKTOK_CLIENT_KEY;
|
||||
const clientSecret = process.env.TIKTOK_CLIENT_SECRET;
|
||||
const redirectUri = `${process.env.BASE_URL}/api/auth/tiktok/callback`;
|
||||
|
||||
if (!clientKey || !clientSecret) {
|
||||
return NextResponse.json(
|
||||
{ error: "TIKTOK_CLIENT_KEY or TIKTOK_CLIENT_SECRET not set" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// Exchange the code for tokens
|
||||
const tokenRes = await fetch(
|
||||
"https://open.tiktokapis.com/v2/oauth/token/",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({
|
||||
client_key: clientKey,
|
||||
client_secret: clientSecret,
|
||||
code,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: redirectUri,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const tokenData = await tokenRes.json();
|
||||
|
||||
if (tokenData.error) {
|
||||
return NextResponse.json(
|
||||
{ error: tokenData.error, description: tokenData.error_description },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Return an HTML page showing the tokens to copy
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>TikTok OAuth Success</title></head>
|
||||
<body style="font-family: system-ui; max-width: 600px; margin: 40px auto; padding: 20px; background: #0a0a0a; color: #e5e5e5;">
|
||||
<h1 style="color: #22c55e;">✅ TikTok OAuth Success!</h1>
|
||||
<p>Copy these values to your <code>.env</code> file:</p>
|
||||
|
||||
<div style="background: #1a1a1a; padding: 16px; border-radius: 8px; margin: 16px 0; word-break: break-all;">
|
||||
<p><strong>TIKTOK_ACCESS_TOKEN=</strong><br/><code>${tokenData.access_token}</code></p>
|
||||
<p><strong>TIKTOK_REFRESH_TOKEN=</strong><br/><code>${tokenData.refresh_token}</code></p>
|
||||
<p><strong>TIKTOK_OPEN_ID=</strong><br/><code>${tokenData.open_id}</code></p>
|
||||
</div>
|
||||
|
||||
<p style="color: #a3a3a3; font-size: 14px;">
|
||||
Access token expires in ${Math.floor(tokenData.expires_in / 3600)} hours.<br/>
|
||||
Refresh token expires in ${Math.floor(tokenData.refresh_expires_in / 86400)} days.<br/>
|
||||
The server will auto-refresh the access token using the refresh token.
|
||||
</p>
|
||||
|
||||
<details style="margin-top: 20px;">
|
||||
<summary style="cursor: pointer;">Full response</summary>
|
||||
<pre style="background: #1a1a1a; padding: 12px; border-radius: 6px; overflow-x: auto;">${JSON.stringify(tokenData, null, 2)}</pre>
|
||||
</details>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
return new NextResponse(html, {
|
||||
headers: { "Content-Type": "text/html" },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user