ASP.NET Identity を使って Microsoft アカウントで認証する

進捗ダメです。

などと一部の方に状況報告しつつ、ASP.NET Identity を使ってみようシリーズその4です。

その1 - ASP.NET Identity のセキュリティスタンプ機能を使う
その2 - ASP.NET Identity で二要素認証を使う - SMS 編
その3 - ASP.NET Identity で二要素認証を使う - E-mail 編

今回やること

今回は External Login を試してみます。

サービスプロバイダは Microsoft アカウントを使いますが、Twitter でも Facebook でも変わらないと思いますので、適宜読み替えて下さい。

前提

いつも通り環境は Visual Studio 2013 Update 3 です。
ASP.NET MVC Web アプリケーションテンプレートからプロジェクトを作成しておいてください。

Microsoft アカウントでの認証の流れ

  • Microsoft アカウントで連携するアプリケーションの登録
  • OWIN の Microsoft アカウント認証機能を有効化
  • (テストのため) IIS Express の設定

以上が大まかな流れです。

Microsoft アカウントで連携するアプリケーションの登録

そもそもどこで登録すれば良いか分からなくて、これが一番時間かかりました。

https://account.live.com/developers/applications/index

↑ のページから「アプリケーションの作成」を行ってください。

アプリケーション設定を作り終えたら、画面左のメニューの「API 設定」を開いてリダイレクト先の URL を登録します。

f:id:kendik:20140825015334j:plain

URL のドメインは適当にオリジナルに設定して下さい。
はっきりしていませんが、どうやら localhost では登録を受け付けてくれないようです。
また、URL の末尾に signin-microsoft という文字列を追加します。

上記設定が完了したら、「アプリケーション設定」メニューを開きます。
クライアント ID とクライアントシークレットが確認出来ますのでメモっておいてください。連携の時に使います。

OWIN の Microsoft アカウント認証機能を有効化

アプリケーションで Microsoft アカウント認証機能を有効化します。

APP_Start\Startup.Auth.cs の以下の箇所を修正します。

// APP_Start\Startup.Auth.cs
// 以下の行のコメントアウトを解除して、先ほどメモっておいたクライアント ID とシークレットをセット
app.UseMicrosoftAccountAuthentication(
    clientId: "your_client_id",
    clientSecret: "your_client_secret");

UseMicrosoftAccountAuthentication メソッドは、Microsoft.Owin.Security.MicrosoftAccount 名前空間内に定義された IAppBuilder に対する拡張メソッドです。

実態は MicrosoftAccountAuthenticationMiddleware というミドルウェアですね。こちらソースコードが読めます。

そして認証処理そのもののは MicrosoftAccountAuthenticationHandler で行われているようです。OAuth v2 を使っているようですが、気になる方は ソースコード をどうぞ。

(テストのため) IIS Express の設定

ここまででアプリケーションの設定としては完了です。この後は、テストのために IIS Express の設定を行います。

連携アプリケーションの登録の時に、リダイレクト先 URL に localhost でないドメインを設定したので、その URL でアプリケーションが見れるよう OS や IIS を設定します。

IIS Express で仮想サイトに複数のホスト名を割り当てる - しばやん雑記 を参考にさせていただきました。

C:\Users\<user name>\Documents\IISExpress\config\applicationhost.config を以下の通り編集します。

<site name="your_application_name" id="7">
    <application path="/" applicationPool="Clr4IntegratedAppPool">
        <virtualDirectory path="/" physicalPath="c:\users\<user name>\documents\visual studio 2013\Projects\WebApplication5\WebApplication5" />
    </application>
    <bindings>
        <binding protocol="http" bindingInformation="*:61548:localhost" />
        
        <!-- ↓ の行を追加 -->
        <binding protocol="http" bindingInformation="*:61548:<test domain>.com" />

    </bindings>
</site>

続いて、プロジェクトのプロパティを開き、プロジェクトの URL を変更します。

f:id:kendik:20140825015411j:plain

最後に hosts ファイルを編集します。

# localhost name resolution is handled within DNS itself.
#   127.0.0.1       localhost
#   ::1             localhost

# ↓ の行を追加
127.0.0.1           <your domain>

実行する

ここまでで全ての準備は完了です。
アプリケーションを実行してログインしようとすると、以下のようになっているはずです。

f:id:kendik:20140825015218j:plain

「別のサービスを利用してログインしてください。」の下に「Microsoft」というリンクがあります。
これを押すと Microsoft アカウントのログイン画面が表示され、Microsoft アカウントにログインすると同時にアプリケーションのログインも完了します。簡単ですね。

よく分からないんだけど

あまりに簡単すぎてどう処理が流れていったのか掴みにくいと思いますので、簡単に流れでも(本当に簡単ですが)。

ログイン画面の「Microsoft」リンクを押下するというユーザー操作から、、、

  1. Views\Account\_ExternalLoginsListPartial.cshtml から /Account/ExternalLogin アクションへ
  2. ExternalLogin アクションメソッドが、ChallangeResult を返す
    • HttpUnauthorizedResult を継承した ActionResult アプリ独自のクラス
    • ChallangeResult メソッド内で、OWIN に認証処理を委譲
  3. Microsoft サイトから /Account/ExternalLoginCallback にリダイレクト
  4. /Account/ExternalLoginCallback アクション中で認証
    • SignInManager<TUser, TKey> クラスの ExternalSignInAsync メソッドにより認証

大分見難いですね、、、すいません。

SignInManager<TUser, TKey> クラスは、 Microsoft.AspNet.Identity.Owin 名前空間内に定義された、ASP.NET Identity のログイン処理を一括で見るサービスです。OWIN とがっつり連携し、Identity を利用する上である程度の複雑さを担ってくれています。

また、データは、アプリケーションのアカウント情報は普通にログインした時と同じく AspNetUsers テーブルに保存され、Microsoft アカウントとのひも付きは AspNetUserLogins テーブルに保存されるようです。