File size: 2,806 Bytes
654b283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { NextRequest, NextResponse } from "next/server";

import {
  createSignupRecord,
  exchangeCodeForToken,
  fetchUserInfo,
  fetchViewer,
  getAppUrl,
  getOAuthRedirectUri,
  storeSignupRecord,
} from "@/lib/huggingface";
import {
  clearOAuthCookies,
  clearSessionCookie,
  readOAuthCookies,
  setSessionCookie,
} from "@/lib/session";

export const runtime = "nodejs";

function redirectWithAuthError(request: NextRequest, code: string) {
  const response = NextResponse.redirect(new URL(`/?error=${code}`, getAppUrl(request)));
  clearOAuthCookies(response);
  clearSessionCookie(response);
  return response;
}

export async function GET(request: NextRequest) {
  const url = new URL(request.url);
  const providerError = url.searchParams.get("error");

  if (providerError) {
    return redirectWithAuthError(
      request,
      providerError === "access_denied" ? "oauth_denied" : "oauth_failed",
    );
  }

  const code = url.searchParams.get("code");
  const returnedState = url.searchParams.get("state");
  const { state, verifier } = readOAuthCookies(request);

  if (!code || !returnedState || !state || returnedState !== state || !verifier) {
    return redirectWithAuthError(request, "oauth_state");
  }

  try {
    const token = await exchangeCodeForToken({
      code,
      codeVerifier: verifier,
      redirectUri: getOAuthRedirectUri(request),
    });

    const [userInfo, viewer] = await Promise.all([
      fetchUserInfo(token.access_token),
      fetchViewer(token.access_token),
    ]);

    const session = {
      id: userInfo.sub,
      username: userInfo.preferred_username || viewer.name,
      name: userInfo.name.trim() || viewer.fullname || viewer.name,
      email: userInfo.email ?? viewer.email,
      emailVerified: userInfo.email_verified ?? viewer.emailVerified,
      avatarUrl: userInfo.picture || viewer.avatarUrl,
      profileUrl: userInfo.profile || `${process.env.HF_HUB_URL ?? "https://huggingface.co"}/${viewer.name}`,
      website: userInfo.website ?? null,
      isPro: userInfo.isPro ?? viewer.isPro,
      registrationComplete: false,
    };

    try {
      await storeSignupRecord(createSignupRecord({ session }));

      const response = NextResponse.redirect(new URL("/?signed_up=1", getAppUrl(request)));
      clearOAuthCookies(response);
      setSessionCookie(response, {
        ...session,
        registrationComplete: true,
      });

      return response;
    } catch (error) {
      console.error(error);

      const response = NextResponse.redirect(new URL("/?error=signup_store", getAppUrl(request)));
      clearOAuthCookies(response);
      setSessionCookie(response, session);

      return response;
    }
  } catch (error) {
    console.error(error);
    return redirectWithAuthError(request, "oauth_exchange");
  }
}