だるろぐ

明日できることは、今日しない。

WebMatrix 3: Twitter でログインする

f:id:daruyanagi:20130905061149p:plain

ちょっと Twitter と連携するアプリを作ってみたかったのだけど、“スターター サイト”テンプレートを使った例以外はあまり載っていなかったので、今回はそれを使わずに、“空のサイト”テンプレートから作ってみるよ。というのも、ググってたら自分のサイトが検索に引っかかって、WebMatrix 2:OAuth でログインする(2) - だるろぐ を放置することすでに半年経つことが判明したので……さすがにこの当時のことはあまりよく思い出せないのだけど、今回の記事がフォローアップのようなものになれば幸い。

下準備

f:id:daruyanagi:20130905063705p:plain

まず NuGet で NuGet Gallery | Microsoft.AspNet.WebPages.OAuth 3.2.7 をインストール。これで“スターター サイト”テンプレートでも使われている OAuthWebSecurity Class (Microsoft.Web.WebPages.OAuth) | Microsoft Docs が使えるようになる。

f:id:daruyanagi:20130905062911p:plain

次に https://dev.twitter.com/apps/ でアプリの登録を行っておく。登録祭の必須入力事項は以下のとおり。

  • Name: アプリの名前
  • Description: title
  • Website: http://127.0.0.1:****/ (localhost は無効な URL として蹴られる)
  • Callback URL: http://127.0.0.1:****/ (空っぽだと動かないっぽい)
  • Allow this application to be used to Sign in with Twitter: 無効化

アプリを登録したら、

  • Consumer key
  • Consumer secret

を取得し、~/_AppStart.cshtml で初期化を行う。

@{
    // DB名: kenzo-memo.sdf
    WebSecurity.InitializeDatabaseConnection("kenzou-memo", "Users", "UserId", "Name", true);  

    OAuthWebSecurity.RegisterTwitterClient(
        "kFe1j**********LTcSizQ",
        "aKz6C**********Qzws06agyxRXImPk9sfETNQeg"
    );
}

これで OAuthWebSecurity の twitter プロバイダーが利用できるようになる。このプロバイダーは OAuth 認証に必要な面倒事の一切を引き受けてくれる。

~/Default.cshtml

f:id:daruyanagi:20130905061617p:plain

ログインしていない場合はログインボタンを、ログインしている場合はログオフのリンクを表示するだけの簡単なコード。

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    @if (User.Identity.IsAuthenticated)
    {
        <p>@User.Identity.Name</p>
        <p><a href="Logout.cshtml">Logout</a></p>
    }
    else
    {
        <a href="~/Login.cshtml">
            <img src="Twitter ボタンを用意する" />
        </a>
    }
    </body>
</html>

資格関連の情報は User.Identity (HttpContext.User Property (System.Web) | Microsoft Docs)でとれる。

~/Login.cshtml

@{
    var returnUrl = Request.UrlReferrer.ToString();

    OAuthWebSecurity.RequestAuthentication(
        "twitter", // プロバイダー名は文字列で指定
        Href("~/LoginCallback", new { returnUrl })
    );
}

OAuthWebSecurity.RequestAuthentication で Twitter へリクエストを投げる。

f:id:daruyanagi:20130905064446p:plain

おなじみのこの画面へ飛ばされる。

~/LoginCallback

Twitter で認証処理を行うと、~/LoginCallback にリダイレクトされてくるので、OAuthWebSecurity.VerifyAuthentication で検証し、成否を得る(今回で言えば result 変数)。Twitter でのログインが成功していたら、その資格情報(OAuth なのでパスワードはないけど)をデータベースに格納し(OAuthWebSecurity.CreateOrUpdateAccount)、アプリにログインさせる(OAuthWebSecurity.Login)。

@{
    var returnUrl = Request["returnUrl"];

    // ログインの検証
    var result = OAuthWebSecurity.VerifyAuthentication(  
         Href("LogonCallBack", new { ReturnUrl = returnUrl })
    );

    if (result.IsSuccessful)
    {
        // ログインが成功すると、
        // - provider: twitter
        // - ProviderUserId: twitter の ID
        // - UserName: twitter のスクリーンネーム
        // の3つが得られる。自動補完が効かないので変数に入れとく
        var provider = result.Provider;
        var providerUserId = result.ProviderUserId;
        var userName = result.UserName;

        // ユーザー名が Users テーブルに存在しない場合、
        // あらかじめユーザー名を追加しておく。
        // でないと CreateOrUpdateAccount() でコケる
        using (var db = Database.Open("kenzou-memo"))
        {
            const string SELECT = "SELECT * FROM USERS WHERE Name=@0";
            const string INSERT = "INSERT INTO Users (Name) VALUES (@0)";

            if (db.QuerySingle(SELECT, userName) == null)
            {
                db.Execute(INSERT, userName);
            }
        }

        // CreateOrUpdate とか言ってるけど、
        // やってることは Users テーブルと内部管理テーブルの紐づけ
        OAuthWebSecurity.CreateOrUpdateAccount(
            provider,
            providerUserId,
            userName);

        // ログインチケットの発行
        OAuthWebSecurity.Login(
            provider,
            providerUserId,
            createPersistentCookie: true);

        Response.Redirect(returnUrl);        
    }
    else
    {
        // ログインに失敗したときの処理
    }
}

f:id:daruyanagi:20130905064840p:plain

f:id:daruyanagi:20130905064842p:plain

成功するとこんなかんじにユーザーがデータベースに追加される。ちなみに result の中身はこんな感じ。

f:id:daruyanagi:20130905064941p:plain

ObjectInfo.Print() はデバッグにとっても便利なヘルパーなのでぜひ覚えておいてね!

~/Logout.cshtml

最後にログアウトの処理も書いておく。

@{
    var returnUrl = Request.UrlReferrer.ToString();

    WebSecurity.Logout();

    Response.Redirect(returnUrl);
}

ログインの時はもっぱら OAuthWebSecurity Class (Microsoft.Web.WebPages.OAuth) | Microsoft Docs を使っていたけれど、ログアウトはいつもどおり WebSecurity Class (WebMatrix.WebData) | Microsoft Docs が使える。

まとめ

  1. OAuthWebSecurity.RegisterTwitterClient で twitter プロバイダーを有効化
  2. OAuthWebSecurity.RequestAuthentication で Twitter の認証画面へ飛ばす
  3. OAuthWebSecurity.VerifyAuthentication で Twitter での認証がうまく言ったか確認
  4. OAuthWebSecurity.CreateOrUpdateAccount でアプリのアカウントと紐付ける
  5. OAuthWebSecurity.Login でアプリにログインする

“スターター サイト”テンプレートは手広い実装になっていて、その分複雑になっているけれど、アプリのアカウント= Twitter アカウントという運用でよいのならば、このように比較的簡単に実装できる。