はじめまして、NextAuthさん、Rainbowkitさん
最終的にはRainbowKitとNextAuthで認証機能を実装したいが、そもそも素の認証機能すら作ったことがないので一歩ずつすすめることにした。そもそも認証とは?みたいな話から始めてもいいかもしれないが、深追いしすぎ防止のためにハンズオンから入る。
Tutorialの最初にこれ見ろって
色々見たけど結局このリポジトリを探検するのが良さそう
Built on top of wagmi and ethers
wagmiさん。なんかうまいことWalletを使えるようにしてくれてるらしい。こいつもethers依存。
ethersさん。 Ethereum Blockchain と対話するただひとつの掃除機。
Exampleはこれを参考にしているらしい
unstable_getServerSession
も想定してくれてる。完璧じゃない?
siweはsign in with ethの略
Providersがこいつでラップされてる
翻訳
Credentialsプロバイダは、ユーザー名とパスワード、ドメイン、二要素認証やハードウェアデバイス(YubiKey U2F / FIDOなど)など、任意の資格情報を使ってサインインすることを可能にします。
これは、ユーザーを認証する必要がある既存のシステムがある場合の使用をサポートすることを目的としています。
この方法で認証されたユーザはデータベースに保存されないため、JSON Web Tokens がセッションで有効な場合にのみ Credentials プロバイダを使用できるという制約があります。
ユーザー名とパスワードがDBに保存されないなら誰がデータ保存するの?
とりあえずこいつが本丸
const providers = [
CredentialsProvider({
async authorize(credentials) {
try {
const siwe = new SiweMessage(
JSON.parse(credentials?.message || '{}')
);
const nextAuthUrl =
process.env.NEXTAUTH_URL ||
(process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: null);
if (!nextAuthUrl) {
return null;
}
const nextAuthHost = new URL(nextAuthUrl).host;
if (siwe.domain !== nextAuthHost) {
return null;
}
if (siwe.nonce !== (await getCsrfToken({ req }))) {
return null;
}
await siwe.validate(credentials?.signature || '');
return {
id: siwe.address,
};
} catch (e) {
return null;
}
},
credentials: {
message: {
label: 'Message',
placeholder: '0x0',
type: 'text',
},
signature: {
label: 'Signature',
placeholder: '0x0',
type: 'text',
},
},
name: 'Ethereum',
}),
];
下のここらへんはForm定義っぽい
credentials: {
message: {
label: 'Message',
placeholder: '0x0',
type: 'text',
},
signature: {
label: 'Signature',
placeholder: '0x0',
type: 'text',
},
},
name: 'Ethereum',
ゆーて認証部分これだけか
await siwe.validate(credentials?.signature || '');
return {
id: siwe.address,
};
signatureが妥当であればidを発行する
signature, siwe.address
ってなんなんだろ
// Hide Sign-In with Ethereum from default sign page
if (isDefaultSigninPage) {
authOptions.providers.pop();
}
siweの公式によれば
We're going to now add the provider that will handle the message validation. Since it's not possible to sign in using the default page, the original provider should be removed from the list of providers before rendering. Modify pages/api/auth/[...nextauth].ts with the following:
とのこと
addressはブロックチェーン上の識別子らしい
signatureとかtokenのイメージが湧いてない
電子署名とは、文書やメッセージなどのデータの真正性を証明するために付加される、短い暗号データ。作成者を証明し、改竄やすり替えが行われていないことを保証する。欧米で紙の文書に記されるサイン(signature)に似た働きをするためこのように呼ばれる。
tokenはかなり抽象的だ
トークンは、プログラミングコード上でコードを構成する最小単位の要素であり、変数名、予約語、演算子、定数などが該当します。
またトークンが持つ「意味を持つ最小単位、証拠」という意味を起点に、近年は複数の意味を持つ多義語となりました。まず、情報セキュリティの認証で一度だけ使える「ワンタイムパスワード」を生成するための機器やソフトウェアなどのツール全般がトークンと呼ばれます。こちらは混同を避けるために「セキュリティトークン」と呼ばれることもありますが、意味は同じです。
そして、ブロックチェーン技術を利用して発行された仮想通貨などのデータをトークンと呼ぶこともあります。また、ブロックチェーン技術が使われており、デジタルアートなどで活用が進んでいるNFT(非代替性トークン)もトークンの一種です。
要するにこれは、
- Ethの人かどうか確かめて
- そうであるならそのデータをアプリ上でもそのまま認証に使う
ってことかな
await siwe.validate(credentials?.signature || '');
return {
id: siwe.address,
};
Google認証とかとはモノが違うと思うんだけどうまく説明できない
はてこの返されたidはどこでどうなる
単純にこういうことか?
user = { id: 'address' }
これを使ってFirestoreにユーザーデータ登録したいんだけどな、多分
_app.tsxの方はwagmiとRainbowKitがよしなにやってくれすぎてて追いきれない
import '../styles/global.css';
import '@rainbow-me/rainbowkit/styles.css';
import type { AppProps } from 'next/app';
import {
RainbowKitProvider,
getDefaultWallets,
connectorsForWallets,
} from '@rainbow-me/rainbowkit';
import {
argentWallet,
trustWallet,
ledgerWallet,
} from '@rainbow-me/rainbowkit/wallets';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import { mainnet, polygon, optimism, arbitrum, goerli } from 'wagmi/chains';
import { alchemyProvider } from 'wagmi/providers/alchemy';
import { publicProvider } from 'wagmi/providers/public';
import { SessionProvider } from 'next-auth/react';
import {
RainbowKitSiweNextAuthProvider,
GetSiweMessageOptions,
} from '@rainbow-me/rainbowkit-siwe-next-auth';
const { chains, provider, webSocketProvider } = configureChains(
[
mainnet,
polygon,
optimism,
arbitrum,
...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true' ? [goerli] : []),
],
[
alchemyProvider({ apiKey: '_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC' }),
publicProvider(),
]
);
const { wallets } = getDefaultWallets({
appName: 'RainbowKit demo',
chains,
});
const demoAppInfo = {
appName: 'Rainbowkit Demo',
};
const connectors = connectorsForWallets([
...wallets,
{
groupName: 'Other',
wallets: [
argentWallet({ chains }),
trustWallet({ chains }),
ledgerWallet({ chains }),
],
},
]);
const wagmiClient = createClient({
autoConnect: true,
connectors,
provider,
webSocketProvider,
});
const getSiweMessageOptions: GetSiweMessageOptions = () => ({
statement: 'Sign in to the RainbowKit + SIWE example app',
});
export default function App({ Component, pageProps }: AppProps) {
return (
<SessionProvider refetchInterval={0} session={pageProps.session}>
<WagmiConfig client={wagmiClient}>
<RainbowKitSiweNextAuthProvider
getSiweMessageOptions={getSiweMessageOptions}
>
<RainbowKitProvider appInfo={demoAppInfo} chains={chains}>
<Component {...pageProps} />
</RainbowKitProvider>
</RainbowKitSiweNextAuthProvider>
</WagmiConfig>
</SessionProvider>
);
}
手元で動かしたらerrorでた
consoleにはerror - uncaughtException: Error: unable to get local issuer certificate
とあって、SSL証明書が見つからないらしい
applicationの問題ではないので一旦無視
Connect Walletのボタンを押すとこのようなモーダルが出てくる
ボタンはkitのものらしい import { ConnectButton } from '@rainbow-me/rainbowkit';
MetaMaskを選択するとこんなかんじ。
なんかもう全部やってくれそう
ちなみにMobileはこんな見た目
Connected?
Send messageを押すとSignature requestの画面に。
Signボタンを押すも失敗しているみたい
原因がわからない....