NFLabs. エンジニアブログ

セキュリティやソフトウェア開発に関する情報を発信する技術者向けのブログです。

Hankoによるパスキーを利用してみた

この記事は、NFLaboratories Advent Calendar 2024 6日目の記事*1です。

研究開発部 研究開発担当のda13daです。
今回の記事では、パスワードレスな認証を可能とするパスキーを提供する「Hanko」というサービスをご紹介します。

はじめに

従来のID/パスワード認証は、多くのサービスで採用されてきましたが、フィッシングやパスワード漏洩など、セキュリティ上の課題が指摘されています。このような背景から、セキュリティを強化しつつ、ユーザー体験を向上させるパスワードレス認証が注目を集めています。Hankoは、WebAuthnを基盤としたパスキーを活用することで、簡単かつ安全にパスワードレス認証を導入できるソリューションを提供しています。

パスキーとは

パスキーは、FIDOアライアンスが提唱する次世代認証技術で、WebAuthnを基盤にしています。この技術では、公開鍵暗号方式を利用し、ユーザーがデバイスに保存した秘密鍵を用いて認証を行います。以下は、従来のID/パスワード認証と比較した際の主な特長です。

  • パスワード不要: パスワードを入力・管理する必要がないため、フィッシングやパスワード漏洩のリスクを排除します。
  • 生体認証との連携: 指紋や顔認証を利用することで、直感的でスムーズなユーザー体験を提供します。
  • 幅広い互換性: Windows、macOS、iOS、Androidなど主要なプラットフォームで幅広くサポートされています。

パスキーに関する仕組みは、FIDOアライアンス | How Passkeys Work*2で詳しく説明されています。

Hankoは、このパスキー技術を活用して、セキュリティと利便性を兼ね備えたパスワードレス認証を提供しています。
また、Hankoのメンバーは、パスキーの普及と教育を目的とした情報プラットフォーム*3を運営しています。

Hankoの提供形態

Hankoは、WebAuthnを基盤とした認証機能を以下の2つの方法で提供しています。

OSSプロダクト

Hankoのオープンソース版は、GitHubで公開されており、ユーザーが自身の環境に導入して利用することが可能です。高い自由度を持ち、自社システムや独自要件に応じたカスタマイズが可能です。*4

Hanko Cloud

Hanko Cloudは、IDaaS(Identity as a Service)として提供されるクラウドベースのサービスです。Hankoが用意したログイン画面や管理機能、APIを利用することで、最小限の実装作業で認証機能を組み込むことができます。
*5

今回は、Hanko Cloudを利用した方法についてご紹介します。

利用方法

1. hanko.ioのアカウント作成

まずは、hanko.ioでアカウントを作成します。

2. プロジェクト作成

プロジェクトを作成します。その際、以下の2つのprojectタイプから選択可能です。

projectタイプ

それぞれの特徴を以下に説明します。

projectタイプについて

Hanko
  • 包括的な認証とユーザー管理: パスキーを含む多様なログインオプション(ソーシャルログインや企業向けSSOなど)を提供しています。
  • カスタマイズ可能なUIコンポーネント: 登録、ログイン、ユーザープロファイルのUIを簡単に組み込むことができます。
  • 管理者用ダッシュボード: ユーザー管理や設定を操作可能です。
  • APIベースの柔軟な統合: 全APIは有料プランでのみ利用可能です。また、有料プランで利用可能になるAPIにパスキーAPIが含まれるかについては、Hankoへの確認が必要です。

こららの特徴から、運用や管理の負担を軽減し、長期的なメンテナンスコストを抑制することが可能です。

Passkey Infrastructure
  • APIベースの柔軟な統合

任意のアプリケーションにパスキー機能を組み込むためのAPIを提供しています。

このタイプは、APIベースであることから、それらに対する実装は必要なものの以下のように幅広いユースケースで活用できます。

  • アカウント管理は自前で実装したい
  • 既存のログイン機能を強化したい
  • 特定のページでパスキーを利用したい

3. プロジェクト詳細の入力

プロジェクト名とアプリケーションURLを入力して作成します。このURLは、Hankoを統合するアプリケーションのURLを入力します。このURLは、登録後に変更可能です。

crate project


4. アプリケーションへの統合
公式サイトにフレームワークごとのHanko統合方法が示されています。*6

今回は、公式がGitHubで公開しているサンプルアプリケーション*7を使用します。

公開されているサンプルだとhankoが公開している最新バージョンに対応していなかったため、最新バージョンに対応したrepository*8を用意しました。

結合するためには、.envファイルにHANKO API URLを指定する必要があります。*9

project詳細画面にAPI URLが表示されており、これが先述した値です。
この値をコピーし、.envにその値をセットすることで、サンプルを動作させることが可能です。
上記を設定し、pnpm devで起動します。

サンプルアプリケーションでは、ログインボタンが表示されており、そのボタンを押下し遷移に従うことでアカウントを作成します。

ログイン

アカウント作成時にパスキーの登録が求められます。パスキーの登録をもってアカウント登録が完了します。

パスキーの登録

アカウントの登録が完了すると、パスキーでのログインが可能となります。

ログイン画面
パスキーによるサインイン1
パスキーによるサインイン2


パスキーによるサインインが成功すると、User idなどが表示され、成功したことがわかります。

ログイン後

実装について

ログイン

@teamhanko/hanko-elementsというライブラリからHankoをimportし、環境変数をセットし、hanko-authをレンダリングする以下のようなcomponentを実装し、それを呼び出すような形でHankoが用意するログインUIを利用可能です。

"use client";
 
import { useEffect, useCallback, useState } from "react";
import { useRouter } from "next/navigation";
import { register, Hanko } from "@teamhanko/hanko-elements";
 
const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL;
 
export default function HankoAuth() {
  const router = useRouter();
 
  const [hanko, setHanko] = useState<Hanko>();
 
  useEffect(() => setHanko(new Hanko(hankoApi)), []);
 
  const redirectAfterLogin = useCallback(() => {
    // successfully logged in, redirect to a page in your application
    router.replace("/dashboard");
  }, [router]);
 
  useEffect(
    () =>
      hanko?.onSessionCreated(() => {
        redirectAfterLogin();
      }),
    [hanko, redirectAfterLogin]
  );
 
  useEffect(() => {
    register(hankoApi).catch((error) => {
      // handle error
    });
  }, []);
 
  return <hanko-auth />;
}
ユーザー情報の取得

こちらも同様にライブラリを用い、以下のようにhanko.user.getCurrent()を実行することで、現在ログインしているユーザーの情報が取得可能です。

"use client";

import { useState, useEffect } from "react";
import { Hanko } from "@teamhanko/hanko-elements";

const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL || "";

interface HankoUser {
  id: string;
  email: string;
  loading: boolean;
  error: string | null;
}

export function useUserData(): HankoUser {
  const [hanko, setHanko] = useState<Hanko>();
  const [userState, setUserState] = useState<HankoUser>({
    id: "",
    email: "",
    loading: true,
    error: null,
  });

  useEffect(() => setHanko(new Hanko(hankoApi)), []);

  useEffect(() => {
    hanko?.user
      .getCurrent()
      .then(({ id, email }) => {
        setUserState({ id, email, loading: false, error: null });
      })
      .catch((error) => {
        setUserState((prevState) => ({ ...prevState, loading: false, error }));
      });
  }, [hanko]);

  return userState;
}

パスキーAPIについて

projectタイプ「Passkey Infrastructure」を選択した場合、Standalone Passkey APIが利用可能です。*10

パスキーAPIを利用した場合に必要な実装についても簡単に紹介します。

主なAPIエンドポイント

パスキーのみでログインを実装する場合は、以下の4つのAPIを利用します。

  • Start Passkey Registration*11
  • Finish Passkey Registration *12
  • Start Login *13
  • Finish Login *14

これらのAPIをバックエンドから呼び出すことでパスキーの登録およびログインが可能となります。

4つのAPIを利用する際に参考になりそうなサンプル実装として、passkeys-python*15がHankoTeamによって公開されています。


ID/パスワード認証 + パスキー認証を併用する場合は、以下のAPIを利用します。

  • Start MFA Registration *16
  • Finish MFA Registration *17
  • Start MFA Login *18
  • Finish MFA Login *19


注意点として、組織によってMFAのon/offを切り替えるようなケースにおいては、前者(パスキーのみのAPI)とMFA用(ID/パスワード認証 + パスキー認証)のAPIの両方を使用した実装が必要です。
こちらに関してサンプル実装は存在しませんが、上記と同様の方法で実装可能です。

終わりに

Hankoを導入することで、セキュリティとユーザー体験を向上させるためのパスキーを簡単に提供することが可能です。また、今回ご紹介していない項目として、ユーザーのパスワードに関するルールや2FAなど認証に関する細かい設定も可能となっています。
この記事を参考に、Hankoを活用したパスキーの導入を検討してみてください。