ac.jsを試してみました

結構前の話になりますが、OpenID TechNight Vol.9に参加した時にac.jsなるものがあると聞いたので、試してみました。

ac.jsとは

ac.jsとはaccount chooserを既存サイトに組み込むためのJavaScriptライブラリです。ac.jsを使用することで、account chooserのUXを既存のwebsiteに導入することができます。http://accountchooser.net から辿れるac.jsの解説ドキュメントにac.jsの簡単な導入方法があったり、少し細かい設定について書いてあるドキュメントもあるので、今回はそれらを参考に試しました。ちなみに、ac.jsはApache License 2.0です。

試す

今回は簡単なlegacy login(従来のemail, passwordなどを使ってログインする形式)を実装しました。

デフォルトでは以下のような構成のパスになります。パスはscriptタグ中に設定を記述することで変更することもできます。

名称 パス 用途
homeUrl / アプリのhome screen
loginUrl /account-login ログインページ
userStatusUrl /account-status ステータス確認エンドポイント
signupUrl /account-create アカウント作成ページ

動作の流れ

以下のような動作になります。

  1. ユーザーがWeb Pageにアクセスし、ログイン画面に遷移します。
  2. accountchooser.com上にアカウントが存在していると、ac.jsによりaccountchooser.comにリダイレクトが行われます。
  3. ユーザーがアカウントを選択します。
  4. ログイン画面に戻ります。
  5. ログイン画面からuserStatusUrlにPOSTでリクエストが行われます。
  6. userStatusUrlでは、Email(及びproviderId)を見て登録済みのユーザーであれば {"registered":true} を返し、未登録のユーザーであれば {"registered":false} を返します。
    • providerIdが指定されていたら {"registered":IP-Url} を返します。IP-UrlはIdPを利用してログインするためのURL。このパターンは今回は実装しません。
  7. ac.jsにより、{"registered":true} であればログイン画面に、{"registered":false} であればサインアップ画面にユーザーをリダイレクトさせます。
  8. loginUrlおよびsignupUrlでは従来通りのログイン・アカウント作成処理を行います。
  9. ログインが完了したら、ac.jsを含んだコンテンツを返します。
  10. ac.jsによりaccountchooser.comにリダイレクトが行われ、既にaccountchooser.comに存在しているアカウントであればhomeUrlにリダイレクトされ、存在していなければstoreするかどうかの確認がaccountchooser.comで行われます。確認後、homeUrlにリダイレクトされます。

実装

今回はデフォルトのパスで実装しました。それぞれのパスのhtml・JavaScriptは以下のようになります。 実際のサインアップ、ログイン処理は別途実装するか既存の実装を利用することになりますが、そのあたりは省略してます。 また、ac.jsは自動でinput fieldを埋めることもしてくれるのですが、対象となるinput fieldのidは各endpointのパスと同様に設定で変更可能です。

homeUrl

homeUrlのコンテンツはなんでも構いませんが、こんな感じで。ユーザーがログインしていなかったらloginUrlに飛ばす、もしくはloginUrlのリンクを貼ることになると思います。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>homeUrl</title>
    </head>
    <body>
        <h1>homeUrl</h1>
        <a href="/account-login">login</a>
    </body>
</html>

loginUrl

ac.jsを読み込むことと、formを設置する必要があります。formにはid=emailとid=passwordのinput fieldを設置します。accountchooser.comにアカウントが存在していない場合、リダイレクトが発生しないので、アカウント作成画面へのリンクでも貼っておきます(そのまま従来のログインをしても構いません)。ログインが成功したらアカウントの情報をscriptタグ内に記述することで、アカウントの情報をaccountchooser.comにstoreする処理が行われます。accountchooser.comにstoreした後はhomeUrlにリダイレクトされます。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>loginUrl</title>
        <script type="text/javascript" src="https://www.accountchooser.com/ac.js">
            storeAccount: {
                email: "foo@example.com",
                displayName: "bang_yy",
                photoUrl: "https://si0.twimg.com/profile_images/1146622182/___.png"
            }
        </script>
    </head>
    <body>
        <h1>loginUrl</h1>
        <form action="account-check" method="POST">
            <ul>
                <li>email: <input id="email" type="text" name="email"></li>
                <li>password: <input id="password" type="password" name="password"></li>
                <li><input id="submit" type="submit" value="submit"></li>
            </ul>
        </form>
        <a href="/account-create">create account</a>
    </body>
</html>

userStatusUrl

このエンドポイントはaccountchooser.comからPOSTされてくるユーザーのアカウント情報を見て、すでにアカウントが自サイトに存在していれば{"registered":true}を、存在していなければ{"regisitered":false}を、IdPにリダイレクトする場合は、{"registered":"IP-uri"}としてIdPを利用してログインするためのURLを返します。今回はlegacy loginなので最後のパターンはありません。

signupUrl

accountchooser.com経由で遷移してきたパターンだと、id=displayName, photoUrl, emailのinput fieldに自動でfillしてくれます。アカウント作成が成功したらアカウントの情報をscriptタグ内に記述します。このアカウントの情報をaccountchooser.comにstoreします。accountchooser.comにstore後はhomeUrlにリダイレクトされます。 また、loginUrlでaccountchooser.comにアカウントが存在していない場合はaccountchooser.comにリダイレクトがされないので、従来通りアカウント作成用の画面を表示する必要があります。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>signupUrl</title>
        <script type="text/javascript" src="https://www.accountchooser.com/ac.js">
            storeAccount: {
                email: "foo@example.com",
                displayName: "bang_yy",
                photoUrl: "https://si0.twimg.com/profile_images/1146622182/___.png"
            }
        </script>
    </head>
    <body>
        <h1>signupUrl</h1>
        <form method="POST">
            <ul>
                <li>displayName: <input id="displayName" type="text" name="displayName"></li>
                <li>photoUrl: <input id="photoUrl" type="text" name="photoUrl"></li>
                <li>email: <input id="email" type="text" name="email"></li>
                <li>password: <input id="password" type="password" name="password"></li>
                <li>confirm password: <input id="confirm" type="password" value="" name="confirm"></li>
                <li><input id="submit" type="submit" valur="submit"></li>
            </ul>
        </form>
    </body>
</html>

Identity Provider Filtering

今回は指定していませんが、Facebook loginなどを実装している場合は、以下のように記述することでサポートしているIdPのアカウントをaccountchooser.com上で表示させることができます。

<script>
    accountchooser.CONFIG.providers = ["yahoo.com", "facebook.com" ];
</script>

まとめ

今回のように、ac.jsを単純に組み込むだけだとそんなに良いUXにはなりませんが、ac.jsは他にもいろいろとできるのでもっとUXの良いフローの実装もできます。 あと、ac.jsでlegacy loginだけできてもあまり嬉しくないのでidPを使ったログインを実装したいところですが、ac.jsを使う場合のfederated loginではOAuthなりOpenID Connectなりの実装はRPがすることになります。

ac.jsはまだまだ変更がありそうな気がするので(実際、書いてる間にIdentity Provider Filteringの仕様が変わってました)、時折見ていこうかなと思います。