in app/lib/server/githubTokenService.ts [10:70]
static async createEphemeralToken(
accessToken: string,
repositoryUrl: string,
expirationMinutes: number = 60
): Promise<{ token: string; expiresAt: Date } | null> {
try {
// Extract repository owner and name from URL
const repoMatch = repositoryUrl.match(
/github\.com[\/:]([^\/]+)\/([^\/\.]+)/
);
if (!repoMatch) {
console.error("Invalid GitHub repository URL:", repositoryUrl);
return null;
}
const [, owner, repo] = repoMatch;
console.log(`Creating ephemeral token for ${owner}/${repo}`);
// Calculate expiration time
const expiresAt = new Date();
expiresAt.setMinutes(expiresAt.getMinutes() + expirationMinutes);
// Create a fine-grained personal access token
// Note: This requires the user to have authorized the app with appropriate scopes
const tokenResponse = await fetch(
`${this.GITHUB_API_BASE}/user/installations`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
}
);
if (!tokenResponse.ok) {
console.warn("Could not create fine-grained token, using main token");
// Fallback: return the main access token with a warning
return {
token: accessToken,
expiresAt,
};
}
// For now, we'll use the main access token as GitHub's fine-grained
// personal access tokens through API are still in beta
// In a production environment, you might want to implement installation tokens
// or use GitHub Apps for better security
console.log(
`Using main access token as ephemeral token (expires in ${expirationMinutes} minutes)`
);
return {
token: accessToken,
expiresAt,
};
} catch (error) {
console.error("Error creating ephemeral GitHub token:", error);
return null;
}
}