Closed Pruxis closed 2 years ago
I have to admit, I have zero knowledge of what Web 3 even is, so probably not in the short term.
Right now I can only see its a controversial topic on Twitter amongst developers.
I have no idea what that would mean for next-auth, what would be required/expected from us, etc.
For starters, could you link to some useful resources that starts explaining at absolute zero? What is the goal of Web 3, what is a wallet, etc?
Resources:
Here's a dated but simple repository of how wallet authentication would look like: https://github.com/vanbexlabs/web3-auth
A large portion of the web3 community currently builds dApps that mainly run on-top of blockchain technologies. However I believe that there's more and more need to combine those applications with "web2" backends and that there's very few proper resources that use best practices to facilitate this.
It would be a long way for me to advocate for best practices of Web3 auth, as I barely started out with OAuth, and there is much to learn!
I also don't know what dApps are and have never really looked into blockchain either.
As this project is mainly maintained by me, I am afraid I won't have the capacity for a very long time to look into this.
But I encourage you to dive into the source code, learn about it, and see if you can get what you want working, and see how that goes.
I would like to learn what are the true benefits of this approach? Who is the userbase? Why isn't the current way working for them?
Only relying on the limited knowledge of my social media exposure, I jump on all of this a bit skeptical, please forgive me for that.
@balazsorban44 I'll make a sample with CredentialsProviders, perhaps some documentation or a blogpost could help those in need of Web3 Auth. Then there is no commitment and deep knowledge required on next-auth.
Let's see how that goes! if not complicated and we can just add this as an extension of the Credentials provider as it's standalone provider, that would be great.
So I made a sample real quick, I'll find some time tomorrow perhaps write a blogpost about it. This allows you to use an etherium wallet to authenticate yourself.
The really cool thing about next-auth is the csrfToken that it's a unique token for your session so you can actuality use it as signing mechanism to verify that the connected wallet is properly authenticated :)
Frontend sample:
import Web3 from 'web3';
import { useCallback, useEffect, useState } from 'react';
export let web3: Web3 | undefined;
interface State {
started: boolean;
connected: boolean;
}
interface MetamaskButtonProps {
csrfToken: string;
}
const LoginButton = (props: MetamaskButtonProps) => {
const [state, setState] = useState<State>({ started: false, connected: false });
const checkConnectedWallet = useCallback(() => {
web3?.eth?.getAccounts((err, accounts) => {
if (err != null) return setState(s => ({ ...s, started: true }));
else if (!accounts.length) return setState(s => ({ ...s, started: true }));
return setState({ started: true, connected: true });
});
}, [setState]);
const signInWithWallet = useCallback(async () => {
window.ethereum
?.request({ method: 'eth_requestAccounts' })
// Returns an array of web3 addresses.
.then(async (accounts: string[]) => {
try {
const signedCsrf = await window.ethereum?.request({
method: 'personal_sign',
params: ['0x' + props.csrfToken, accounts[0]],
});
const form = document.createElement('form');
form.setAttribute('action', '/api/auth/callback/credentials');
form.setAttribute('method', 'POST');
// Add csrfToken
const csfrInput = document.createElement('input');
csfrInput.setAttribute('name', 'csrfToken');
csfrInput.setAttribute('value', props.csrfToken);
form.appendChild(csfrInput);
const web3Address = document.createElement('input');
web3Address.setAttribute('name', 'address');
web3Address.setAttribute('value', accounts[0]);
form.appendChild(web3Address);
const signature = document.createElement('input');
signature.setAttribute('name', 'signature');
signature.setAttribute('value', signedCsrf);
form.appendChild(signature);
document.getElementsByTagName('body')[0].appendChild(form);
form.submit();
console.log('Submitted form');
} catch (e) {}
});
}, [props.csrfToken]);
useEffect(() => {
web3 = new Web3(window.ethereum as any);
checkConnectedWallet();
}, []);
if (!state.started || typeof window === 'undefined') return null;
return (
<div className="flex flex-col w-full h-full p-4">
<button className="p-2 border-2 border-black rounded-sm max-w-sm" onClick={signInWithWallet}>
Authenticate with Metamask
</button>
</div>
);
};
export default LoginButton;
Provider:
CredentialsProvider({
name: 'Web3',
authorize: async (credentials: any, f) => {
const tokenBuffer = ethUtil.toBuffer('0x' + credentials.csrfToken);
const tokenHash = ethUtil.hashPersonalMessage(tokenBuffer);
const signatureParams = ethUtil.fromRpcSig(credentials.signature);
const publicKey = ethUtil.ecrecover(tokenHash, signatureParams.v, signatureParams.r, signatureParams.s);
const addressBuffer = ethUtil.publicToAddress(publicKey);
const address = ethUtil.bufferToHex(addressBuffer);
if (address.toLowerCase() !== credentials.address?.toLowerCase()) return null;
// Replace with db stuff here.
const user = { id: 1, name: 'J Smith', email: 'jsmith@example.com', ...credentials };
return user;
},
type: 'credentials',
credentials: {},
})
Hello,
I'm interested in helping, but I know little about the blockchain, do you have any updates to the above code?
I'm working on it, but I can't successfully pass ethUtil.toBuffer()
I'm using 'ethereumjs-util' can you specify the package you used?
================= Update question:
replace this line:
const tokenBuffer = ethUtil.toBuffer('0x' + credentials.csrfToken);
for this:
const tokenBuffer = Buffer.from('0x' + credentials.csrfToken);
now it worked for me
@kellvembarbosa I got some time this weekend to finalise this sample, as there's a bug when you first connect your wallet it doesnt properly succeed, however after refresh and resigning the token it does which is quite odd
I used ethereumjs-util@7.1.3 to use the sample here.
@Pruxis it's true, I also realized that, in the code above, the user will need to perform two actions, As I understand it, one action is to 'activate' access to the account and then make the personal sign…
Thanks for your time, I'll keep an eye out when you update the code, thanks again.
@Pruxis really looking forward to your blog post. Thanks for the example code!
@Pruxis love to see the complete example! I can't manage to make it work. Thanks a lot!
@Pruxis +1
Thanks @Pruxis. Love to see this to be integrated. web3 social platforms will need this.
Hi,
This makes the code cleaner and easier to read.
First import:
import { utils } from "ethers"
and
CredentialsProvider({
name: 'Web3',
authorize: async (credentials: any, f) => {
const nonce = '0x' + credentials.csrfToken;
const address = utils.verifyMessage(nonce, credentials.signature);
if (address.toLowerCase() !== credentials.address?.toLowerCase()) return null;
// create newUser or return existent user
const user = { id: 1, name: 'J Smith', email: 'jsmith@example.com', ...credentials };
return user;
},
type: 'credentials',
credentials: {},
}) ´´´
@Pruxis Have you made any headway with this bug?
It looks like the csrfToken lags behind?
I have a page connect.js
with a call to getServerSideProps
:
export async function getServerSideProps(context) {
return {
props: {
csrfToken: await getCsrfToken(context),
},
}
}
The token values change in the sequence:
1) Load http://localhost:3000/connect front-end
2) getServerSideProps -> props.csrfToken: 011bdcd...
3) signInWithWallet -> props. csrfToken: 011bdcd...
4)Click signInWithWallet
button and sign MetaMask signature request
From [...nextauth].js
5) From CredentialsProvider authorize function -> credentials.csrfToken: 6c3263b.....
When you refresh http://localhost:3000/connect
5)signInWithWallet -> props. csrfToken: 6c3263b...
I replaced:
const form = document.createElement('form');
...
console.log('Submitted form');
with
signIn("web3", {callbackUrl: '/', address: accounts[0], csrfToken: props.csrfToken, signature: signedCsrf})
because I was getting an error about POST to /api/auth/callback/credentials
not being supported.
@balazsorban44 Would you have any idea why the token value passed into the signin
method is not passed into the authorize
method?
Thank you for any suggestion.
Edit: I also noticed that every time you refresh before your first MetaMask connect/signature attempt you will get a new/different csrfToken each time, while after your first attempt you will get a new csrfToken that remains the same after each refresh.
I have been using web3-react
(https://github.com/NoahZinsmeister/web3-react) with Next.js successfully. Maybe we can think of an integration?
Sign-In with Ethereum could be a good option. I put together a guide for the wagmi
library here.
I'm no longer active in the Web3 space, therefore I'll close this PR, if anyone else wants to pick this up feel free to re-open
Sign-In with Ethereum could be a good option. I put together a guide for the
wagmi
library here.
I'm using wagmi so I'd love to see it integrated!
I'd like to see this re-opened. To answer @balazsorban44's questions:
I would like to learn what are the true benefits of this approach? Who is the userbase? Why isn't the current way working for them?
The userbase is crypto adopters who value decentralization. With current next auth providers there's a single company who is the source of authn/authz. At any time and for any reason they could shut down, ban you, get hacked, etc...
With a web3 provider there would be no need to register with such a company, all you would need to do is generate a new wallet address which is free and available to anyone through a variety of means. This address can then be used to login to many different web3 "dApps" (decentralized apps). It's basically a public / private key system, where you have a private key that can be used to verify you own the public key address.
I think a few potential snags would be there is no OAuth involved and there would be no "profile" fields like username, image or email.
Hello guys i've wrapped a web3 auth web app with next auth, maybe this can helpfull
The SIWE team published this guide that uses next-auth
and wagmi
.
So I made a sample real quick, I'll find some time tomorrow perhaps write a blogpost about it. This allows you to use an etherium wallet to authenticate yourself.
The really cool thing about next-auth is the csrfToken that it's a unique token for your session so you can actuality use it as signing mechanism to verify that the connected wallet is properly authenticated :)
Frontend sample:
import Web3 from 'web3'; import { useCallback, useEffect, useState } from 'react'; export let web3: Web3 | undefined; interface State { started: boolean; connected: boolean; } interface MetamaskButtonProps { csrfToken: string; } const LoginButton = (props: MetamaskButtonProps) => { const [state, setState] = useState<State>({ started: false, connected: false }); const checkConnectedWallet = useCallback(() => { web3?.eth?.getAccounts((err, accounts) => { if (err != null) return setState(s => ({ ...s, started: true })); else if (!accounts.length) return setState(s => ({ ...s, started: true })); return setState({ started: true, connected: true }); }); }, [setState]); const signInWithWallet = useCallback(async () => { window.ethereum ?.request({ method: 'eth_requestAccounts' }) // Returns an array of web3 addresses. .then(async (accounts: string[]) => { try { const signedCsrf = await window.ethereum?.request({ method: 'personal_sign', params: ['0x' + props.csrfToken, accounts[0]], }); const form = document.createElement('form'); form.setAttribute('action', '/api/auth/callback/credentials'); form.setAttribute('method', 'POST'); // Add csrfToken const csfrInput = document.createElement('input'); csfrInput.setAttribute('name', 'csrfToken'); csfrInput.setAttribute('value', props.csrfToken); form.appendChild(csfrInput); const web3Address = document.createElement('input'); web3Address.setAttribute('name', 'address'); web3Address.setAttribute('value', accounts[0]); form.appendChild(web3Address); const signature = document.createElement('input'); signature.setAttribute('name', 'signature'); signature.setAttribute('value', signedCsrf); form.appendChild(signature); document.getElementsByTagName('body')[0].appendChild(form); form.submit(); console.log('Submitted form'); } catch (e) {} }); }, [props.csrfToken]); useEffect(() => { web3 = new Web3(window.ethereum as any); checkConnectedWallet(); }, []); if (!state.started || typeof window === 'undefined') return null; return ( <div className="flex flex-col w-full h-full p-4"> <button className="p-2 border-2 border-black rounded-sm max-w-sm" onClick={signInWithWallet}> Authenticate with Metamask </button> </div> ); }; export default LoginButton;
Provider:
CredentialsProvider({ name: 'Web3', authorize: async (credentials: any, f) => { const tokenBuffer = ethUtil.toBuffer('0x' + credentials.csrfToken); const tokenHash = ethUtil.hashPersonalMessage(tokenBuffer); const signatureParams = ethUtil.fromRpcSig(credentials.signature); const publicKey = ethUtil.ecrecover(tokenHash, signatureParams.v, signatureParams.r, signatureParams.s); const addressBuffer = ethUtil.publicToAddress(publicKey); const address = ethUtil.bufferToHex(addressBuffer); if (address.toLowerCase() !== credentials.address?.toLowerCase()) return null; // Replace with db stuff here. const user = { id: 1, name: 'J Smith', email: 'jsmith@example.com', ...credentials }; return user; }, type: 'credentials', credentials: {}, })
https://twitter.com/JonBry_0xDreamr/status/1576992188692852737
tipped you some BAT for contributing
Anyone had any luck with Web3Auth as a Provider? Would it follow the same methods as @Pruxis ?
Yes, there's convoluted methods in the wild where we have to use Auth0 as a passthrough, but I would prefer to minimize risk and abstraction layers.
I'm looking to end up with Prisma as an oracle to match Web3Auth (currently experimenting via T3 App)
I think we can now look into pre-built providers for ETH, MATIC, SOL; for now. The providers take care of necessary auth but the user provides his own rpc network (Alchemy, Infura, etc).
Maybe I can look into it if time permits (no commitment).
Why would we need a network for signature verification?
I'm also looking for something stable ❤️🌼
I'm also looking for something stable ❤️🌼
Description 📓
I was wondering if there are plans to include Web3 wallet connections as part of the providers? If this is something that you'd like to scope in next-auth then I'd be happy to contribute.
Contributing 🙌🏽
Yes, I am willing to help implement this feature in a PR