lycheejam's tech log

チラ裏のメモ帳 | プログラミングは苦手、インフラが得意なつもり。

ASP.NET MVC CoreTweetを使ってツイートを投稿する。

概要

前回のTwitter連携の続き
kitigai.hatenablog.com

これをな?こうして

f:id:HM_Atlas:20180104214556p:plainf:id:HM_Atlas:20180104214618p:plain

こうじゃ!

見たいな感じです。

実行環境

ソース

github.com
APIキーとかはリポジトリに残らないようコミットしたから大丈夫なはず?

CoreTweetの入手

NuGetから入手する。
f:id:HM_Atlas:20180104224641p:plain
github.com

CoreTweet.Streaming.ReactiveはStreamingAPIを使用する為に使うので
今回はインストールの必要はありません
(使ったことはない。調べて何に使うのかはわかった)

磯野~、ツイートしてみよーぜ

前回の記事で連携はできてるじゃん?じゃあツイートできるっしょ

と、その前にツイート用にコントローラーとかモデルを用意する

  • Controllers/TweetController.cs

   Indexの表示
   TweetPostの表示
   ツイートトークンの作成
   ツイートの実行

  • Models/TweetViewModels.cs

   ツイート内容を入れるオブジェクト

  • View/Tweet/Index.cshtml

   ツイートする画面

  • View/Tweet/TweetPost.cshtml

   ツイート結果を表示する画面

この4つを用意
Controllerでやっているツイート云々はモデルでやるべき部分っぽい?

流れとしては
http://localhost/Tweetにアクセス → ツイート入力&ボタン押下 →
ActionResultで値を受け取る → トークン生成 → ツイート → TweetPost表示
みたいな感じです。

早速ツイートしてみる。

public ActionResult TweetPost(TweetViewModels tt)
{
    var session = CoreTweet.OAuth.Authorize("API-Key", "API-Secret");
    var url = session.AuthorizeUri;
    
    tokens = CoreTweet.OAuth.GetTokens(session, "PIN code");
    
    tokens.Statuses.Update(status => DateTime.Now + " " + tt.TweetText);
    return View(tt);
}

はえ?なんもおこらんぜよ?
と言うかPIN codeって何ぞ?適当じゃだめ?

とりあえず認証時に登録されたデータを見てみる
f:id:HM_Atlas:20180104235343p:plain

どれかわからないから全部”それっぽい”のを全部試してみたけどダメでした。
とりあえず「CoreTweet PIN code」でググる

なにやら見つけた・・・がせっかくASP.NETのテンプレートがあるのだからお手軽じゃない!
とか思ってたら同じ方がASP.NETでのやり方書いてくれてました。
blog.nakajix.jp

PINcodeではなくAccessTokenとAccessTokenSecretを使うらしい
そしてASP.NET標準の認証コードじゃそれが取得できないらしい

なのでインターネットコピペマン登場
書いてある通りにやります。
修正する箇所は前回のツイッター連携と同じ下記のファイルです。

App_Start/Startup.Auth.cs
var options = new TwitterAuthenticationOptions();
options.ConsumerKey = "API-Key";
options.ConsumerSecret = "API-Secret";
options.BackchannelCertificateValidator = new CertificateSubjectKeyIdentifierValidator(new[]{
    "A5EF0B11CEC04103A34A659048B21CE0572D7D47", // VeriSign Class 3 Secure Server CA - G2
    "0D445C165344C1827E1D20AB25F40163D8BE79A5", // VeriSign Class 3 Secure Server CA - G3
    "7FD365A7C2DDECBBF03009F34339FA02AF333133", // VeriSign Class 3 Public Primary Certification Authority - G5
    "39A55D933676616E73A761DFA16A7E59CDE66FAD", // Symantec Class 3 Secure Server CA - G4
    "5168FF90AF0207753CCCD9656462A212B859723B", //DigiCert SHA2 High Assurance Server CA 
    "B13EC36903F8BF4701D498261A0802EF63642BC3" //DigiCert High Assurance EV Root CA
});
options.Provider = new TwitterAuthenticationProvider()
{
    OnAuthenticated = async (context) =>
    {
        context.Identity.AddClaim(new Claim("ExternalAccessToken", context.AccessToken));
        context.Identity.AddClaim(new Claim("ExternalAccessTokenSecret", context.AccessTokenSecret));
    }
};

app.UseTwitterAuthentication(options);

証明書のBackchannelCertificateValidatorの部分でエラーが出た記憶があるが
なにがどうだったかどうやって解決したか覚えていない
なんせ年明け前に書いたからね仕方ないね。

とにかくAccessTokenを保存できるようにした。
これをセッション限りではなくて永続化させる(DBに登録?)

Controllers/AccountController.cs
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl){
    if (User.Identity.IsAuthenticated){
        return RedirectToAction("Index", "Manage");
    }
    if (ModelState.IsValid){
        var info = await AuthenticationManager.GetExternalLoginInfoAsync();
        if (info == null){
            return View("ExternalLoginFailure");
        }
        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
        var result = await UserManager.CreateAsync(user);
        if (result.Succeeded){
            result = await UserManager.AddLoginAsync(user.Id, info.Login);
            if (result.Succeeded){
                //追記スタート
                //コピペ元:http://blog.nakajix.jp/entry/2014/09/12/074000
                // ユーザ情報の登録が完了してから、AccessTokenとAccessTokenSecretを永続化する
                var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
                var claims = claimsIdentity.Claims;
                foreach (var claim in claims){
                    if ((claim.Type == "ExternalAccessToken") || (claim.Type == "ExternalAccessTokenSecret"))
                        await UserManager.AddClaimAsync(user.Id, claim);
                }
                // ここまで

                await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
                return RedirectToLocal(returnUrl);
            }
        }
        AddErrors(result);
    }
    ViewBag.ReturnUrl = returnUrl;
    return View(model);
}

詳細まではって感じだけどTwitterの認証が成功しているか確認して
メールアドレスを入力したらAccessToken(Secretも)をテーブルに追加する感じ?

それじゃーAccessToken発行いってみよー
ヘッダーメニュー右のログインから
Twitter連携を行う
連携後メールアドレス登録画面にリダイレクト(?)されるので適当に登録
f:id:HM_Atlas:20180105010500p:plain

そうするとAspNetUserClaimsテーブルにAccessTokenが登録される
f:id:HM_Atlas:20180105010751p:plain

このAccessTokenを使ってツイートしてみる

public ActionResult TweetPost(TweetViewModels tt)
{
    var tokens = CoreTweet.Tokens.Create("API-Key", "API-Secret", "AccessToken", "AccessTokenSecret");
    tokens.Statuses.Update(status => DateTime.Now + " " + tt.TweetText);
    return View(tt);
}

これで最初の概要に貼ったツイートにつながります。

課題

  • NuGetってよく聞くけど何者?
  • contextきみなんぞや?
  • ついでにIdentity君なんぞや?
  • 自作のテーブル追加をやる

qiita.com

  • OAuthとかよくわかってない
  • DBからデータの取り出し

ほんとはToken生成している部分でテーブルからデータを読み込んで
直書きではなくていい感じに書きたかった

雑感

寄り道してプログラム書いて予想以上に時間かかったり
Identityが何者かわからなかったり
公式チュートリアルやるのが一番の近道っすかね