hugex-gh / app /routes /auth.huggingface.callback.tsx
drbh
fix: improve token rention
c202a37
import { redirect } from "@remix-run/node";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { huggingFaceOAuth } from "~/lib/huggingface-oauth.server";
import { parseCookies } from "~/lib/oauth-utils.server";
import { getSession, commitSession } from "~/lib/session.server";
import { accountLinkingService } from "~/lib/account-linking.server";
export async function loader({ request }: LoaderFunctionArgs) {
console.log("πŸ”₯ HuggingFace OAuth callback route hit");
const url = new URL(request.url);
const code = url.searchParams.get("code");
const state = url.searchParams.get("state");
const error = url.searchParams.get("error");
const errorDescription = url.searchParams.get("error_description");
console.log("Callback params:", {
code: code ? `${code.substring(0, 4)}...` : null,
state: state ? `${state.substring(0, 4)}...` : null,
error,
errorDescription,
});
// Handle OAuth errors
if (error) {
console.error("HuggingFace OAuth error:", error);
if (errorDescription) {
console.error("Error description:", errorDescription);
}
const redirectUrl = `/?error=hf_oauth_failed&details=${encodeURIComponent(
errorDescription ? `${error}: ${errorDescription}` : error
)}`;
console.log("Redirecting to:", redirectUrl);
return redirect(redirectUrl);
}
if (!code || !state) {
console.error("Missing code or state in HuggingFace OAuth callback");
return redirect("/?error=hf_missing_params");
}
// Get stored PKCE data from cookies
const cookieHeader = request.headers.get("Cookie");
const cookies = parseCookies(cookieHeader || "");
console.log("Cookie keys available:", Object.keys(cookies));
const storedState = cookies.hf_oauth_state;
const codeVerifier = cookies.hf_oauth_code_verifier;
const returnTo = cookies.hf_oauth_return_to
? decodeURIComponent(cookies.hf_oauth_return_to)
: "/";
console.log("Cookie values:", {
storedState: storedState ? `${storedState.substring(0, 4)}...` : null,
codeVerifier: codeVerifier ? `length: ${codeVerifier.length}` : null,
returnTo,
});
// Verify state parameter (CSRF protection)
if (!storedState || storedState !== state) {
console.error("HuggingFace OAuth state mismatch");
console.error(
"Stored state:",
storedState ? storedState.substring(0, 10) + "..." : "null"
);
console.error(
"Received state:",
state ? state.substring(0, 10) + "..." : "null"
);
return redirect("/?error=hf_state_mismatch");
}
if (!codeVerifier) {
console.error("Missing code verifier for HuggingFace OAuth");
return redirect("/?error=hf_missing_verifier");
}
try {
console.log("πŸ”„ Attempting to complete OAuth flow with code and verifier");
// Complete OAuth flow
const { accessToken, userInfo } = await huggingFaceOAuth.completeOAuthFlow(
code,
codeVerifier
);
console.log("βœ… HuggingFace access token received:", accessToken);
console.log("βœ… HuggingFace OAuth successful for user:", userInfo.username);
// Get existing session
const session = await getSession(request.headers.get("Cookie"));
let userSession = session.get("user") || { isLinked: false };
// Add HuggingFace info to session
userSession.huggingface = userInfo;
// Check if we can link accounts (if GitHub auth exists)
if (userSession.github) {
const linkCheck = accountLinkingService.canLink(
userSession.github.userId,
userInfo.username
);
if (linkCheck.canLink) {
// Create account link with access token
const accountLink = accountLinkingService.createLink(
userSession.github.userId,
userSession.github.login,
userInfo.username,
accessToken
);
userSession.isLinked = true;
userSession.linkedAt = accountLink.linkedAt;
console.log(
`πŸ”— Accounts automatically linked: ${userSession.github.login} ↔ ${userInfo.username}`
);
} else {
console.warn("⚠️ Cannot link accounts:", linkCheck.reason);
// Check if there's an existing link that needs token update
const existingLink = accountLinkingService.findByHuggingFaceUser(userInfo.username);
if (existingLink) {
// Update the access token for the existing link
accountLinkingService.updateAccessToken(existingLink.githubUserId, accessToken);
console.log(`πŸ”„ Updated access token for existing link: ${existingLink.githubLogin} ↔ ${userInfo.username}`);
// If this is the same GitHub user, set session as linked
if (existingLink.githubUserId === userSession.github.userId) {
userSession.isLinked = true;
userSession.linkedAt = existingLink.linkedAt;
} else {
userSession.isLinked = false;
}
} else {
userSession.isLinked = false;
}
}
} else {
console.log(
"ℹ️ No GitHub authentication found, HuggingFace auth saved for later linking"
);
userSession.isLinked = false;
}
// try {
// const serverConfig = {
// OAUTH2: {
// // PROVIDER_URL: process.env.OAUTH2_PROVIDER_URL,
// PROVIDER_URL: "https://huggingface.co",
// CLIENT_ID: process.env.OAUTH2_CLIENT_ID,
// CLIENT_SECRET: process.env.OAUTH2_CLIENT_SECRET,
// CALLBACK_URL: process.env.OAUTH2_CALLBACK_URL,
// },
// };
// // Exchange authorization code for access token
// const tokenResponse = await fetch(
// `${serverConfig.OAUTH2.PROVIDER_URL}/oauth/token`,
// {
// method: "POST",
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// Accept: "application/json",
// },
// body: new URLSearchParams({
// grant_type: "authorization_code",
// client_id: serverConfig.OAUTH2.CLIENT_ID!,
// client_secret: serverConfig.OAUTH2.CLIENT_SECRET!,
// code,
// redirect_uri: "http://localhost:5173/api/auth/github/callback", // serverConfig.OAUTH2.CALLBACK_URL,
// code_verifier: codeVerifier,
// }),
// }
// );
// const tokenData = await tokenResponse.json();
// const accessToken = tokenData;
// console.log(
// "βœ… Access token successfully exchanged for HuggingFace OAuth", accessToken
// );
// } catch (tokenError) {
// console.error(
// "Failed to exchange authorization code for access token:",
// tokenError
// );
// return redirect(
// `/?error=hf_token_exchange_failed&message=${encodeURIComponent(
// "Failed to exchange authorization code for access token"
// )}`
// );
// }
// Save updated session
session.set("user", userSession);
// Create response with updated session cookie
const response = redirect(returnTo);
// Clear OAuth temporary cookies
const clearCookieOptions = "Path=/; HttpOnly; SameSite=Lax; Max-Age=0";
response.headers.append(
"Set-Cookie",
`hf_oauth_state=; ${clearCookieOptions}`
);
response.headers.append(
"Set-Cookie",
`hf_oauth_code_verifier=; ${clearCookieOptions}`
);
response.headers.append(
"Set-Cookie",
`hf_oauth_return_to=; ${clearCookieOptions}`
);
// Set session cookie
response.headers.append("Set-Cookie", await commitSession(session));
console.log("βœ… Redirecting to:", returnTo);
return response;
} catch (error: any) {
console.error("HuggingFace OAuth callback error:", error);
const errorMessage = error.message || "Unknown error";
console.error("Error details:", errorMessage);
return redirect(
`/?error=hf_callback_failed&message=${encodeURIComponent(errorMessage)}`
);
}
}