This commit is contained in:
2024-07-29 13:17:10 +05:30
commit 87eaf79afc
37 changed files with 18964 additions and 0 deletions

57
pages/_app.tsx Normal file
View File

@@ -0,0 +1,57 @@
import "../styles/globals.css";
import type { AppProps } from "next/app";
import Head from "next/head";
import { PrivyProvider } from "@privy-io/react-auth";
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<link
rel="preload"
href="/fonts/AdelleSans-Regular.woff"
as="font"
crossOrigin=""
/>
<link
rel="preload"
href="/fonts/AdelleSans-Regular.woff2"
as="font"
crossOrigin=""
/>
<link
rel="preload"
href="/fonts/AdelleSans-Semibold.woff"
as="font"
crossOrigin=""
/>
<link
rel="preload"
href="/fonts/AdelleSans-Semibold.woff2"
as="font"
crossOrigin=""
/>
<link rel="icon" href="/favicons/favicon.ico" sizes="any" />
<link rel="icon" href="/favicons/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon.png" />
<link rel="manifest" href="/favicons/manifest.json" />
<title>Privy Auth Starter</title>
<meta name="description" content="Privy Auth Starter" />
</Head>
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || ""}
config={{
embeddedWallets: {
createOnLogin: "all-users",
},
}}
>
<Component {...pageProps} />
</PrivyProvider>
</>
);
}
export default MyApp;

37
pages/api/verify.ts Normal file
View File

@@ -0,0 +1,37 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { PrivyClient, AuthTokenClaims } from "@privy-io/server-auth";
const PRIVY_APP_ID = process.env.NEXT_PUBLIC_PRIVY_APP_ID;
const PRIVY_APP_SECRET = process.env.PRIVY_APP_SECRET;
const client = new PrivyClient(PRIVY_APP_ID!, PRIVY_APP_SECRET!);
export type AuthenticateSuccessResponse = {
claims: AuthTokenClaims;
};
export type AuthenticationErrorResponse = {
error: string;
};
async function handler(
req: NextApiRequest,
res: NextApiResponse<
AuthenticateSuccessResponse | AuthenticationErrorResponse
>,
) {
const headerAuthToken = req.headers.authorization?.replace(/^Bearer /, "");
const cookieAuthToken = req.cookies["privy-token"];
const authToken = cookieAuthToken || headerAuthToken;
if (!authToken) return res.status(401).json({ error: "Missing auth token" });
try {
const claims = await client.verifyAuthToken(authToken);
return res.status(200).json({ claims });
} catch (e: any) {
return res.status(401).json({ error: e.message });
}
}
export default handler;

146
pages/dashboard.tsx Normal file
View File

@@ -0,0 +1,146 @@
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { getAccessToken, usePrivy } from "@privy-io/react-auth";
import Head from "next/head";
async function verifyToken() {
const url = "/api/verify";
const accessToken = await getAccessToken();
const result = await fetch(url, {
headers: {
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : undefined),
},
});
return await result.json();
}
export default function DashboardPage() {
const [verifyResult, setVerifyResult] = useState();
const router = useRouter();
const {
ready,
authenticated,
user,
logout,
linkEmail,
linkWallet,
unlinkEmail,
linkPhone,
unlinkPhone,
unlinkWallet,
linkGoogle,
unlinkGoogle,
linkTwitter,
unlinkTwitter,
linkDiscord,
unlinkDiscord,
} = usePrivy();
useEffect(() => {
if (ready && !authenticated) {
router.push("/");
}
}, [ready, authenticated, router]);
const numAccounts = user?.linkedAccounts?.length || 0;
const canRemoveAccount = numAccounts > 1;
const email = user?.email;
const phone = user?.phone;
const wallet = user?.wallet;
const googleSubject = user?.google?.subject || null;
const twitterSubject = user?.twitter?.subject || null;
const discordSubject = user?.discord?.subject || null;
return (
<>
<Head>
<title>Account Settings</title>
</Head>
<main className="flex flex-col min-h-screen px-4 sm:px-20 py-6 sm:py-10 bg-black text-white">
{ready && authenticated ? (
<>
<div className="flex flex-row justify-between">
<h1 className="text-2xl font-semibold text-white">Welcome Warlock,</h1>
<button
onClick={logout}
className="text-sm bg-red-500 hover:bg-red-700 py-2 px-4 rounded-md text-violet-100"
>
Logout
</button>
</div>
<div className="items-center flex flex-wrap justify-center p-20">
<h1 className="text-4xl">$0.1</h1>
</div>
<div className="mt-12 flex gap-4 flex-wrap">
{twitterSubject ? (
<button
onClick={() => {
unlinkTwitter(twitterSubject);
}}
className="text-sm border border-violet-600 hover:border-violet-700 py-2 px-4 rounded-md text-violet-600 hover:text-violet-700 disabled:border-gray-500 disabled:text-gray-500 hover:disabled:text-gray-500"
disabled={!canRemoveAccount}
>
Unlink Twitter
</button>
) : (
<button
className="text-sm bg-violet-600 hover:bg-violet-700 py-2 px-4 rounded-md text-white"
onClick={() => {
linkTwitter();
}}
>
Link Twitter
</button>
)}
{discordSubject ? (
<button
onClick={() => {
unlinkDiscord(discordSubject);
}}
className="text-sm border border-violet-600 hover:border-violet-700 py-2 px-4 rounded-md text-violet-600 hover:text-violet-700 disabled:border-gray-500 disabled:text-gray-500 hover:disabled:text-gray-500"
disabled={!canRemoveAccount}
>
Unlink Discord
</button>
) : (
<button
className="text-sm bg-violet-600 hover:bg-violet-700 py-2 px-4 rounded-md text-white"
onClick={() => {
linkDiscord();
}}
>
Link Discord
</button>
)}
{wallet ? (
<button
onClick={() => {
unlinkWallet(wallet.address);
}}
className="text-sm border border-violet-600 hover:border-violet-700 py-2 px-4 rounded-md text-violet-600 hover:text-violet-700 disabled:border-gray-500 disabled:text-gray-500 hover:disabled:text-gray-500"
disabled={!canRemoveAccount}
>
Unlink wallet
</button>
) : (
<button
onClick={linkWallet}
className="text-sm bg-violet-600 hover:bg-violet-700 py-2 px-4 rounded-md text-white border-none"
>
Connect wallet
</button>
)}
</div>
</>
) : null}
</main>
</>
);
}

64
pages/index.tsx Normal file
View File

@@ -0,0 +1,64 @@
import Portal from "../components/graphics/portal";
import { useLogin } from "@privy-io/react-auth";
import { PrivyClient } from "@privy-io/server-auth";
import { GetServerSideProps } from "next";
import Head from "next/head";
import { useRouter } from "next/router";
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
const cookieAuthToken = req.cookies["privy-token"];
// If no cookie is found, skip any further checks
if (!cookieAuthToken) return { props: {} };
const PRIVY_APP_ID = process.env.NEXT_PUBLIC_PRIVY_APP_ID;
const PRIVY_APP_SECRET = process.env.PRIVY_APP_SECRET;
const client = new PrivyClient(PRIVY_APP_ID!, PRIVY_APP_SECRET!);
try {
const claims = await client.verifyAuthToken(cookieAuthToken);
// Use this result to pass props to a page for server rendering or to drive redirects!
// ref https://nextjs.org/docs/pages/api-reference/functions/get-server-side-props
console.log({ claims });
return {
props: {},
redirect: { destination: "/dashboard", permanent: false },
};
} catch (error) {
return { props: {} };
}
};
export default function LoginPage() {
const router = useRouter();
const { login } = useLogin({
onComplete: () => router.push("/dashboard"),
});
return (
<>
<Head>
<title>Login · Privy</title>
</Head>
<main className="flex min-h-screen min-w-full">
<div className="flex bg-black flex-1 p-6 justify-center items-center">
<div>
<div>
{/* <Portal style={{ maxWidth: "100%", height: "auto" }} /> */}
</div>
<div className="mt-6 flex justify-center text-center">
<button
className="bg-violet-600 hover:bg-violet-700 py-3 px-6 text-white rounded-lg"
onClick={login}
>
Log in
</button>
</div>
</div>
</div>
</main>
</>
);
}