ASP.NET Identity で二要素認証を使う - SMS 編

ASP.NET Identity を使ってみようシリーズその2。その1はこちら

今回はみんな大好き二要素認証を実装してみましょう。

二要素認証って?

さすがに知らない人も少なくなってきたかと思いますが、、、

例えば、Microsoft アカウントに ID とパスワードを入力してログインすると、登録したメールアドレス宛にメールが届いたり、携帯に SMS メッセージが飛んできた経験があるかと思います。

で、それらに記載されているセキュリティコードも入力しないと、ログインが完了しないという例のやつです。
最近はメジャーなサービスは大抵実装されているので Microsoft アカウントがなくても大抵経験あるはず。これが二要素認証です。

より厳密には

  • ユーザーが知っていること
  • ユーザーが持っているもの
  • ユーザー自身の属性

のいずれか二つを使って認証する仕組みのことを言います。

「ユーザーが知っていること」の代表がパスワードにあたり、大抵はそれと「ユーザーが持っているもの」を組み合わせます。

まぁこのあたりはググればいくらでも情報出てきますので、詳しく知りたい方はそちらで。

ASP.NET Identity の二要素認証

ASP.NET Identity は今風なしゃれおつ設計を売りにしているので、当然二要素認証もサポートしてます。

主に絡んでくるのは

ですかね。に、日本語の情報が欲しいよ!!

まぁでも実際めっちゃ簡単なので何とかなります。

メールを使うケースと SMS を使うケースの両方実装してみましたが、今回ご紹介するのは SMS です。
正直メールもやることはほとんど変わらないのですが、せっかく実装したので後日もう一本ブログ書きます。

前提

環境は Visual Studio 2013 Update 3 です。
ASP.NET MVC Web アプリケーションテンプレートからプロジェクトを作成します。

SMS での二要素認証の流れ

やることの大まかな流れは以下の通りです。

  • プロジェクトを作成
  • SMS メッセージ送信処理を書く
    • IIdentityMessageService インタフェースを実装する SMS メッセージサービスを作成
    • SMS メッセージ送信サービスを UserManager クラスに登録
  • 二要素認証を利用するために画面を編集
    • テンプレートにすでに用意されているので、コメントを外すだけ

以上。えっ。

驚くほど簡単ですね。テンプレートの時点でここまで作り込まれているのは素晴らしいです。
なにしろ実際やってるのは SMS メッセージ送信処理だけですからね。

はじめに

プロジェクトを作成、起動し、テスト用のユーザを登録しておいてください。

SMS メッセージ送信処理を書く - IIdentityMessageService インタフェースを実装

まずは、IIdentityMessageService インタフェースを実装します。

Microsoft.AspNet.Identity.IIdentityMessageService インタフェースは、その名の通りユーザへのメッセージ通知を行う API を定義しているインタフェースです。

今回は SMS メッセージ送信やメール送信を実装するわけですが、通知できれば実装は何でもいいと思います。

テンプレートにはこのインタフェースを実装するクラスのひな形も用意されていて、App_Start\IdentityConfig.csEmailService クラスと SmsService がそれにあたります。

// App_Start\IdentityConfig.cs
public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // 電子メールを送信するには、電子メール サービスをここにプラグインします。
        return Task.FromResult(0);
    }
}

public class SmsService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // テキスト メッセージを送信するための SMS サービスをここにプラグインします。
        return Task.FromResult(0);
    }
}

では実際にメッセージの送信処理を実装しましょう。今回は Twilio を利用します。

NuGet で Twilio ライブラリ をインストールして、SmsService クラスの SendAsync メソッドを以下の様に実装します。

// App_Start\IdentityConfig.cs
public Task SendAsync(IdentityMessage message)
{
    string accountSid = "your_twilio_account_sid";
    string authToken = "your_twilio_auth_token";
    var twilio = new TwilioRestClient(accountSid, authToken);

    var smsFrom = "your_from_phone_number";
    var smsTo = message.Destination;
    var smsMessage = twilio.SendMessage(smsFrom, smsTo, message.Body);

    // エラー処理は省略

    return Task.FromResult(0);
}

事前に Twilio アカウントの取得が必要です。

さらに、無料で使えるトライアルアカウントではメッセージを送信できないのでアカウントのアップグレードも行う必要があります。事前チャージ制で最低 2,000 円かかるので、試されるときはご注意をば。

アカウントをアップグレードしたら、メッセージ送信用に電話番号を購入してください。
Account SID、そして Auth Token がもらえますので、上記コードに反映します。

SMS メッセージ送信処理を書く - SMS メッセージ送信サービスを登録

次に、作成したメッセージ送信サービスを ASP.NET Identity に使ってもらえるよう登録します。

と言っても実はテンプレートの時点ですでに UserManager クラスに SmsService クラスと EmailService クラスが登録されています。該当箇所は App_Start\IdentityConfig.csApplicationUserManager クラス、Create メソッドの以下の部分です。

// App_Start\IdentityConfig.cs
manager.RegisterTwoFactorProvider("電話コード", new PhoneNumberTokenProvider<ApplicationUser>
{
    MessageFormat = "あなたのセキュリティ コードは {0} です。"
});
manager.RegisterTwoFactorProvider("電子メール コード", new EmailTokenProvider<ApplicationUser>
{
    Subject = "セキュリティ コード",
    BodyFormat = "あなたのセキュリティ コードは {0} です。"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();

新しく PhoneNumberTokenProvider<TUser> クラスと EmailTokenProvider<TUser> クラスが出てきましたね。
こいつらは、セキュリティコードを生成してくれる、ASP.NET Identity 既定で実装されているクラスです。

TOTP 認証という、現在時刻と秘密鍵からトークンを生成して認証する仕様に基づいた TotpSecurityStampBasedTokenProvider<TUser, TKey> クラスを継承していますが、これについては後日またブログを書こうと思いますのでとりあえず今は忘れていいです。セキュリティコード作ってくれるにくい奴という理解で。

二要素認証を利用するために画面を編集

さて。実は初期状態のままだと二要素認証は使えません。

なので、二要素認証を有効化するために Views\Manage\Index.cshtml の以下の箇所をちょっと修正します。

@* Views\Manage\Index.cshtml *@

@* 略 *@

@* 以下のコメントを解除 *@ 
<dt>電話番号:</dt>
<dd>
    @(Model.PhoneNumber ?? "なし") [
    @if (Model.PhoneNumber != null)
    {
        @Html.ActionLink("変更", "AddPhoneNumber")
        @: &nbsp;|&nbsp;
        @Html.ActionLink("削除", "RemovePhoneNumber")
    }
    else
    {
        @Html.ActionLink("追加", "AddPhoneNumber")
    }
    ]
</dd>

@* 略 *@

<dt>2 要素認証:</dt>
<dd>
    @* 以下をコメントアウト
    <p>
        2 要素認証プロバイダーは構成されていません。2 要素認証がサポートされるようにこの ASP.NET アプリケーションを設定する方法の詳細については、
        <a href="http://go.microsoft.com/fwlink/?LinkId=313242">この記事</a>をご覧ください。
    </p>
    *@

    @* 以下のコメントを解除 *@ 
    @if (Model.TwoFactor)
    {

        <form method="post" action="/Manage/DisableTwoFactorAuthentication">
            有効
            <input type="submit" value="無効にする" class="btn btn-link" />
        </form>
    }
    else
    {

        <form method="post" action="/Manage/EnableTwoFactorAuthentication">
            無効
            <input type="submit" value="有効にする" class="btn btn-link" />
        </form>
    }
</dd>

実行!

以上、お疲れ様でした!

修正が終わったらアプリを起動してアカウント管理画面を開きましょう。

f:id:kendik:20140821025407j:plain

ここで、「2 要素認証」項目の「有効にする」リンクをクリックして二要素認証を有効化し、電話番号を追加します。

なお、2014/08/21 現在、Twilio では国外の番号からしかメッセージを送信できないようです。
ですので、ここで追加する電話番号は国際発信の規則に則り +81 から始まる先頭 0 を除いた番号を登録しましょう。

この電話番号追加処理の時点で確認のために SMS が送信されるので、Twilio を使ったメッセージ送信処理の検証になります。送られてこなかったら Twilio の設定か実装を見直してみて下さい。

では一旦ログオフして、再度ログインを試みます。

ログインボタンを押すと、二要素認証プロバイダの選択画面になりました。

f:id:kendik:20140821030448j:plain

今回は電話での認証なので、このまま「送信」ボタンを押しましょう。すると、、、

f:id:kendik:20140821030528j:plain

確認コードの入力画面に表示されました!

さらに、先ほど登録した番号にセキュリティコード入りのメッセージが届きますので、それを入力することでやっとログイン成功となります

まとめ

プロジェクトテンプレート有能。

テンプレートを使えば ASP.NET Identity で二要素認証を有効化するにあたって IIdentityMessageService インタフェースの実装と、それを UserManager<TUser> クラスに登録するだけで OK です。いやん、すてき-。