📨

Supabase+Remixで認証メールからクリックでログイン

2022/02/17に公開約2,500字

GitHub

ソースをGitHubに上げました。

https://github.com/smallStall/mail-to-oneClickLoginSRC

目標

前回は最低限の認証を実装しました。

https://zenn.dev/smallstall/articles/631342c94dfc44
今回はより使えるものにしていこうと思います。Supabaseの認証メールからリンクに飛んだ際、ボタンをクリックしてログインできるようにします。

Supabaseで認証された後の挙動

前回の実装ではSupabaseからの認証メールでリンクに飛んだ際、またログインするためにメールアドレスとパスワードを入力する必要がありました。しかし、それでは手間がかかります。URLの中にaccess_tokenが入っているのでそれを利用させてもらいます。このaccess_tokenなんですが、そのままではサーバーから見れません。URLの#以下はhashやfragmentと呼ばれるようですが、サーバーには送られないみたいですね。

https://github.com/remix-run/remix/issues/1574
なので、ブラウザから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を取得します。

https://developer.mozilla.org/ja/docs/Web/API/Location
ハッシュをサーバーに送る際にはinput要素を使いました。useEffectによってフォーム読み込み時にLocationのhashがinput要素に入ります。submitを押すことでサーバーにhashが送信されます。サーバーでは正規表現にてJWTを取り出しています。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
JWTはBase64URLが使われているので、/wとハイフンが使用文字で、ドットで各部が区切られます。
JWTを取り出したらクッキーに入れてコミットしてリダイレクトしています。

終わりに

hashがサーバーに送られない事実に戸惑いましたが、なんとか実装できてよかったです。
次回はその他の実装をして完成度を上げたいです。現場からは以上です。

Discussion

ログインするとコメントできます