-
-
Save tatsuyasusukida/1293733300e689bd5556f4619626130f to your computer and use it in GitHub Desktop.
Web3 login practice
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
COOKIE_NAME="web3login_session" | |
COOKIE_PASSWORD="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from "crypto"; | |
import { NextApiHandler } from "next"; | |
import { withSession } from "../../lib/with-session"; | |
const apiMessage: NextApiHandler = async (req, res) => { | |
const {address} = req.body | |
const nonce = crypto.randomUUID() | |
const issuedAt = new Date().toISOString() | |
const token = {address, nonce, issuedAt} | |
const message = [ | |
`Welcome to XxxxXxx!`, | |
``, | |
`Click to sign in and accept the XxxxXxx Terms of Service: https://xxxxxxx.io/tos`, | |
``, | |
`This request will not trigger a blockchain transaction or cost any gas fees.`, | |
``, | |
`Your authentication status will reset after 24 hours.`, | |
``, | |
`Wallet address:`, | |
`${token.address}`, | |
``, | |
`Nonce:`, | |
`${token.nonce}`, | |
].join('\n') | |
req.session.token = token | |
await req.session.save() | |
res.send({message}) | |
} | |
export default withSession(apiMessage) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from "crypto"; | |
import { ethers } from "ethers"; | |
import { NextApiHandler } from "next"; | |
import { withSession } from "../../lib/with-session"; | |
const apiSignin: NextApiHandler = async (req, res) => { | |
const {token} = req.session | |
if (!token) { | |
res.send({ok: false}) | |
return | |
} | |
const {message, signature} = req.body | |
const lines = message.split('\n') | |
const nonce = lines[lines.length - 1] | |
const digest = ethers.utils.hashMessage(message) | |
const expected = ethers.utils.recoverAddress(digest, signature) | |
const issuedAt = new Date(token.issuedAt).getTime() | |
const ok = token.address === expected | |
&& nonce === token.nonce | |
&& Date.now() < issuedAt + 15 * 60 * 1000 | |
if (ok) { | |
req.session.address = token.address | |
delete req.session.token | |
await req.session.save() | |
} | |
res.send({ok}) | |
} | |
export default withSession(apiSignin) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ethers } from "ethers"; | |
import { NextPage } from "next"; | |
import { useState } from "react"; | |
async function post(url: string, body: object) { | |
const response = await fetch(url, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json; charset=UTF-8', | |
}, | |
body: JSON.stringify(body), | |
}) | |
return await response.json() | |
} | |
declare global { | |
interface Window { | |
ethereum: ethers.providers.ExternalProvider | |
} | |
} | |
const Signin: NextPage = () => { | |
const [isAuthenticated, setIsAuthenticated] = useState(false) | |
const onClick = async () => { | |
const provider = new ethers.providers.Web3Provider(window.ethereum) | |
await provider.send('eth_requestAccounts', []) | |
const signer = provider.getSigner() | |
const address = await signer.getAddress() | |
const {message} = await post('/api/message', {address}) | |
const signature = await signer.signMessage(message) | |
const {ok} = await post('/api/signin', {message, signature}) | |
setIsAuthenticated(ok) | |
} | |
return ( | |
<main> | |
<h1>Web3 Login </h1> | |
<button onClick={onClick}>Signin</button> | |
{isAuthenticated && ( | |
<p>Sign in completed</p> | |
)} | |
</main> | |
) | |
} | |
export default Signin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { withIronSessionApiRoute } from "iron-session/next"; | |
import { NextApiHandler } from "next"; | |
declare module "iron-session" { | |
interface IronSessionData { | |
token?: { | |
address: string | |
nonce: string | |
issuedAt: string | |
} | |
address?: string | |
} | |
} | |
export function withSession(handler: NextApiHandler) { | |
return withIronSessionApiRoute(handler, { | |
cookieName: process.env.COOKIE_NAME as string, | |
password: process.env.COOKIE_PASSWORD as string, | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment