ASP.NET SPA (JavaScript) の Web API 認証 (ASP.NET Identity)


環境 : Visual Studio 2013

ASP.NET Identity に関する補足

こんにちは。

knockout.js 勉強会の補足 (カットしたネタ) の 2 つ目です。
デモで紹介した ASP.NET Web API で認証 (Authentication) を実装し、JavaScript からどのように使用するか補足します。(実アプリケーションでは、必ず必要になるシナリオです。)

 

ASP.NET Identity の Token 認証

昔、「ASP.NET Web API における操作ごとの制御 (Validation, 認証/権限, Exception 処理 など)」で紹介した頃には、フォーム認証などでもお馴染みの Cookie 認証の方式のみが使用されていましたが、Visual Studio 2013 からは ASP.NET Identity と呼ばれる新しいフレームワークが導入され、ASP.NET Web API をはじめとする ASP.NET 全体で Token 認証を柔軟に利用できるようになりました。
最新の ASP.NET SPA のプロジェクト テンプレートでは、Web API において、Cookie 認証は抑制されており、この Token 認証の方式のみがオンになっています。

まずは、この新しい Token 認証の方式について、バックグラウンドを解説しておきます。

補足 : ASP.NET Web API で Authentication を有効にするには、下記の通り Authorize 属性を付与するだけです。

[Authorize]
public class ValuesController : ApiController
{
  . . .
}

補足 : Cookie 認証の抑制は WebApiConfig.cs の下記に記載されています。この箇所を書き変えて動作を変更できます。

public static void Register(HttpConfiguration config)
{
  // Web API configuration and services
  // Bearer token 認証のみを使用するように Web API を設定します
  config.SuppressDefaultHostAuthentication();
  config.Filters.Add(new HostAuthenticationFilter("Bearer"));
  . . .

ASP.NET Identity が提供する個人ユーザーアカウント (Individual User Accounts) では、「Azure AD を使った API (Service) 連携の Client 開発」などで紹介した OAuth のフロー (現在主流となっているフロー) のためのエンドポイントが提供されています。
作り方次第で、以下の /Token のエンドポイントを使って Access Token を取得できます。(下記の localhost:1111 は、開発・デバッグで使用しているアプリケーション サーバーを指定してください。)

POST http://localhost:1111/Token
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=<your user id>&password=<your password>

返ってくる Response は下記の通りです。以降は、下記の access_token を使って、さまざまな処理の呼び出しが可能です。
このエンドポイントは、server-to-server のシナリオなどで使用できますね。

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Tue, 20 May 2014 06:46:02 GMT

{
  "access_token":"oxbk18at1v_2d...",
  "token_type":"bearer",
  "expires_in":1209599,
  "userName":"tsmatsuz",
  ".issued":"Tue, 20 May 2014 06:45:54 GMT",
  ".expires":"Tue, 03 Jun 2014 06:45:54 GMT"
}

この /Token の動作は、OWIN を使って、Startup/Startup.Auth.cs に下記の通り定義されています。(OWIN のパイプラインと ASP.NET Web API との蜜月関係については、矢後さんの「OWIN を使ったセルフホストで ASP.NET Web API 2 をかんたん起動」などを参考にしてみてください。)

. . .
OAuthOptions = new OAuthAuthorizationServerOptions
{
  TokenEndpointPath = new PathString("/Token"),
  AuthorizeEndpointPath = new PathString("/Account/Authorize"),
  Provider = new ApplicationOAuthProvider(PublicClientId),
  AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
  AllowInsecureHttp = true
};

public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
. . .

public void ConfigureAuth(IAppBuilder app)
{
  . . .

  app.UseOAuthBearerTokens(OAuthOptions);
  . . .

}

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
  . . .
}
. . .

なお、プロジェクト テンプレートが提供する既定の OAuthAuthorizationServerProvider (上記の ApplicationOAuthProvider) では、Credential の確認の際に reject をおこなっていますので、このエンドポイント (/Token) を使用する前に、あらかじめ、ValidateClientAuthentication、GrantResourceOwnerCredentials などの override メソッドを実装しておいてください。(Controllers/AccountController.cs などの処理が参考になります。)

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
  . . .

  public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  {
    // always validated (demo purpose, this time ...)
    context.Validated();
    return Task.FromResult<object>(null);
  }

  public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  {
    Debug.WriteLine(context.UserName);
    Debug.WriteLine(context.Password);
    ... implement some kind of auth check, here !
  }
}

また、ASP.NET SPA のプロジェクト テンプレートが提供する AccountController を使用すると、以下の URI から Access Token を取得できます。(下記の state は遷移後に引き継ぎたい情報を設定します。今回は空とします。)
このあとサンプルで解説しますが、MVC の Controller なので、上記と異なり、UI (ログイン画面) を前提とした連携で使用できます。また上記と異なり、プロジェクト テンプレートに何の変更もおこなうことなく すぐ使えます。(ASP.NET SPA のプロジェクト テンプレートで既に実装済みです。)

GET http://localhost:1111/Account/Authorize?client_id=web&response_type=token&state=

上記にアクセスすると ASP.NET のフォーム認証のログイン画面が表示され、認証に成功すると、下記の通り Url のアンカー (fragment) によって access token が取得できます。(JavaScript などからの呼び出しに適しています。)

http://localhost:1111/#access_token=8a4lqhYNRovJTd7APmjGHu...&token_type=bearer&expires_in=1209600

ASP.NET Identity では Facebook, Twitter, Google など External Login との組み合わせも可能ですが、この場合も ASP.NET Identity がハブとなって access token を設定するので、アプリ側はいっさい変更せずに多くの IdP と統合できます。(ちょうど「Build Insider : Windows Azure の認証におけるその他サービス ~ Windows Azureモバイル・サービス、多要素認証」で紹介した Azure Mobile App の認証におけるアーキテクチャと似ていますね。)

 

JavaScript からの認証

仕組みがわかれば、あとは上記をコードに落とすのみです。

例えば、ASP.NET SPA のプロジェクトなど、JavaScript を使って ASP.NET Idenity と連携する場合は、下記のようなコードになります。
下記では、上述の AccountController のエンドポイントを使用しています。ログイン画面を表示して access token を取得し、このトークンを sessionStorage に記憶しています。以降は、期限切れになるまで sessionStorage に記憶した access token を使用して ASP.NET Web API などを呼び出します。(実際の開発では、access token が期限切れになった際の再取得の処理も実装してください。)

var access_token = '',
  token_type = '';

// access token を確認 (なければ、ログイン画面を表示して取得)
$(document).ready(function () {
  if (!sessionStorage.getItem('accessToken')) {
    access_token = getParamFromHash('access_token');
    token_type = getParamFromHash('token_type');
    if (access_token.length > 0) {
      sessionStorage.setItem('accessToken', access_token);
      sessionStorage.setItem('tokenType', token_type);
    } else {
      window.location = '/Account/Authorize?client_id=web&response_type=token&state=';
    }
  }
  else {
    access_token = sessionStorage.getItem('accessToken');
    token_type = sessionStorage.getItem('tokenType');
  }

  . . .
}

// fragment (#access_token=8a4lqhYNRovJTd7APmjGHu...) の Value を取得
function getParamFromHash(name) {
  var regex = new RegExp('[\\?#&]' + name + '=([^&#]*)');
  var res = regex.exec(window.location.hash);
  return res == null ? '' : decodeURIComponent(res[1].replace(/\+/g, ' '));
}
. . .

// access token を設定して ASP.NET Web API の呼び出し !
$.ajax({
  type: 'GET',
  url: '/api/Values/',
  dataType: 'json',
  beforeSend: function (xhr, settings) {
    xhr.setRequestHeader('Authorization', token_type + ' ' + access_token);
  }
})
.done(function (result) {
  . . .
})
. . .

ASP.NET SPA のプロジェクト テンプレートには、すでにこうしたコードが埋め込まれていますので是非参考にしてください。

最新の ASP.NET では、このフレームワークを使って Microsoft Azure Active Directory (Azure AD) と使用した ASP.NET Web API 認証も簡単に行えます。この方法については、以前記載した「Azure Active Directory の SSO 開発 (Visual Studio 2013 編)」を参照してください。

以上、今 “某イベント” で忙しいので、実行結果の画面とかは省略してますが、6 月に入ったら ASP.NET Identity の解説動画を作る予定です。。。(もっと前に作る予定だったんですが、「忙しさ」を理由に こんな時期に。。。既に ASP.NET vNext が見えてきているビミョーな時期なんですが)

 

Comments (1)

  1. tekikesyo says:

    よし、勉強になりました。

Skip to main content