import Moralis from "moralis";
const TIME = new Date();
const FUTURE = new Date(
TIME.getFullYear(),
TIME.getMonth(),
TIME.getDate() + 7,
TIME.getHours(),
TIME.getMinutes(),
TIME.getSeconds(),
TIME.getMilliseconds()
);
const DOMAIN = process.env.APP_DOMAIN;
const STATEMENT = "Please sign this message to confirm your identity.";
const URI = process.env.NEXTAUTH_URL;
const EXPIRATION_TIME = FUTURE.toISOString();
const NOT_BEFORE = TIME.toISOString();
const TIMEOUT = 60;
export default async function handler(req, res) {
const { address, chain, network } = req.body;
await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
try {
if (!DOMAIN || !URI) {
throw new Error("Please add APP_DOMAIN in the .env.local");
}
const message = await Moralis.Auth.requestMessage({
address: address,
solNetwork: chain,
network: network,
domain: DOMAIN,
statement: STATEMENT,
uri: URI,
expirationTime: EXPIRATION_TIME,
timeout: TIMEOUT,
notBefore: NOT_BEFORE,
});
res.status(200).json(message);
} catch (error) {
console.log(error);
res.status(400).json({ error });
}
}
import { useEffect, useState } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
require("@solana/wallet-adapter-react-ui/styles.css");
import base58 from "bs58";
import { apiPost } from "../../utils/apiPost";
import { signIn } from "next-auth/react";
export default function WalletAdaptor() {
const { publicKey, signMessage } = useWallet();
const [signed, setSigned] = useState(false);
const signCustomMessage = async () => {
const address = publicKey.toBase58();
const chain = "mainnet";
const account = {
address: address,
chain: chain,
network: "solana",
};
// const message = "Sign to provide access to app";
const { message } = await apiPost("api/auth/request-message", account);
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await signMessage(encodedMessage, "utf8");
setSigned(true);
const signature = base58.encode(signedMessage);
try {
await signIn("credentials", {
message,
signature,
redirect: false,
});
} catch (e) {
console.log(e);
return;
}
};
useEffect(() => {
publicKey ? !signed && signCustomMessage() : setSigned(false);
}, [publicKey]);
return (
<>
<WalletMultiButton />
</>
);
}
import CredentialsProvider from "next-auth/providers/credentials";
import NextAuth from "next-auth";
import Moralis from "moralis";
export default NextAuth({
providers: [
CredentialsProvider({
name: "MoralisAuth",
credentials: {
message: {
label: "Message",
type: "text",
placeholder: "0x0",
},
signature: {
label: "Signature",
type: "text",
placeholder: "0x0",
},
},
async authorize(credentials) {
try {
const { message, signature } = credentials;
await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
const { address, network, profileId, expirationTime } = (
await Moralis.Auth.verify({ message, signature, network: "solana" })
).raw;
const user = {
address,
network,
profileId,
expirationTime,
};
return user;
} catch (e) {
console.error(e);
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user }) {
user && (token.user = user);
return token;
},
async session({ session, token }) {
session.expires = token.user.expirationTime;
session.user = token.user;
return session;
},
},
session: {
strategy: "jwt",
},
});
I have this code to allow new users to validate and verify with their solana wallets. The signtature pops up for signing and it is possibel to sign, but I always get further linked to http://localhost:3000/api/auth/error. I used the code from a youtubevideo: https://www.youtube.com/watch?v=0fuevxebv_E
Thanks for any helping hand.