📨
Supabase+Remixで認証メールからクリックでログイン
GitHub
ソースをGitHubに上げました。
目標
前回は最低限の認証を実装しました。
今回はより使えるものにしていこうと思います。Supabaseの認証メールからリンクに飛んだ際、ボタンをクリックしてログインできるようにします。Supabaseで認証された後の挙動
前回の実装ではSupabaseからの認証メールでリンクに飛んだ際、またログインするためにメールアドレスとパスワードを入力する必要がありました。しかし、それでは手間がかかります。URLの中にaccess_tokenが入っているのでそれを利用させてもらいます。このaccess_tokenなんですが、そのままではサーバーから見れません。URLの#以下はhashやfragmentと呼ばれるようですが、サーバーには送られないみたいですね。
なので、ブラウザからhashをサーバーに送ります。Remixのactionを使ってsecretページに入れるようにしてみます。その前に、Supabaseにてメールのリンク先を変更します。Authentication → Setting → Site URLにてhttp://localhost:8787/authenticated と入力し、Saveします。
コードの追加
新しくファイルを作成します。
app/routes/authenticated.tsx
import { useEffect, useRef } from "react";
import { Form, redirect } from "remix";
import { commitSession, getSession } from "~/session.server";
export const action = async ({ request }: { request: Request }) => {
const form = await request.formData();
const tokenMixture = form.get("access_token")?.toString();
const match = tokenMixture?.match(
/#access_token=(?<access_token>[\w\-]{20,60}\.[\w\-]{100,500}\.[\w\-]{20,60})&/);
if (!match?.groups) {
console.log("トークンが見つかりませんでした。")
return {};
}
const access_token = match?.groups.access_token;
const session = await getSession(request.headers.get("Cookie"));
session.set("access_token", access_token);
return redirect("/secret", {
headers: {
"Set-Cookie": await commitSession(session),
},
});
};
export default function Authenticated() {
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current) {
ref.current.value = location.hash;
}
}, []);
return (
<div>
<h1>登録されました</h1>
<Form method="post">
<input ref={ref} type="hidden" name="access_token" />
<input type="submit" />
</Form>
</div>
);
}
コードの説明です。Locationからhashを取得します。
ハッシュをサーバーに送る際にはinput要素を使いました。useEffectによってフォーム読み込み時にLocationのhashがinput要素に入ります。submitを押すことでサーバーにhashが送信されます。サーバーでは正規表現にてJWTを取り出しています。 JWTはBase64URLが使われているので、/wとハイフンが使用文字で、ドットで各部が区切られます。JWTを取り出したらクッキーに入れてコミットしてリダイレクトしています。
終わりに
hashがサーバーに送られない事実に戸惑いましたが、なんとか実装できてよかったです。
次回はその他の実装をして完成度を上げたいです。現場からは以上です。
Discussion