次の方法で共有


MVC 4 で OAuth プロバイダーを使用する

Tom FitzMacken

このチュートリアルでは、ユーザーが Facebook、Twitter、Microsoft、Google などの外部プロバイダーからの資格情報を使ってログインできるようにする ASP.NET MVC 4 Web アプリケーションを構築し、それらのプロバイダーの機能の一部を Web アプリケーションに統合する方法について説明します。 わかりやすくするために、このチュートリアルでは Facebook からの資格情報の操作に焦点を当てます。

ASP.NET MVC 5 Web アプリケーションで外部資格情報を使うには、Facebook と Google OAuth2 および OpenID サインオンを使った ASP.NET MVC 5 アプリの作成に関するページを参照してください。

何百万ものユーザーが既にこれらの外部プロバイダーのアカウントを持っているため、Web サイトでこれらの資格情報を有効にすると、大きな利点があります。 これらのユーザーは、新しい資格情報のセットを作成して記憶する必要がない場合、サイトにサインアップする傾向が高くなります。 また、ユーザーがこれらのプロバイダーのいずれかを介してログインした後、プロバイダーからのソーシャル操作を組み込むことができます。

作成する内容

このチュートリアルには 2 つの主な目的があります。

  1. ユーザーが OAuth プロバイダーからの資格情報を使ってログインできるようにする。
  2. プロバイダーからアカウント情報を取得し、その情報をサイトのアカウント登録に統合する。

このチュートリアルの例では認証プロバイダーとして Facebook を使うことに焦点を当てていますが、任意のプロバイダーを使うようにコードを変更できます。 プロバイダーを実装する手順は、このチュートリアルで説明する手順とほとんど同じです。 大きな違いに気付くのは、プロバイダーの API セットを直接呼び出す場合のみです。

前提条件

または

さらに、このトピックは、ASP.NET MVC と Visual Studio に関する基本的な知識があることを前提としています。 ASP.NET MVC 4 の概要が必要な場合は、「ASP.NET MVC 4 の概要」を参照してください。

プロジェクトを作成する

Visual Studio で、新しい ASP.NET MVC 4 Web アプリケーションを作成し、"OAuthMVC" という名前を付けます。 .NET Framework 4.5 または 4 のいずれかをターゲットにできます。

create project

[新しい ASP.NET MVC 4 プロジェクト] ウィンドウで、[インターネット アプリケーション] を選び、ビュー エンジンは [Razor] のままにします。

select Internet Application

プロバイダーを有効にする

インターネット アプリケーション テンプレートを使って MVC 4 Web アプリケーションを作成すると、App_Start フォルダーに AuthConfig.cs というファイルを含むプロジェクトが作成されます。

AuthConfig file

AuthConfig ファイルには、外部認証プロバイダーのクライアントを登録するコードが含まれています。 既定では、このコードはコメント アウトされているため、外部プロバイダーはいずれも有効になりません。

public static class AuthConfig
{
    public static void RegisterAuth()
    {
        // To let users of this site log in using their accounts from other sites such as Microsoft, Facebook, and Twitter,
        // you must update this site. For more information visit https://go.microsoft.com/fwlink/?LinkID=252166

        //OAuthWebSecurity.RegisterMicrosoftClient(
        //    clientId: "",
        //    clientSecret: "");

        //OAuthWebSecurity.RegisterTwitterClient(
        //    consumerKey: "",
        //    consumerSecret: "");

        //OAuthWebSecurity.RegisterFacebookClient(
        //    appId: "",
        //    appSecret: "");

        //OAuthWebSecurity.RegisterGoogleClient();
    }
}

外部認証クライアントを使うには、このコードのコメントを解除する必要があります。 サイトに含めるプロバイダーのみのコメントを解除します。 このチュートリアルでは、Facebook 資格情報のみを有効にします。

public static class AuthConfig
{
    public static void RegisterAuth()
    {
        //OAuthWebSecurity.RegisterMicrosoftClient(
        //    clientId: "",
        //    clientSecret: "");

        //OAuthWebSecurity.RegisterTwitterClient(
        //    consumerKey: "",
        //    consumerSecret: "");

        OAuthWebSecurity.RegisterFacebookClient(
            appId: "",
            appSecret: "");

        //OAuthWebSecurity.RegisterGoogleClient();        
    }
}

上記の例では、登録パラメーターとして空の文字列がメソッドに含まれていることに注意してください。 ここでアプリケーションを実行しようとすると、パラメーターに空の文字列を使用できないため、アプリケーションは引数例外をスローします。 有効な値を指定するには、次のセクションで示すように、Web サイトを外部プロバイダーに登録する必要があります。

外部プロバイダーを登録する

外部プロバイダーからの資格情報を使ってユーザーを認証するには、Web サイトをプロバイダーに登録する必要があります。 サイトを登録すると、クライアントの登録時に含めるパラメーター (キーまたは ID、シークレットなど) を受け取ります。 使用するプロバイダーのアカウントを持っている必要があります。

このチュートリアルでは、これらのプロバイダーに登録するための手順を詳しく説明しません。 複雑な手順ではないので、 プロバイダーの指示に従ってサイトを登録してください。 サイトの登録については、次の開発者向けサイトを参照してください。

サイトを Facebook に登録するときは、次の図に示すように、サイトのドメインに "localhost" を、URL に "http://localhost/" を指定できます。 localhost の使用はほとんどのプロバイダーで機能しますが、現在、Microsoft プロバイダーでは機能しません。 Microsoft プロバイダーの場合は、有効な Web サイト URL を含める必要があります。

register site

前の画像では、アプリ ID、アプリのシークレット、連絡先メール アドレスの値が削除されています。 実際にサイトを登録すると、これらの値が表示されます。 アプリ ID とアプリ シークレットはアプリケーションに追加するため、その値をメモしておきます。

テスト ユーザーを作成する

既存の Facebook アカウントを使ってサイトをテストしても構わない場合は、このセクションをスキップできます。

Facebook アプリ管理ページ内で、アプリケーションのテスト ユーザーを簡単に作成できます。 これらのテスト アカウントを使ってサイトにログインできます。 テスト ユーザーを作成するには、左側のナビゲーション ウィンドウで [ロール] リンクをクリックし、[作成] リンクをクリックします。

create test users

Facebook サイトは、要求した数のテスト アカウントを自動的に作成します。

プロバイダーのアプリケーション ID と鍵を追加する

Facebook から ID とシークレットを受け取ったので、AuthConfig ファイルに戻り、それらをパラメーター値として追加します。 以下に示す値は実際の値ではありません。

public static class AuthConfig
{
    public static void RegisterAuth()
    {
        //OAuthWebSecurity.RegisterMicrosoftClient(
        //    clientId: "",
        //    clientSecret: "");

        //OAuthWebSecurity.RegisterTwitterClient(
        //    consumerKey: "",
        //    consumerSecret: "");

        //OAuthWebSecurity.RegisterFacebookClient(
        //    appId: "",
        //    appSecret: "");

        //OAuthWebSecurity.RegisterGoogleClient();
    }
}

外部資格情報を使ってログインする

サイトで外部資格情報を有効にするために必要な作業はこれだけです。 アプリケーションを実行し、右上隅にあるログイン リンクをクリックします。 テンプレートでは、Facebook をプロバイダーとして登録したことが自動的に認識され、プロバイダーのボタンが含まれます。 複数のプロバイダーを登録すると、各プロバイダーのボタンが自動的に含まれます。

external login

このチュートリアルでは、外部プロバイダーのログイン ボタンをカスタマイズする方法については説明しません。 詳細については、「OAuth/OpenID を使う場合のログイン UI のカスタマイズ」を参照してください。

Facebook ボタンをクリックして、Facebook の資格情報を使ってログインします。 外部プロバイダーの 1 つを選ぶと、そのサイトにリダイレクトされ、そのサービスによってログインするように求められます。

次の画像は Facebook のログイン画面を示しています。 Facebook アカウントを使って oauthmvcexample というサイトにログインしていることを示しています。

facebook authentication

Facebook の資格情報を使ってログインすると、そのサイトに基本情報へのアクセス権が付与されることをユーザーに通知するページが表示されます。

request permission

[アプリに移動] を選んだ後、ユーザーはサイトに登録する必要があります。 次の画像は、ユーザーが Facebook 資格情報を使ってログインした後の登録ページを示しています。 ユーザー名には、通常、プロバイダーの名前が事前に入力されます。

Screenshot shows a Register page where you can associate your Facebook account with this app.

[登録] をクリックして登録を完了します。 ブラウザーを閉じます。

新しいアカウントがデータベースに追加されたことがわかります。 サーバー エクスプローラーで、DefaultConnection データベースを開き、Tables フォルダーを開きます。

database tables

UserProfile テーブルを右クリックし、[テーブル データの表示] を選びます。

show data

追加した新しいアカウントが表示されます。 webpage_OAuthMembership テーブルのデータを見てください。 追加したアカウントの外部プロバイダーに関連するより多くのデータが表示されます。

外部認証のみを有効にする場合は、これで完了です。 ただし、次のセクションで示すように、プロバイダーからの情報をさらに新しいユーザー登録プロセスに統合することができます。

追加のユーザー情報のモデルを作成する

前のセクションで説明したように、組み込みのアカウント登録を機能させるために追加情報を取得する必要はありません。 ただし、ほとんどの外部プロバイダーはユーザーに関する追加情報を返します。 次のセクションでは、その情報を保持し、データベースに保存する方法を示します。 具体的には、ユーザーのフル ネーム、ユーザーの個人 Web ページの URI、Facebook がアカウントを検証したかどうかを示す値の値を保持します。

Code First Migrations を使って、追加のユーザー情報を格納するためのテーブルを追加します。 テーブルを既存のデータベースに追加するので、最初に現在のデータベースのスナップショットを作成する必要があります。 現在のデータベースのスナップショットを作成すると、後で新しいテーブルのみを含む移行を作成できます。 現在のデータベースのスナップショットを作成するには:

  1. パッケージ マネージャー コンソールを開きます
  2. コマンド enable-migrations を実行します
  3. コマンド add-migration initial –IgnoreChanges を実行します
  4. コマンド update-database を実行します

ここで、新しいプロパティを追加します。 Models フォルダーで AccountModels.cs ファイルを開き、RegisterExternalLoginModel クラスを見つけます。 RegisterExternalLoginModel クラスは、認証プロバイダーから返される値を保持します。 以下で強調表示されているように、FullName と Link という名前のプロパティを追加します。

public class RegisterExternalLoginModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    public string ExternalLoginData { get; set; }

    [Display(Name = "Full name")]
    public string FullName { get; set; }

    [Display(Name = "Personal page link")]
    public string Link { get; set; }
}

また、AccountModels.cs に、ExtraUserInformation という新しいクラスを追加します。 このクラスは、データベースに作成される新しいテーブルを表します。

[Table("ExtraUserInformation")]
public class ExternalUserInformation
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public string FullName { get; set; }
    public string Link { get; set; }
    public bool? Verified { get; set; }
}

UsersContext クラスに、次の強調表示されたコードを追加して、新しいクラスの DbSet プロパティを作成します。

public class UsersContext : DbContext
{
    public UsersContext()
        : base("DefaultConnection")
    {
    }

    public DbSet<UserProfile> UserProfiles { get; set; }
    public DbSet<ExternalUserInformation> ExternalUsers { get; set; }
}

これで、新しいテーブルを作成する準備ができました。 パッケージ マネージャー コンソールをもう一度開き、今度は次のようにします。

  1. コマンド add-migration AddExtraUserInformation を実行します
  2. コマンド update-database を実行します

新しいテーブルがデータベースに存在するようになりました。

追加データを取得する

追加のユーザー データを取得するには 2 つの方法があります。 1 つ目の方法は、認証要求中に既定で返されるユーザー データを保持することです。 2 つ目の方法は、プロバイダー API を具体的に呼び出して、詳細情報を要求することです。 FullName と Link の値は Facebook によって自動的に返されます。 Facebook がアカウントを検証したかどうかを示す値は、Facebook API の呼び出しを通じて取得されます。 まず、FullName と Link の値を設定し、その後、検証された値を取得します。

追加のユーザー データを取得するには、Controllers フォルダーにある AccountController.cs ファイルを開きます。

このファイルには、アカウントのログ、登録、管理のためのロジックが含まれています。 特に、ExternalLoginCallbackExternalLoginConfirmation というメソッドに注目してください。 これらのメソッド内にコードを追加して、アプリケーションの外部ログイン操作をカスタマイズできます。 ExternalLoginCallback メソッドの最初の行には次が含まれます。

AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(
    Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

追加のユーザー データは、VerifyAuthentication メソッドから返される AuthenticationResult オブジェクトの ExtraData プロパティに戻されます。 Facebook クライアントの ExtraData プロパティには次の値が含まれています。

  • ID
  • name
  • link
  • 性別
  • accesstoken

他のプロバイダーの ExtraData プロパティには、よく似ているがわずかに異なるデータがあります。

ユーザーがサイトを初めて使う場合は、追加データを取得し、そのデータを確認ビューに渡します。 メソッドの最後のコード ブロックは、ユーザーがサイトを初めて使う場合にのみ実行されます。 次の行を置き換えます。

return View("ExternalLoginConfirmation", new RegisterExternalLoginModel 
{ 
    UserName = result.UserName, 
    ExternalLoginData = loginData 
});

次の行に置き換えることができます。

return View("ExternalLoginConfirmation", new RegisterExternalLoginModel
{
    UserName = result.UserName,
    ExternalLoginData = loginData,
    FullName = result.ExtraData["name"],
    Link = result.ExtraData["link"]
});

この変更には、FullName プロパティと Link プロパティの値が含まれるだけです。

ExternalLoginConfirmation メソッドで、以下に強調表示されているようにコードを変更して、追加のユーザー情報を保存します。

if (user == null)
{
    // Insert name into the profile table
    UserProfile newUser = db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
    db.SaveChanges();

    db.ExternalUsers.Add(new ExternalUserInformation 
    { 
        UserId = newUser.UserId, 
        FullName = model.FullName, 
        Link = model.Link 
    });
    db.SaveChanges();

    OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
    OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

    return RedirectToLocal(returnUrl);
}
else
{
    ModelState.AddModelError("UserName", "User name already exists. Please enter a different user name.");
}

ビューの調整

プロバイダーから取得した追加のユーザー データは、登録ページに表示されます。

Views/Account フォルダーで、ExternalLoginConfirmation.cshtml を開きます。 ユーザー名の既存のフィールドの下に、FullName、Link、PictureLink のフィールドを追加します。

<li>
    @Html.LabelFor(m => m.FullName)
    @Html.TextBoxFor(m => m.FullName)
</li>
<li>
    @Html.LabelFor(m => m.Link)
    @Html.TextBoxFor(m => m.Link)
</li>

これで、アプリケーションを実行し、保存されたその他の情報を使って新しいユーザーを登録する準備がほぼ整いました。 サイトにまだ登録されていないアカウントを持っている必要があります。 別のテスト アカウントを使うか、再利用するアカウントの UserProfile テーブルと webpages_OAuthMembership テーブルの行を削除します。 これらの行を削除すると、アカウントが確実に再登録されます。

アプリケーションを実行し、新しいユーザーを登録します。 今回は、確認ページにさらに多くの値が含まれていることに注意してください。

Screenshot shows where you can enter a user name and other information after associating a Facebook account with the app.

登録が完了したらブラウザーを閉じます。 データベースを参照して、ExtraUserInformation テーブルの新しい値を確認します。

Facebook API 用の NuGet パッケージをインストールする

Facebook には、操作を実行するために呼び出すことができる API が用意されています。 Facebook API を呼び出すには、HTTP 要求の送信を指示するか、それらの要求の送信を容易にする NuGet パッケージのインストールを使います。 このチュートリアルでは NuGet パッケージの使用が示されていますが、NuGet パッケージのインストールは必須ではありません。 このチュートリアルでは、Facebook C# SDK パッケージの使用方法について説明します。 Facebook API の呼び出しを支援する NuGet パッケージは他にもあります。

[NuGet パッケージの管理] ウィンドウから Facebook C# SDK パッケージを選びます。

install package

Facebook C# SDK を使って、ユーザーのアクセス トークンを必要とする操作を呼び出します。 次のセクションでは、アクセス トークンを取得する方法を示します。

アクセス トークンの取得

ほとんどの外部プロバイダーは、ユーザーの資格情報が検証された後にアクセス トークンを返します。 このアクセス トークンは、認証されたユーザーのみが使用できる操作を呼び出すことができるため、非常に重要です。 そのため、より多くの機能を提供する場合には、アクセス トークンを取得して格納することが不可欠です。

外部プロバイダーによっては、アクセス トークンが限られた時間しか有効でない場合があります。 有効なアクセス トークンを持っていることを確実にするには、ユーザーがログインするたびにアクセス トークンを取得し、データベースに保存するのではなく、セッション値として格納します。

ExternalLoginCallback メソッドでは、アクセス トークンは AuthenticationResult オブジェクトの ExtraData プロパティにも戻されます。 強調表示されたコードを ExternalLoginCallback に追加して、アクセス トークンを Session オブジェクトに保存します。 このコードは、ユーザーが Facebook アカウントでログインするたびに実行されます。

[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(
        Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
    if (!result.IsSuccessful)
    {
        return RedirectToAction("ExternalLoginFailure");
    }

    if (result.ExtraData.Keys.Contains("accesstoken"))
    {
        Session["facebooktoken"] = result.ExtraData["accesstoken"];
    }

    if (OAuthWebSecurity.Login(
        result.Provider, 
        result.ProviderUserId, 
        createPersistentCookie: false))
    {
        return RedirectToLocal(returnUrl);
    }

    if (User.Identity.IsAuthenticated)
    {
        // If the current user is logged in add the new account
        OAuthWebSecurity.CreateOrUpdateAccount(
            result.Provider,
            result.ProviderUserId, 
            User.Identity.Name);
        return RedirectToLocal(returnUrl);
    }
    else
    {
        // User is new, ask for their desired membership name
        string loginData = OAuthWebSecurity.SerializeProviderUserId(
            result.Provider, 
            result.ProviderUserId);
        ViewBag.ProviderDisplayName =
            OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
        ViewBag.ReturnUrl = returnUrl;
        return View("ExternalLoginConfirmation", new RegisterExternalLoginModel
        {
            UserName = result.UserName,
            ExternalLoginData = loginData,
            FullName = result.ExtraData["name"],
            Link = result.ExtraData["link"]
        });    
    }
}

この例では Facebook からアクセス トークンを取得しますが、"accesstoken" という名前の同じキーを使って外部プロバイダーからアクセス トークンを取得することもできます。

ログオフ

既定の LogOff メソッドでは、ユーザーはアプリケーションからログアウトされますが、外部プロバイダーからはログアウトされません。 ユーザーを Facebook からログアウトし、ユーザーがログオフした後にトークンが保持されないようにするには、次の強調表示されたコードを AccountController の LogOff メソッドに追加します。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
    WebSecurity.Logout();
    if (Session["facebooktoken"] != null)
    {
        var fb = new Facebook.FacebookClient();
        string accessToken = Session["facebooktoken"] as string;
        var logoutUrl = fb.GetLogoutUrl(new { access_token = accessToken, next = "http://localhost:39852/" });

        Session.RemoveAll();
        return Redirect(logoutUrl.AbsoluteUri);
    }

    return RedirectToAction("Index", "Home");
}

next パラメーターに指定する値は、ユーザーが Facebook からログアウトした後に使われる URL です。 ローカル コンピューターでテストする場合は、ローカル サイトの正しいポート番号を指定します。 運用環境では、contoso.com などの既定のページを指定します。

アクセス トークンが必要なユーザー情報を取得する

アクセス トークンを格納し、Facebook C# SDK パッケージをインストールしたので、これらを一緒に使って Facebook に追加のユーザー情報を要求できます。 ExternalLoginConfirmation メソッドで、アクセス トークンの値を渡して、FacebookClient クラスのインスタンスを作成します。 現在の認証済みユーザーの verified プロパティの値を要求します。 verified プロパティは、Facebook が携帯電話にメッセージを送信するなど、他の方法でアカウントを検証したかどうかを示します。 この値をデータベースに保存します。

if (user == null)
{
    // Insert name into the profile table
    UserProfile newUser = db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
    db.SaveChanges();

    bool facebookVerified;

    var client = new Facebook.FacebookClient(Session["facebooktoken"].ToString());
    dynamic response = client.Get("me", new { fields = "verified" });
    if (response.ContainsKey("verified"))
    {
        facebookVerified = response["verified"];
    }
    else
    {
        facebookVerified = false;
    }

    db.ExternalUsers.Add(new ExternalUserInformation 
    { 
        UserId = newUser.UserId, 
        FullName = model.FullName, 
        Link = model.Link, 
        Verified = facebookVerified 
    });
    db.SaveChanges();

    OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
    OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

    return RedirectToLocal(returnUrl);
}

もう一度、データベース内のユーザーのレコードを削除するか、別の Facebook アカウントを使う必要があります。

アプリケーションを実行し、新しいユーザーを登録します。 ExtraUserInformation テーブルを参照して、Verified プロパティの値を確認します。

まとめ

このチュートリアルでは、ユーザー認証と登録データのために Facebook と統合されたサイトを作成しました。 MVC 4 Web アプリケーションに設定されている既定の動作と、その既定の動作をカスタマイズする方法について説明しました。