mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-16 08:08:56 -05:00
97 lines
2.5 KiB
TypeScript
97 lines
2.5 KiB
TypeScript
import { AuthOptions, getServerSession } from "next-auth"
|
|
import CredentialsProvider from "next-auth/providers/credentials";
|
|
import { createAppClient, viemConnector } from "@farcaster/auth-client";
|
|
|
|
declare module "next-auth" {
|
|
interface Session {
|
|
user: {
|
|
fid: number;
|
|
};
|
|
}
|
|
}
|
|
|
|
function getDomainFromUrl(urlString: string | undefined): string {
|
|
if (!urlString) {
|
|
console.warn('NEXTAUTH_URL is not set, using localhost:3000 as fallback');
|
|
return 'localhost:3000';
|
|
}
|
|
try {
|
|
const url = new URL(urlString);
|
|
return url.hostname;
|
|
} catch (error) {
|
|
console.error('Invalid NEXTAUTH_URL:', urlString, error);
|
|
console.warn('Using localhost:3000 as fallback');
|
|
return 'localhost:3000';
|
|
}
|
|
}
|
|
|
|
export const authOptions: AuthOptions = {
|
|
// Configure one or more authentication providers
|
|
providers: [
|
|
CredentialsProvider({
|
|
name: "Sign in with Farcaster",
|
|
credentials: {
|
|
message: {
|
|
label: "Message",
|
|
type: "text",
|
|
placeholder: "0x0",
|
|
},
|
|
signature: {
|
|
label: "Signature",
|
|
type: "text",
|
|
placeholder: "0x0",
|
|
},
|
|
// In a production app with a server, these should be fetched from
|
|
// your Farcaster data indexer rather than have them accepted as part
|
|
// of credentials.
|
|
// question: should these natively use the Neynar API?
|
|
name: {
|
|
label: "Name",
|
|
type: "text",
|
|
placeholder: "0x0",
|
|
},
|
|
pfp: {
|
|
label: "Pfp",
|
|
type: "text",
|
|
placeholder: "0x0",
|
|
},
|
|
},
|
|
async authorize(credentials, req) {
|
|
const csrfToken = req?.body?.csrfToken;
|
|
const appClient = createAppClient({
|
|
ethereum: viemConnector(),
|
|
});
|
|
|
|
const domain = getDomainFromUrl(process.env.NEXTAUTH_URL);
|
|
console.log('Using domain for auth:', domain);
|
|
|
|
const verifyResponse = await appClient.verifySignInMessage({
|
|
message: credentials?.message as string,
|
|
signature: credentials?.signature as `0x${string}`,
|
|
domain,
|
|
nonce: csrfToken,
|
|
});
|
|
const { success, fid } = verifyResponse;
|
|
|
|
if (!success) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
id: fid.toString(),
|
|
};
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
session: async ({ session, token }) => {
|
|
if (session?.user) {
|
|
session.user.fid = parseInt(token.sub ?? '');
|
|
}
|
|
return session;
|
|
},
|
|
}
|
|
}
|
|
|
|
export const getSession = () => getServerSession(authOptions)
|