LoginSignup
9
7

More than 5 years have passed since last update.

Express + passport(-twitter) + (PM2)で最速でTwitter認証機能付きWebAppを作る

Last updated at Posted at 2019-03-07

いくつかTwitter認証を使うWebApplicationを作る過程でTwitterでの認証をテンプレート化させコピペで作れるようにしたのでそれをご紹介します。

この記事で出来るようになること

  • Twitter認証機能を持ったExpressAppをサクッと作れるようになる
  • WebAppとしても,ネイティブ使うAPIエンドポイントとしても使える
  • (後日出来るようになるかも) Swift4でのiOSネイティブからの当APIの使い方がサクッとわかる

前提

コピペで作るためには以下を満たしておく必要があります。

  • TwitterAppを作成できる

    • 以前にTwitterDeveloperの大規模な変更があって、TwitterAPPを作るためには作文を書かなきゃいけなかった気がします。
    • TwitterDeveloperにアクセスしてAppのリストとCreate an app ボタンが表示されればOK.
  • express-generatorが導入されている
    Expressプロジェクトをコマンド一つで生成してくれる便利な奴
    導入とか(公式)

  • PM2が導入されている
    ProcessManager(多分)
    nodeプロジェクトを手軽にデーモン化させられる奴(起動/ホットリロード/クラッシュ時の再起動/startup等々)
    導入とか(公式)

  • ドメインが用意されていてWebAppを公開できるようになっている
    長くなるので省きますが、https://xxx.your.domain/ とかにアクセスして生成したてのExpressを起動した際に

    Express
    Welcome to Express
    

    が表示されればOK.

今回はhttps://qiita.your.domain/というURLでAPIを公開してみたいと思います。

TwitterAppを作成

  1. twitterDeveloperからCreate an appを押し、App作成画面へ移ります。

  2. 必要事項を記述し、Allow this application to be used to sign in with Twitter項目のEnable Sign in with Twitterにチェックを入れます。

  3. Callback URLs項目がアクティブになるので、ここにhttps://qiita.your.domain/auth/twitter/callbackを入力します。
    ※ /auth/twitter/callbackの部分は本来任意ですが、これを変更する場合後述のルーティングのコードも変更する必要があります。

  4. 項目記述後、流れに沿ってAppを作成し最終的に自分のAppsDashboardに作成したAppが表示されていれば準備完了。

  5. AppsDashboardからAppを開き、keys and tokensのConsumer API Keysを確認できるようにしておきます。

実装

Express

まずはExpressを初期化します。

$ express -e qiita-twitter
$ cd qiita-twitter
$ npm i

次にpm2を初期化します

$ pm2 init
$ vi ecosystem.config.js

pm2

pm2の設定を変更します。

ecosystem.config.js
module.exports = {
    apps: [
        {
            name: 'qiita-twitter',
            script: 'npm',
            args: 'start',
            watch: true,
            env: {
                PORT: 3000,
                HOST_NAME: 'qiita.your.domain',
                USE_SSL: true,
                AUTH: {
                    twitter: {
                        active: true,
                        CONSUMER_KEY: 'Your CONSUMER_KEY',
                        CONSUMER_SECRET: 'Your CONSUMER_SECRET',
                    },
                }
            }
        }
    ]
};

nameはpm2上で管理するApp名です。
pm2 logs qiita-twitterとかするとログを見れたりします。

scriptとargsは起動時のコマンドです。
$ npm startで起動するので、このように記述します。

watchはホットリロード(ファイルを変更すると自動再起動してくれるやつ)を使うか否かです。
trueにすると起動ディレクトリ以下全てのファイルを、trueの代わりに['routes', 'models']みたいにするとroutesとmodelsディレクトリ以下を監視してくれるようです。

で、重要なのがenvです。

USE_SSLとかは後ほどコールバックURLを組み立てるときに使っていますが、そこでURLを直打ちしてしまえば必要ありません。

AUTH=>twitter=>activeみたいになってるのは、他にもlocal認証(よくあるメアドとPWの認証)やFaceBook認証とか使う時に分かるやすくなるメリットがあります。

この辺はコードと相談しながらお好みで変更しましょう。

gitで管理する場合ecosystem.config.jsはgitignoreに突っ込むなりしてgit監視下から外しましょう

個人的にはecosystem.config.js.sampleというenvの値を空白にしたものを用意しておき、これをgitに上げておくのがベターかなと思います。

休憩

一先ずここで起動確認をしておきましょう。

$ pm2 start ecosystem.config.js
$ pm2 logs qiita-twitter

適当なブラウザでhttps://qiita.your.domainにアクセスして

Express
Welcome to Experss

が表示されていればOKです。

Twitter認証

いよいよTwitter認証のroutingを実装します。

以下個人的に使っているディレクトリ構成となります。
お試す前にコードを読んで各自改変することをお勧めします。
(contorllers内でrouting設定しちゃってたりと健康被害を及ぼす可能性があります)

packageインストール

$ npm i -S express-session passport passport-twitter

ルーティング

$ mkdir controllers
$ touch controllers/authController.js
$ touch controllers/userController.js
controllers/authController.js

const passport = require('passport');
const TwitterStrategy = require('passport-twitter').Strategy;
const uc = require('./userController');

module.exports = { 
    initialize: function(app) {
        this.app = app;
        this.authSettings = JSON.parse(process.env.AUTH);

        app.use(passport.initialize());
        app.use(passport.session());
        passport.serializeUser(uc.userSerialize);
        passport.deserializeUser(uc.userDeserialize);

        this.twitterActivate();
    },
    twitterActivate: function() {
        if(!this.authSettings.twitter.active) {
            console.log('Twitter Auth is not activated');
            return;
        }
        let consumerKey = this.authSettings.twitter.CONSUMER_KEY;
        let consumerSecret = this.authSettings.twitter.CONSUMER_SECRET;
        let callbackURL = process.env.USE_SSL ? 'https' : 'http';
        callbackURL += '://';
        callbackURL += process.env.HOST_NAME;
        callbackURL += callbackURL.endsWith('/') ? '' : '/';
        callbackURL += 'auth/twitter/callback';
        passport.use(new TwitterStrategy({
            consumerKey: consumerKey,
            consumerSecret: consumerSecret,
            callbackURL: callbackURL
        }, uc.twitterAuth));

        this.app.get('/auth/twitter', passport.authenticate('twitter'));
        this.app.get('/auth/twitter/callback', passport.authenticate('twitter'), async function(req, res) {
            const user = req.session.passport.user;
            res.json(user);
        });
        console.log('Twitter Auth is activated');
    },
}
controllers/userController.js

const ac = require('./authController');

module.exports = {
    twitterAuth: async function(token, tokenSecret, profile, done) {
        //本来ここでユーザー検索して、存在したらそのObjectを返す
        //存在しなければ新規ユーザー作成
        //みたいなことをする。
        //tokenとtokenSecretそのままobjectに格納してログイン状態として扱うのは良くないです。
        const userObj = {
            twitterId: profile.id,
            screenName: profile.username,
            token: token,
            tokenSecret: tokenSecret
        };

        //ログ表示もいくない。
        console.log(userObj);
        done(null, userObj);
    },
    userSerialize: async function(user, done) {
        done(null, user);
    },
    userDeserialize: async function(id, done) {
        console.log('user deserializing');
        console.log(id);
        done(null, id);
    },
};

app.jsから読み込み/初期化

$ vi app.js

最後にapp.jsを編集し、読み込み・初期化を行います。

app.js
//最魚のrequire群でsessionとauthControllerを読み込み(6行目くらい)
const session = require('express-session');
const authController = require('./controllers/authController');

//requireしたモジュール系をuseしている部分でsessionの読み込みをしてからauthControllerをinitialize(順番大事)を追加(23行目くらい)
app.use(session({
    secret: 'kokoikeapi',
    resave: false,
    saveUninitialized: true
}));
authController.initialize(app);

動作確認

最後にコールバックしてきたら、ユーザーのTweet一覧を表示してみましょう.

さらに体に良くないですが、authController内でTwitterモジュールを読み込んでお試し表示してみましょう。

$ npm i -S twitter
controllers/authController.js

//4行目くらいでrequireしておきます。
const Twitter = require('twitter');

// 36行目くらいの/auth/twitter/callbackのルーティングの処理の中身を変更
 const user = req.session.passport.user;
 const consumer = JSON.parse(process.env.AUTH).twitter;
 const client = new Twitter({
     consumer_key: consumer.CONSUMER_KEY,
     consumer_secret: consumer.CONSUMER_SECRET,
     access_token_key: user.token,
     access_token_secret: user.tokenSecret
 });

const params = { screen_name: user.screenName};
client.get('statuses/user_timeline', params, function(err, tweets, response) {
    if(!err) {
        res.json(tweets);
    } else {
        res.json(err);
    }
});

//res.json(user);

これで認証が終わってcallbackしてくると自分のツイート一覧のjsonがresponseに流れます。

本来の使い方としては、userController時点でuserデータを作成し、doneにそのuserデータを渡しておきます。
すると、authControllerで取得するuserにはidが含まれているので、これを使ってユーザーのプロフィールページやトップページにリダイレクトするといいと思います。

controllers/authController.js

this.app.get('/auth/twitter/callback', passport.authenticate('twitter'), async function(req, res) {
    //トップへリダイレクトする場合
    //res.redirect('/');

   //user個別ページへリダイレクトする
   const user = req.session.passport.user;
   res.redirect('/users/' + user.id);

   //みたいな。
});
9
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7