import { createRepo, repoExists, uploadFile, whoAmI } from "@huggingface/hub"; import type { NextRequest } from "next/server"; import { z } from "zod"; import type { SignupSession } from "./session"; const DEFAULT_HUB_URL = "https://huggingface.co"; const OAUTH_SCOPE = "openid profile email"; const EVENT_NAME = "Humanity's Last Hackathon"; const EVENT_OBJECTIVE = "Write Mac Metal kernels with Codex."; const tokenResponseSchema = z.object({ access_token: z.string().min(1), expires_in: z.number().int().positive(), scope: z.string().min(1), token_type: z.string().min(1), }); const userInfoSchema = z.object({ sub: z.string().min(1), name: z.string(), preferred_username: z.string().min(1), email: z.string().email().optional(), email_verified: z.boolean().optional(), picture: z.string().url(), profile: z.string().url(), website: z.string().url().optional(), isPro: z.boolean(), }); export interface SignupRecord { schemaVersion: 2; event: { name: string; objective: string; }; signedUpAt: string; source: { type: "huggingface-oauth"; scope: string; }; participant: { hf: { id: string; username: string; fullName: string; email: string; emailVerified: boolean; avatarUrl: string; profileUrl: string; website: string | null; isPro: boolean; }; }; } function getHubUrl(): string { return (process.env.HF_HUB_URL?.trim() || DEFAULT_HUB_URL).replace(/\/+$/, ""); } function requireEnv(name: string): string { const value = process.env[name]?.trim(); if (!value) { throw new Error(`Missing ${name}.`); } return value; } export function getAppUrl(request?: NextRequest): string { const configured = process.env.APP_URL?.trim(); if (configured) { return configured.replace(/\/+$/, ""); } if (!request) { return "http://localhost:3000"; } const forwardedProto = request.headers.get("x-forwarded-proto"); const forwardedHost = request.headers.get("x-forwarded-host") ?? request.headers.get("host"); if (forwardedProto && forwardedHost) { return `${forwardedProto}://${forwardedHost}`; } return new URL(request.url).origin; } export function getOAuthRedirectUri(request?: NextRequest): string { return `${getAppUrl(request)}/api/auth/huggingface/callback`; } export function createOAuthAuthorizationUrl(params: { state: string; codeChallenge: string; request?: NextRequest; }): string { const search = new URLSearchParams({ client_id: requireEnv("HF_OAUTH_CLIENT_ID"), redirect_uri: getOAuthRedirectUri(params.request), response_type: "code", scope: OAUTH_SCOPE, state: params.state, code_challenge: params.codeChallenge, code_challenge_method: "S256", }); return `${getHubUrl()}/oauth/authorize?${search.toString()}`; } export async function exchangeCodeForToken(params: { code: string; codeVerifier: string; redirectUri: string; }) { const response = await fetch(`${getHubUrl()}/oauth/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "authorization_code", code: params.code, redirect_uri: params.redirectUri, client_id: requireEnv("HF_OAUTH_CLIENT_ID"), client_secret: requireEnv("HF_OAUTH_CLIENT_SECRET"), code_verifier: params.codeVerifier, }), }); if (!response.ok) { throw new Error(`Hugging Face token exchange failed with status ${response.status}.`); } return tokenResponseSchema.parse(await response.json()); } export async function fetchUserInfo(accessToken: string) { const response = await fetch(`${getHubUrl()}/oauth/userinfo`, { headers: { Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error(`Hugging Face userinfo failed with status ${response.status}.`); } return userInfoSchema.parse(await response.json()); } export async function fetchViewer(accessToken: string) { const viewer = await whoAmI({ accessToken }); if (viewer.type !== "user") { throw new Error("Expected a user account from Hugging Face OAuth."); } return viewer; } export function createSignupRecord(params: { session: SignupSession }): SignupRecord { const signedUpAt = new Date().toISOString(); return { schemaVersion: 2, event: { name: EVENT_NAME, objective: EVENT_OBJECTIVE, }, signedUpAt, source: { type: "huggingface-oauth", scope: OAUTH_SCOPE, }, participant: { hf: { id: params.session.id, username: params.session.username, fullName: params.session.name, email: params.session.email, emailVerified: params.session.emailVerified, avatarUrl: params.session.avatarUrl, profileUrl: params.session.profileUrl, website: params.session.website, isPro: params.session.isPro, }, }, }; } function createDatasetReadme(): string { return `--- tags: - private - registrations - hackathon --- # Humanity's Last Hackathon Signups Private registration dataset for the Humanity's Last Hackathon landing page. - Event: ${EVENT_NAME} - Objective: ${EVENT_OBJECTIVE} - Source: simple Hugging Face OAuth signup app Each participant record is stored at \`signups/.json\`. `; } export async function storeSignupRecord(record: SignupRecord) { const repo = { type: "dataset" as const, name: requireEnv("HF_DATASET_REPO"), }; const accessToken = requireEnv("HF_DATASET_WRITE_TOKEN"); const filePath = `signups/${record.participant.hf.id}.json`; if (!(await repoExists({ repo, accessToken }))) { await createRepo({ repo, accessToken, private: true, files: [ { path: "README.md", content: new Blob([createDatasetReadme()], { type: "text/markdown" }), }, ], }); } await uploadFile({ repo, accessToken, file: { path: filePath, content: new Blob([`${JSON.stringify(record, null, 2)}\n`], { type: "application/json", }), }, commitTitle: `Register ${record.participant.hf.username} for Humanity's Last Hackathon`, commitDescription: "Write or update a signup record from the Hugging Face OAuth signup app.", }); return { repoName: repo.name, path: filePath, }; }