Magicを使ってNext.jsアプリにパスワードレス認証を追加する
Magicというサービスを使うことで、簡単にパスワードレス / WebAuthn またはソーシャルログイン機能を実装することができます。 今回はNext.jsをベースに作ってみました。 Step1: Magicアカウント […]
目次
Magicというサービスを使うことで、簡単にパスワードレス / WebAuthn またはソーシャルログイン機能を実装することができます。
今回はNext.jsをベースに作ってみました。
Step1: Magicアカウントを作成する
https://dashboard.magic.link/signup からアカウントを作成します。ここもパスワードレスになっているあたり流石です。
認証メールを確認すると自動でリダイレクトされますので、開いたページはそのままにしておきましょう。
届いたメールの「Log in to Magic」をクリックします。
ログイン操作していた画面の方が自動でダッシュボードに切り替わります。
Step2: APIキーの取得
First Appというアプリが既に作成されていますので、Get Started
をクリックしましょう。
APIキーが2つあります。ReactなどのフロントエンドではPublishable Key
だけを使うようにしましょう。
万が一お漏らししてしまった場合などには、API Keys
タブから再生成します。
Step3: Next.jsのアプリを作ってMagicを組み込む
続いてアプリを作っていきます。
個人的にTypeScriptでやりたい派なので、自前のスターターをインポートして作りました。
$ npx create-next-app magic-example --example "https://github.com/wpkyoto/nextjs-starter-typescript/tree/main"
$ cd magic-example
フロントエンドでは、magic-sdkをインストールします。
$ yarn add magic-sdk
あとはImportして、クラスをnewしてやるだけです。
import { Magic } from 'magic-sdk'
const client = new Magic('pk_test_xxxxx')
ちなみに、以下のようなContextを作っておくとuseMagic
を使えば簡単にクライアントを取り出せて便利だと思います。
const MagicContext = createContext<{
magicClient?: Magic;
}>({})
const MagicProvider: FC<PropsWithChildren<{
publishableAPIKey: string;
}>> = ({publishableAPIKey, children}) => {
const magic = useMemo(() => {
if (typeof window === 'undefined') return;
return new Magic(publishableAPIKey)
}, [publishableAPIKey])
return (
<MagicContext.Provider value={{
magicClient: magic,
}}>
{children}
</MagicContext.Provider>
)
}
const useMagic = () => useContext(MagicContext)
tips: SSR / SSGではwindow
に注意
内部でwindowプロパティを使用しているらしく、Next.jsやGatsbyなどNode.jsでレンダリングするケースだと ReferenceError: window is not defined
エラーを踏みます。
もしこれらのフレームワークを使う場合は、windowがundefinedじゃ無い事を確認するようにしましょう。
Step4: ログイン状況の確認
Magic.user.isLoggedIn
でログイン状況を確認できます。
const {magicClient} = useMagic()
const [isLogin, setLoginStatus] = useState(false)
useEffect(() => {
if (!magicClient) return;
(async() => {
const result = await magicClient.user.isLoggedIn()
setLoginStatus(result)
})()
}, [magicClient, setLoginStatus])
上のケースでは、以下のように判別できます。
magicClient
がundefinedの場合、まだステータスチェックが終わっていませんmagicClient
がundefinedではなく、isLogin
がfalse
の場合、ユーザーはログインしていない状態ですmagicClient
がundefinedではなく、isLogin
がtrue
の場合、ユーザーはログインしている状態です
あとはこれらの条件に応じてルーティングや表示を制御します。
Step5: Eメールでログインまたはアカウント作成
ユーザー作成とログインはどちらも同じメソッドを使います。
以下のサンプルは、ログイン・アカウント作成をハンドルするフックを提供しています。
const useLoginByMagic = () => {
const [errorMessage, setErrorMessage] = useState<string>(undefined)
const [email, setEmail] = useState('')
const [token, setToken] = useState<string>(undefined)
const {magicClient} = useMagic()
const handleLogin = useCallback(async() => {
if (!magicClient) throw new Error('We have to initilize the magic client before use the hook. Or the Client can not use in Server Side process.')
try {
if (!email) throw new Error('Email is required')
setErrorMessage(undefined)
const magicAuthToken = await magicClient.auth.loginWithMagicLink({
email
})
setToken(magicAuthToken)
} catch (e) {
console.error(e)
setErrorMessage(e.message)
}
}, [email, magicClient, setToken, setErrorMessage])
return {
email,
setEmail,
token,
errorMessage,
handleLogin,
}
}
setEmail
をinputタグなどに使い、メールアドレスの入力を受け付けます。そして handleLogin
を呼び出してMagicのログイン・アカウント作成処理を実行しています。あとはtoken
とerrorMessage
の値をuseEffect
などで監視して結果をチェックしてやりましょう。
もしtoken
がundefined
じゃなくなっていれば、ログイン処理は成功したと考えられます。また、errorMessage
に値がある場合は、何かしらのエラーが起きていると推察できます。
まとめ:devにとってもユーザーにとっても簡単。ただ安くは無い
Magicを使うことで、開発側も利用者側もかなり手軽で簡単に会員限定コンテンツを作成・アクセスできます。ただし、フリープランが100アクティブユーザーまでで、101〜500ユーザーは月額35USDとAuth0やAWS Cognito User Pools、Firebaseあたりよりは高価です。
この金額がペイできる価格感のアプリを作る予定か、100ユーザーに収まるクローズドなアプリを想定している場合にはMagicを使うのが良さそうかなと感じました。
もし金額が厳しいという場合は、Auth0 やCognitoなどでも頑張ればWebAuthnやパスワードレス認証を実装できますので、DIYしていきましょう。