cap nhat auth he thong de nhung voi chebichat

This commit is contained in:
quangdn-ght
2025-07-07 21:37:16 +07:00
parent 972f957633
commit b42c5154e3
25 changed files with 2970 additions and 28 deletions

View File

@@ -0,0 +1,174 @@
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";
const SUPABASE_URL = process.env.SUPABASE_URL!;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
export async function GET(req: NextRequest) {
const url = new URL(req.url);
const authToken = url.searchParams.get("token");
const access_token = url.searchParams.get("access_token");
const refresh_token = url.searchParams.get("refresh_token");
const redirectTo = url.searchParams.get("redirect_to") || "/";
console.log("[Auth Callback] Processing authentication callback");
console.log("[Auth Callback] authToken:", authToken);
console.log("[Auth Callback] access_token:", access_token);
console.log("[Auth Callback] refresh_token:", refresh_token);
// Use either authToken or access_token (flexible for different auth flows)
const token = authToken || access_token;
if (!token) {
console.log(
"[Auth Callback] No authentication token found in query string",
);
return NextResponse.redirect(new URL("/login?error=no_token", req.url));
}
try {
// Validate the token with Supabase
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
global: {
headers: {
Authorization: `Bearer ${token}`,
},
},
});
const { data, error } = await supabase.auth.getUser();
if (error || !data?.user) {
console.error("[Auth Callback] Invalid token:", error);
return NextResponse.redirect(
new URL("/login?error=invalid_token", req.url),
);
}
console.log(
"[Auth Callback] Token validated successfully for user:",
data.user.id,
);
// Create response with redirect
const response = NextResponse.redirect(new URL(redirectTo, req.url));
// Set authentication cookies
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 60 * 60 * 24 * 7, // 7 days
path: "/",
};
// Set access token cookie
response.cookies.set("sb-access-token", token, cookieOptions);
// Set refresh token if available
if (refresh_token) {
response.cookies.set("sb-refresh-token", refresh_token, cookieOptions);
}
// Set user info cookie (optional, for quick access)
response.cookies.set(
"sb-user-info",
JSON.stringify({
id: data.user.id,
email: data.user.email,
user_metadata: data.user.user_metadata,
}),
{
...cookieOptions,
httpOnly: false, // Allow client-side access for user info
},
);
console.log("[Auth Callback] Authentication cookies set successfully");
return response;
} catch (err) {
console.error("[Auth Callback] Error processing authentication:", err);
return NextResponse.redirect(new URL("/login?error=auth_failed", req.url));
}
}
export async function POST(req: NextRequest) {
// Handle POST requests for programmatic token setting
try {
const body = await req.json();
const { access_token, refresh_token, redirect_to = "/" } = body;
if (!access_token) {
return NextResponse.json(
{ error: "access_token is required" },
{ status: 400 },
);
}
// Validate the token
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
global: {
headers: {
Authorization: `Bearer ${access_token}`,
},
},
});
const { data, error } = await supabase.auth.getUser();
if (error || !data?.user) {
return NextResponse.json(
{ error: "Invalid token", details: error },
{ status: 401 },
);
}
// Create response
const response = NextResponse.json({
success: true,
user: {
id: data.user.id,
email: data.user.email,
user_metadata: data.user.user_metadata,
},
redirect_to,
});
// Set authentication cookies
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 60 * 60 * 24 * 7, // 7 days
path: "/",
};
response.cookies.set("sb-access-token", access_token, cookieOptions);
if (refresh_token) {
response.cookies.set("sb-refresh-token", refresh_token, cookieOptions);
}
response.cookies.set(
"sb-user-info",
JSON.stringify({
id: data.user.id,
email: data.user.email,
user_metadata: data.user.user_metadata,
}),
{
...cookieOptions,
httpOnly: false,
},
);
return response;
} catch (err) {
console.error("[Auth Callback POST] Error:", err);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}

View File

@@ -1,21 +1,54 @@
// /app/api/auth/check/route.ts
import { NextRequest } from "next/server";
import { checkAuth } from "../../supabase";
import { NextRequest, NextResponse } from "next/server";
import { checkAuthWithRefresh, getUserInfoFromCookie } from "../../supabase";
export async function GET(req: NextRequest) {
const user = await checkAuth(req);
try {
const authResult = await checkAuthWithRefresh(req);
const userInfo = getUserInfoFromCookie(req);
console.log("[Auth] user ", user);
console.log("[Auth Check] user:", authResult.user?.email || "none");
if (!user) {
return new Response(JSON.stringify({ authenticated: false }), {
status: 401,
headers: { "Content-Type": "application/json" },
if (!authResult.user) {
return NextResponse.json(
{
authenticated: false,
error: "No valid authentication found",
},
{ status: 401 },
);
}
// Create response
const response = NextResponse.json({
authenticated: true,
user: authResult.user,
userInfo: userInfo, // Include cached user info for quick access
});
}
return new Response(JSON.stringify({ authenticated: true, user }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
// If token was refreshed, merge the refreshed cookies
if (authResult.needsRefresh && authResult.response) {
// Copy cookies from refresh response
authResult.response.cookies.getAll().forEach((cookie) => {
response.cookies.set(cookie.name, cookie.value, {
httpOnly: cookie.httpOnly,
secure: cookie.secure,
sameSite: cookie.sameSite,
maxAge: cookie.maxAge,
path: cookie.path,
});
});
}
return response;
} catch (error) {
console.error("[Auth Check] Error:", error);
return NextResponse.json(
{
authenticated: false,
error: "Authentication check failed",
},
{ status: 500 },
);
}
}

View File

@@ -0,0 +1,68 @@
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
console.log("[Auth Logout] Processing logout request");
const redirectTo =
new URL(req.url).searchParams.get("redirect_to") || "/login";
// Create response
const response = NextResponse.json({
success: true,
message: "Logged out successfully",
});
// Clear authentication cookies
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 0, // Expire immediately
path: "/",
};
response.cookies.set("sb-access-token", "", cookieOptions);
response.cookies.set("sb-refresh-token", "", cookieOptions);
response.cookies.set("sb-user-info", "", {
...cookieOptions,
httpOnly: false,
});
console.log("[Auth Logout] Authentication cookies cleared");
return response;
}
export async function GET(req: NextRequest) {
// Handle GET requests with redirect
const url = new URL(req.url);
const redirectTo = url.searchParams.get("redirect_to") || "/login";
console.log("[Auth Logout] Processing logout request with redirect");
// Create redirect response
const response = NextResponse.redirect(new URL(redirectTo, req.url));
// Clear authentication cookies
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 0, // Expire immediately
path: "/",
};
response.cookies.set("sb-access-token", "", cookieOptions);
response.cookies.set("sb-refresh-token", "", cookieOptions);
response.cookies.set("sb-user-info", "", {
...cookieOptions,
httpOnly: false,
});
console.log(
"[Auth Logout] Authentication cookies cleared, redirecting to:",
redirectTo,
);
return response;
}

View File

@@ -0,0 +1,99 @@
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";
const SUPABASE_URL = process.env.SUPABASE_URL!;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
export async function POST(req: NextRequest) {
console.log("[Auth Refresh] Processing token refresh request");
const refreshToken = req.cookies.get("sb-refresh-token")?.value;
if (!refreshToken) {
console.log("[Auth Refresh] No refresh token found");
return NextResponse.json(
{ error: "No refresh token found" },
{ status: 401 },
);
}
try {
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const { data, error } = await supabase.auth.refreshSession({
refresh_token: refreshToken,
});
if (error || !data?.session) {
console.error("[Auth Refresh] Token refresh failed:", error);
return NextResponse.json(
{ error: "Token refresh failed", details: error },
{ status: 401 },
);
}
console.log(
"[Auth Refresh] Token refreshed successfully for user:",
data.session.user.id,
);
// Create response
const response = NextResponse.json({
success: true,
user: {
id: data.session.user.id,
email: data.session.user.email,
user_metadata: data.session.user.user_metadata,
},
session: {
access_token: data.session.access_token,
expires_at: data.session.expires_at,
},
});
// Update authentication cookies
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 60 * 60 * 24 * 7, // 7 days
path: "/",
};
response.cookies.set(
"sb-access-token",
data.session.access_token,
cookieOptions,
);
if (data.session.refresh_token) {
response.cookies.set(
"sb-refresh-token",
data.session.refresh_token,
cookieOptions,
);
}
// Update user info cookie
response.cookies.set(
"sb-user-info",
JSON.stringify({
id: data.session.user.id,
email: data.session.user.email,
user_metadata: data.session.user.user_metadata,
}),
{
...cookieOptions,
httpOnly: false,
},
);
return response;
} catch (err) {
console.error("[Auth Refresh] Error:", err);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}