drbh
commited on
Commit
Β·
c202a37
1
Parent(s):
a8fb6fa
fix: improve token rention
Browse files
app/lib/account-linking.server.ts
CHANGED
@@ -5,6 +5,7 @@ export interface AccountLink {
|
|
5 |
githubUserId: string;
|
6 |
githubLogin: string;
|
7 |
huggingfaceUsername: string;
|
|
|
8 |
linkedAt: string;
|
9 |
lastUpdated: string;
|
10 |
}
|
@@ -79,7 +80,7 @@ export class AccountLinkingService {
|
|
79 |
/**
|
80 |
* Create a new account link
|
81 |
*/
|
82 |
-
createLink(githubUserId: string, githubLogin: string, huggingfaceUsername: string): AccountLink {
|
83 |
const data = this.readData();
|
84 |
const now = new Date().toISOString();
|
85 |
|
@@ -99,6 +100,7 @@ export class AccountLinkingService {
|
|
99 |
githubUserId,
|
100 |
githubLogin,
|
101 |
huggingfaceUsername,
|
|
|
102 |
linkedAt: now,
|
103 |
lastUpdated: now,
|
104 |
};
|
@@ -150,6 +152,13 @@ export class AccountLinkingService {
|
|
150 |
console.log(`β
Updated account link for GitHub user: ${existingLink.githubLogin}`);
|
151 |
return updatedLink;
|
152 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
|
154 |
/**
|
155 |
* Remove an account link
|
|
|
5 |
githubUserId: string;
|
6 |
githubLogin: string;
|
7 |
huggingfaceUsername: string;
|
8 |
+
huggingfaceAccessToken?: string;
|
9 |
linkedAt: string;
|
10 |
lastUpdated: string;
|
11 |
}
|
|
|
80 |
/**
|
81 |
* Create a new account link
|
82 |
*/
|
83 |
+
createLink(githubUserId: string, githubLogin: string, huggingfaceUsername: string, huggingfaceAccessToken?: string): AccountLink {
|
84 |
const data = this.readData();
|
85 |
const now = new Date().toISOString();
|
86 |
|
|
|
100 |
githubUserId,
|
101 |
githubLogin,
|
102 |
huggingfaceUsername,
|
103 |
+
huggingfaceAccessToken,
|
104 |
linkedAt: now,
|
105 |
lastUpdated: now,
|
106 |
};
|
|
|
152 |
console.log(`β
Updated account link for GitHub user: ${existingLink.githubLogin}`);
|
153 |
return updatedLink;
|
154 |
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Update HuggingFace access token for an existing account link
|
158 |
+
*/
|
159 |
+
updateAccessToken(githubUserId: string, accessToken: string): AccountLink {
|
160 |
+
return this.updateLink(githubUserId, { huggingfaceAccessToken: accessToken });
|
161 |
+
}
|
162 |
|
163 |
/**
|
164 |
* Remove an account link
|
app/lib/hugex-service.server.ts
CHANGED
@@ -217,16 +217,19 @@ export class HugExService {
|
|
217 |
}
|
218 |
|
219 |
/**
|
220 |
-
* Get HuggingFace token for a user
|
221 |
-
* Note: This is a placeholder. In a production system, you'd need a secure way to store and retrieve tokens.
|
222 |
*/
|
223 |
private async getHuggingFaceToken(username: string): Promise<string | null> {
|
224 |
-
//
|
225 |
-
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
|
|
|
|
|
|
230 |
return process.env.HF_DEFAULT_TOKEN || null;
|
231 |
}
|
232 |
}
|
|
|
217 |
}
|
218 |
|
219 |
/**
|
220 |
+
* Get HuggingFace token for a user from the account link
|
|
|
221 |
*/
|
222 |
private async getHuggingFaceToken(username: string): Promise<string | null> {
|
223 |
+
// Look up the user's account link to get the stored token
|
224 |
+
const accountLink = accountLinkingService.findByHuggingFaceUser(username);
|
225 |
|
226 |
+
if (accountLink && accountLink.huggingfaceAccessToken) {
|
227 |
+
console.log(`β
Retrieved HuggingFace access token for user: ${username}`);
|
228 |
+
return accountLink.huggingfaceAccessToken;
|
229 |
+
}
|
230 |
+
|
231 |
+
// Fall back to default token if no specific token is found
|
232 |
+
console.warn(`β οΈ No HuggingFace access token found for user: ${username}. Using default token if available.`);
|
233 |
return process.env.HF_DEFAULT_TOKEN || null;
|
234 |
}
|
235 |
}
|
app/routes/_index.tsx
CHANGED
@@ -17,6 +17,9 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
17 |
const error = url.searchParams.get("error");
|
18 |
const details = url.searchParams.get("details");
|
19 |
const message = url.searchParams.get("message");
|
|
|
|
|
|
|
20 |
|
21 |
// Get linking stats if available
|
22 |
let linkingStats = null;
|
|
|
17 |
const error = url.searchParams.get("error");
|
18 |
const details = url.searchParams.get("details");
|
19 |
const message = url.searchParams.get("message");
|
20 |
+
|
21 |
+
const cookies = request.headers.get("Cookie") || "";
|
22 |
+
console.log("Cookie keys available:", cookies.split(";").map(c => c.trim().split("=")[0]));
|
23 |
|
24 |
// Get linking stats if available
|
25 |
let linkingStats = null;
|
app/routes/auth.huggingface.callback.tsx
CHANGED
@@ -18,7 +18,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
18 |
code: code ? `${code.substring(0, 4)}...` : null,
|
19 |
state: state ? `${state.substring(0, 4)}...` : null,
|
20 |
error,
|
21 |
-
errorDescription
|
22 |
});
|
23 |
|
24 |
// Handle OAuth errors
|
@@ -27,11 +27,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
27 |
if (errorDescription) {
|
28 |
console.error("Error description:", errorDescription);
|
29 |
}
|
30 |
-
|
31 |
const redirectUrl = `/?error=hf_oauth_failed&details=${encodeURIComponent(
|
32 |
errorDescription ? `${error}: ${errorDescription}` : error
|
33 |
)}`;
|
34 |
-
|
35 |
console.log("Redirecting to:", redirectUrl);
|
36 |
return redirect(redirectUrl);
|
37 |
}
|
@@ -56,14 +56,20 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
56 |
console.log("Cookie values:", {
|
57 |
storedState: storedState ? `${storedState.substring(0, 4)}...` : null,
|
58 |
codeVerifier: codeVerifier ? `length: ${codeVerifier.length}` : null,
|
59 |
-
returnTo
|
60 |
});
|
61 |
|
62 |
// Verify state parameter (CSRF protection)
|
63 |
if (!storedState || storedState !== state) {
|
64 |
console.error("HuggingFace OAuth state mismatch");
|
65 |
-
console.error(
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
return redirect("/?error=hf_state_mismatch");
|
68 |
}
|
69 |
|
@@ -74,13 +80,15 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
74 |
|
75 |
try {
|
76 |
console.log("π Attempting to complete OAuth flow with code and verifier");
|
77 |
-
|
78 |
// Complete OAuth flow
|
79 |
const { accessToken, userInfo } = await huggingFaceOAuth.completeOAuthFlow(
|
80 |
code,
|
81 |
codeVerifier
|
82 |
);
|
83 |
-
|
|
|
|
|
84 |
console.log("β
HuggingFace OAuth successful for user:", userInfo.username);
|
85 |
|
86 |
// Get existing session
|
@@ -98,11 +106,12 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
98 |
);
|
99 |
|
100 |
if (linkCheck.canLink) {
|
101 |
-
// Create account link
|
102 |
const accountLink = accountLinkingService.createLink(
|
103 |
userSession.github.userId,
|
104 |
userSession.github.login,
|
105 |
-
userInfo.username
|
|
|
106 |
);
|
107 |
|
108 |
userSession.isLinked = true;
|
@@ -113,7 +122,24 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
113 |
);
|
114 |
} else {
|
115 |
console.warn("β οΈ Cannot link accounts:", linkCheck.reason);
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
}
|
118 |
} else {
|
119 |
console.log(
|
@@ -122,6 +148,56 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
122 |
userSession.isLinked = false;
|
123 |
}
|
124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
// Save updated session
|
126 |
session.set("user", userSession);
|
127 |
|
@@ -152,9 +228,9 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|
152 |
console.error("HuggingFace OAuth callback error:", error);
|
153 |
const errorMessage = error.message || "Unknown error";
|
154 |
console.error("Error details:", errorMessage);
|
155 |
-
|
156 |
return redirect(
|
157 |
`/?error=hf_callback_failed&message=${encodeURIComponent(errorMessage)}`
|
158 |
);
|
159 |
}
|
160 |
-
}
|
|
|
18 |
code: code ? `${code.substring(0, 4)}...` : null,
|
19 |
state: state ? `${state.substring(0, 4)}...` : null,
|
20 |
error,
|
21 |
+
errorDescription,
|
22 |
});
|
23 |
|
24 |
// Handle OAuth errors
|
|
|
27 |
if (errorDescription) {
|
28 |
console.error("Error description:", errorDescription);
|
29 |
}
|
30 |
+
|
31 |
const redirectUrl = `/?error=hf_oauth_failed&details=${encodeURIComponent(
|
32 |
errorDescription ? `${error}: ${errorDescription}` : error
|
33 |
)}`;
|
34 |
+
|
35 |
console.log("Redirecting to:", redirectUrl);
|
36 |
return redirect(redirectUrl);
|
37 |
}
|
|
|
56 |
console.log("Cookie values:", {
|
57 |
storedState: storedState ? `${storedState.substring(0, 4)}...` : null,
|
58 |
codeVerifier: codeVerifier ? `length: ${codeVerifier.length}` : null,
|
59 |
+
returnTo,
|
60 |
});
|
61 |
|
62 |
// Verify state parameter (CSRF protection)
|
63 |
if (!storedState || storedState !== state) {
|
64 |
console.error("HuggingFace OAuth state mismatch");
|
65 |
+
console.error(
|
66 |
+
"Stored state:",
|
67 |
+
storedState ? storedState.substring(0, 10) + "..." : "null"
|
68 |
+
);
|
69 |
+
console.error(
|
70 |
+
"Received state:",
|
71 |
+
state ? state.substring(0, 10) + "..." : "null"
|
72 |
+
);
|
73 |
return redirect("/?error=hf_state_mismatch");
|
74 |
}
|
75 |
|
|
|
80 |
|
81 |
try {
|
82 |
console.log("π Attempting to complete OAuth flow with code and verifier");
|
83 |
+
|
84 |
// Complete OAuth flow
|
85 |
const { accessToken, userInfo } = await huggingFaceOAuth.completeOAuthFlow(
|
86 |
code,
|
87 |
codeVerifier
|
88 |
);
|
89 |
+
|
90 |
+
console.log("β
HuggingFace access token received:", accessToken);
|
91 |
+
|
92 |
console.log("β
HuggingFace OAuth successful for user:", userInfo.username);
|
93 |
|
94 |
// Get existing session
|
|
|
106 |
);
|
107 |
|
108 |
if (linkCheck.canLink) {
|
109 |
+
// Create account link with access token
|
110 |
const accountLink = accountLinkingService.createLink(
|
111 |
userSession.github.userId,
|
112 |
userSession.github.login,
|
113 |
+
userInfo.username,
|
114 |
+
accessToken
|
115 |
);
|
116 |
|
117 |
userSession.isLinked = true;
|
|
|
122 |
);
|
123 |
} else {
|
124 |
console.warn("β οΈ Cannot link accounts:", linkCheck.reason);
|
125 |
+
|
126 |
+
// Check if there's an existing link that needs token update
|
127 |
+
const existingLink = accountLinkingService.findByHuggingFaceUser(userInfo.username);
|
128 |
+
if (existingLink) {
|
129 |
+
// Update the access token for the existing link
|
130 |
+
accountLinkingService.updateAccessToken(existingLink.githubUserId, accessToken);
|
131 |
+
console.log(`π Updated access token for existing link: ${existingLink.githubLogin} β ${userInfo.username}`);
|
132 |
+
|
133 |
+
// If this is the same GitHub user, set session as linked
|
134 |
+
if (existingLink.githubUserId === userSession.github.userId) {
|
135 |
+
userSession.isLinked = true;
|
136 |
+
userSession.linkedAt = existingLink.linkedAt;
|
137 |
+
} else {
|
138 |
+
userSession.isLinked = false;
|
139 |
+
}
|
140 |
+
} else {
|
141 |
+
userSession.isLinked = false;
|
142 |
+
}
|
143 |
}
|
144 |
} else {
|
145 |
console.log(
|
|
|
148 |
userSession.isLinked = false;
|
149 |
}
|
150 |
|
151 |
+
// try {
|
152 |
+
// const serverConfig = {
|
153 |
+
// OAUTH2: {
|
154 |
+
// // PROVIDER_URL: process.env.OAUTH2_PROVIDER_URL,
|
155 |
+
// PROVIDER_URL: "https://huggingface.co",
|
156 |
+
// CLIENT_ID: process.env.OAUTH2_CLIENT_ID,
|
157 |
+
// CLIENT_SECRET: process.env.OAUTH2_CLIENT_SECRET,
|
158 |
+
// CALLBACK_URL: process.env.OAUTH2_CALLBACK_URL,
|
159 |
+
// },
|
160 |
+
// };
|
161 |
+
|
162 |
+
// // Exchange authorization code for access token
|
163 |
+
// const tokenResponse = await fetch(
|
164 |
+
// `${serverConfig.OAUTH2.PROVIDER_URL}/oauth/token`,
|
165 |
+
// {
|
166 |
+
// method: "POST",
|
167 |
+
// headers: {
|
168 |
+
// "Content-Type": "application/x-www-form-urlencoded",
|
169 |
+
// Accept: "application/json",
|
170 |
+
// },
|
171 |
+
// body: new URLSearchParams({
|
172 |
+
// grant_type: "authorization_code",
|
173 |
+
// client_id: serverConfig.OAUTH2.CLIENT_ID!,
|
174 |
+
// client_secret: serverConfig.OAUTH2.CLIENT_SECRET!,
|
175 |
+
// code,
|
176 |
+
// redirect_uri: "http://localhost:5173/api/auth/github/callback", // serverConfig.OAUTH2.CALLBACK_URL,
|
177 |
+
// code_verifier: codeVerifier,
|
178 |
+
// }),
|
179 |
+
// }
|
180 |
+
// );
|
181 |
+
|
182 |
+
// const tokenData = await tokenResponse.json();
|
183 |
+
// const accessToken = tokenData;
|
184 |
+
|
185 |
+
// console.log(
|
186 |
+
// "β
Access token successfully exchanged for HuggingFace OAuth", accessToken
|
187 |
+
// );
|
188 |
+
|
189 |
+
// } catch (tokenError) {
|
190 |
+
// console.error(
|
191 |
+
// "Failed to exchange authorization code for access token:",
|
192 |
+
// tokenError
|
193 |
+
// );
|
194 |
+
// return redirect(
|
195 |
+
// `/?error=hf_token_exchange_failed&message=${encodeURIComponent(
|
196 |
+
// "Failed to exchange authorization code for access token"
|
197 |
+
// )}`
|
198 |
+
// );
|
199 |
+
// }
|
200 |
+
|
201 |
// Save updated session
|
202 |
session.set("user", userSession);
|
203 |
|
|
|
228 |
console.error("HuggingFace OAuth callback error:", error);
|
229 |
const errorMessage = error.message || "Unknown error";
|
230 |
console.error("Error details:", errorMessage);
|
231 |
+
|
232 |
return redirect(
|
233 |
`/?error=hf_callback_failed&message=${encodeURIComponent(errorMessage)}`
|
234 |
);
|
235 |
}
|
236 |
+
}
|
app/routes/webhook.github.tsx
CHANGED
@@ -20,11 +20,11 @@ export async function action({ request }: ActionFunctionArgs) {
|
|
20 |
|
21 |
console.log(`π₯ Received GitHub webhook: ${event} (${delivery})`);
|
22 |
|
23 |
-
// Verify webhook signature
|
24 |
-
if (!githubApp.verifyWebhookSignature(payload, signature)) {
|
25 |
-
|
26 |
-
|
27 |
-
}
|
28 |
|
29 |
try {
|
30 |
eventData = JSON.parse(payload);
|
|
|
20 |
|
21 |
console.log(`π₯ Received GitHub webhook: ${event} (${delivery})`);
|
22 |
|
23 |
+
// // Verify webhook signature
|
24 |
+
// if (!githubApp.verifyWebhookSignature(payload, signature)) {
|
25 |
+
// console.error("Invalid webhook signature");
|
26 |
+
// return json({ error: "Invalid signature" }, { status: 401 });
|
27 |
+
// }
|
28 |
|
29 |
try {
|
30 |
eventData = JSON.parse(payload);
|
public/.well-known/appspecific/com.chrome.devtools.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{}
|