tag:crieit.net,2005:https://crieit.net/tags/Passport/feed 「Passport」の記事 - Crieit Crieitでタグ「Passport」に投稿された最近の記事 2020-04-27T21:53:23+09:00 https://crieit.net/tags/Passport/feed tag:crieit.net,2005:PublicArticle/15875 2020-04-27T21:53:23+09:00 2020-04-27T21:53:23+09:00 https://crieit.net/posts/slack-ver3 slack流量計ver3をリリースしました。 <h1 id="slack流量計とは"><a href="#slack%E6%B5%81%E9%87%8F%E8%A8%88%E3%81%A8%E3%81%AF">slack流量計とは</a></h1> <p>サービス運営者コミュニティ「<a target="_blank" rel="nofollow noopener" href="https://qiita.com/organizations/admin-guild?page=1">運営者ギルド</a>」のslackの統計情報を可視化するアプリケーションです。</p> <ul> <li><a href="https://crieit.net/posts/slack">ver2リリース記事</a></li> </ul> <p><a href="https://crieit.now.sh/upload_images/c757f121bd19245f1a30d36c3424b0625ea6c755c0971.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c757f121bd19245f1a30d36c3424b0625ea6c755c0971.jpg?mw=700" alt="" /></a></p> <h2 id="ver3で何が変わったの?"><a href="#ver3%E3%81%A7%E4%BD%95%E3%81%8C%E5%A4%89%E3%82%8F%E3%81%A3%E3%81%9F%E3%81%AE%EF%BC%9F">ver3で何が変わったの?</a></h2> <h3 id="VPSに移設"><a href="#VPS%E3%81%AB%E7%A7%BB%E8%A8%AD">VPSに移設</a></h3> <p>これまでslackに統計情報を集計するbotアプリケーションとして運用していましたが、サーバをVPSに移設しました。</p> <h3 id="WEBアプリケーション化"><a href="#WEB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%8C%96">WEBアプリケーション化</a></h3> <p>貴重なslackのリソースを消費せずにWEBで閲覧できるようになりました。(要slackサインイン)</p> <h3 id="puppeteerでのスクリーンショットは廃止"><a href="#puppeteer%E3%81%A7%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%81%AF%E5%BB%83%E6%AD%A2">puppeteerでのスクリーンショットは廃止</a></h3> <h2 id="使っている技術"><a href="#%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E6%8A%80%E8%A1%93">使っている技術</a></h2> <ul> <li>ReactJS</li> <li>NodeJS/Express/TypeScript</li> <li>passport</li> <li>node-cron</li> <li>chart.js</li> <li>slack/client</li> </ul> <h1 id="技術的な話"><a href="#%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E8%A9%B1">技術的な話</a></h1> <h2 id="react-bootstrap-table2をbootstrap CSSを適用せずに作る"><a href="#react-bootstrap-table2%E3%82%92bootstrap+CSS%E3%82%92%E9%81%A9%E7%94%A8%E3%81%9B%E3%81%9A%E3%81%AB%E4%BD%9C%E3%82%8B">react-bootstrap-table2をbootstrap CSSを適用せずに作る</a></h2> <p>分析機能を作ることが多いので、表を多用するのですが、<a target="_blank" rel="nofollow noopener" href="https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/getting-started.html">react-bootstrap-table2</a>というコンポーネントを結構使っています。</p> <p>これまではbootstrap CSSを適用した後にこれでもかというぐらいCSSを上書きしていたのですが、今回は自力でキャレットを実装できました。<br /> ページネーションも<code>list-style-type: none;</code>に気づいてサクサク実装。<br /> CSSいじるの楽しい!</p> <pre><code class="scss">th{ font-size: 12px; .dropdown>.caret{ &::after{ font-family: "Font Awesome 5 Free"; content: "\f0d7"; } } .dropup>.caret{ &::after{ font-family: "Font Awesome 5 Free"; content: "\f0d8"; } } .caret{ &::after{ font-family: "Font Awesome 5 Free"; content: "\f0d7"; } } } ul.pagination{ display: flex; list-style-type: none; text-align: center; li.page-item{ >a.page-link{ text-decoration: none; background-color: #555555; padding: 3px; margin-right: 3px; color: white; } &.active > a.page-link{ text-decoration: none; background-color: #bfbfbf; padding: 3px; margin-right: 3px; color: #555555; } } } </code></pre> <h2 id="passportを使ったslackサインインの実装"><a href="#passport%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Fslack%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%B3%E3%81%AE%E5%AE%9F%E8%A3%85">passportを使ったslackサインインの実装</a></h2> <p>これは前回<a href="https://crieit.net/posts/React-NodeJS-Passport-twitter">twitterサインインの実装をした</a>のが役に立ちました。</p> <p>以下ソース抜粋</p> <pre><code class="javascript">import passportConfig from './passportConfig'; import * as session from 'express-session'; const MySQLStore = require('express-mysql-session')(session); passportConfig(); const passport = require('passport'); app.use(passport.initialize()); app.use(passport.session()); app.use( session({ secret: '******', resave: false, store: new MySQLStore(dbConfig), saveUninitialized: false, cookie:{ httpOnly: false, secure: false, maxage: 1000 * 60 } }) ); </code></pre> <p>passport-slackはシリアライズ/デシリアライズを使っていないようなので、<br /> 実装個所は変わりますが、認証が成功した際にセッションに情報を格納するといけました。</p> <pre><code class="javascript">(req: Request, res) => { req.session.user = req.account; res.redirect(BACKEND); } </code></pre> ckoshien tag:crieit.net,2005:PublicArticle/15828 2020-04-14T20:37:13+09:00 2020-04-14T20:37:13+09:00 https://crieit.net/posts/React-NodeJS-Passport-twitter React/NodeJS/Passportでtwitterログインを実装してみた <p>こちらの記事をベースにしてReactJS/NodeJSのシステムにtwitterログインを組み込んでみた。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/itagakishintaro/items/e5a0481b51e6a17b304c">https://qiita.com/itagakishintaro/items/e5a0481b51e6a17b304c</a></p> <p>主に異なるのは型指定が緩めな<code>なんちゃって</code>typescriptを使っているところか。</p> <h1 id="実装したもの"><a href="#%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%9F%E3%82%82%E3%81%AE">実装したもの</a></h1> <h2 id="蓋々交換機能"><a href="#%E8%93%8B%E3%80%85%E4%BA%A4%E6%8F%9B%E6%A9%9F%E8%83%BD">蓋々交換機能</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://cap-baseball.com/cap_exchange">https://cap-baseball.com/cap_exchange</a></p> <h1 id="技術的なこと"><a href="#%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E3%81%93%E3%81%A8">技術的なこと</a></h1> <h2 id="passport-config.ts"><a href="#passport-config.ts">passport-config.ts</a></h2> <pre><code class="javascript">export default function passportConfig() { var TWITTER_CONSUMER_KEY = "*****"; var TWITTER_CONSUMER_SECRET = "*******"; var passport = require("passport"), TwitterStrategy = require("passport-twitter").Strategy; // Sessionの設定 passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (obj, done) { done(null, obj); }); passport.use( new TwitterStrategy( { consumerKey: TWITTER_CONSUMER_KEY, consumerSecret: TWITTER_CONSUMER_SECRET, callbackURL: "https://********/auth/twitter/callback", }, function (token, tokenSecret, profile, done) { passport.session.user = profile; // tokenとtoken_secretをセット profile.twitter_token = token; profile.twitter_token_secret = tokenSecret; process.nextTick(function () { return done(null, profile); }); } ) ); } </code></pre> <h2 id="auth.ts"><a href="#auth.ts">auth.ts</a></h2> <p>NodeJSでtwitter認証からのコールバックなどを担当するコントローラ。</p> <pre><code class="javascript">import * as express from "express"; import * as session from 'express-session'; import { Request } from "./interface/express.Request"; const passport = require("passport"); export class Auth{ public router: express.Router; constructor() { this.router = express.Router(); this.router.get("/twitter", passport.authenticate('twitter')); this.router.get("/twitter/success", this.success); this.router.get("/twitter/callback", passport.authenticate('twitter', { successRedirect: 'https://******/api/v2/auth/twitter/success', failureRedirect: 'https://******/' }) )} private success(req:Request,res:express.Response):void{ if(req.session.passport !== undefined){ res.json(req.session.passport.user.username); }else{ res.sendStatus(401); } } } </code></pre> <h2 id="express.Requestインターフェースの拡張"><a href="#express.Request%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%AE%E6%8B%A1%E5%BC%B5">express.Requestインターフェースの拡張</a></h2> <pre><code class="javascript">import * as Express from 'express'; export interface Request extends Express.Request { session:any; } </code></pre> <h2 id="app.ts"><a href="#app.ts">app.ts</a></h2> <pre><code class="javascript">import { Auth } from './auth'; import * as session from 'express-session'; import passportConfig from './passport-config'; passportConfig(); const passport = require("passport"); app.use(passport.initialize()); app.use(passport.session()); app.use( session({ secret: '********', resave: false, saveUninitialized: false, cookie:{ httpOnly: true, secure: true, maxage: 1000 * 60 * 30 } }) ); </code></pre> <h2 id="ReactでNodeJS/Passportから認証情報を受け取る"><a href="#React%E3%81%A7NodeJS%2FPassport%E3%81%8B%E3%82%89%E8%AA%8D%E8%A8%BC%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B">ReactでNodeJS/Passportから認証情報を受け取る</a></h2> <p>credentialsオプションが必要。</p> <pre><code class="javascript"> const response = await fetch('/auth/twitter/success', { method:'GET', credentials: "include", headers: { Accept: "application/json", "Content-Type": "application/json", "Access-Control-Allow-Credentials": true } }); </code></pre> <h2 id="認証が終わったタイミングで認証情報を受け取る"><a href="#%E8%AA%8D%E8%A8%BC%E3%81%8C%E7%B5%82%E3%82%8F%E3%81%A3%E3%81%9F%E3%82%BF%E3%82%A4%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%A7%E8%AA%8D%E8%A8%BC%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B">認証が終わったタイミングで認証情報を受け取る</a></h2> <p>認証のためのウインドウを開き、<br /> そのウインドウが閉じられたタイミングで親の画面をリロードする。<br /> リロードの際に認証情報を受け取っている。</p> <pre><code class="javascript">onClick={()=>{ const authWindow = window.open('/auth/twitter','newTab'); var timer = setInterval(function() { if(authWindow.closed) { clearInterval(timer); window.location.reload(); } }, 1000); <span>}</span><span>}</span> </code></pre> ckoshien tag:crieit.net,2005:PublicArticle/14862 2019-03-09T01:04:54+09:00 2019-03-09T01:04:54+09:00 https://crieit.net/posts/Passport-Twitter PassportでTwitterに遷移する前に処理を行う <p>PassportでのTwitterログインは簡単にできるようになっているが、公式のマニュアルだと説明が簡単すぎて、Twitterに遷移する前に処理を入れたい場合にどうすればいいか分かりづらい。</p> <p>例えばログイン時にいたページに戻るためにパラメータにURLを渡して、ログイン後はそこに戻りたい場合。URLをセッションに保存しておきたいので処理を入れたい。</p> <p>結局authenticateはMiddlewareを返すだけなので処理をしたあとそれを呼べばよいだけ。</p> <pre><code class="javascript"> app.get('/auth/twitter', (req, res, next) => { if (req.query.back) { req.session.loginBack = req.query.back } passport.authenticate('twitter')(req, res, next) }) </code></pre> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14796 2019-02-13T21:59:32+09:00 2019-02-15T21:21:50+09:00 https://crieit.net/posts/passport-twitter-Failed-to-find-request-token-in-session passport-twitterでFailed to find request token in sessionエラー <p>久しぶりにpassport-twitterを使ってみたところ、Failed to find request token in sessionというエラーが出てログインができませんでした。</p> <p>このエラーを調べてみると、だいたいよく見つかる情報として、localhostだったのに127.0.0.1に戻って来てるから、とか、とりあえず127.0.0.1に統一しておけばいい、のような情報が見つかります。</p> <p>しかし今回はどうも統一してもうまくいかずエラーが改善しなかったため、他に原因がありそうでした。</p> <p>詳しくはわかりませんが、セッションの設定を下記のようにすることで改善しました。</p> <pre><code class="javascript">app.use( require('express-session')({ secret: process.env.APP_KEY, resave: true, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV == 'production' } }) ) </code></pre> <p>resave, saveUninitialized, cookieを上記のように指定しています。元々resaveとsaveUninitializedはデフォルトがあるだろうしそのままでいいだろう、と思って省略していたのですが、よくよく見るとビルド時に下記のようなログが出ていました。</p> <pre><code>express-session deprecated undefined resave option; provide resave option server/index.ts:46:35 express-session deprecated undefined saveUninitialized option; provide saveUninitialized option server/index.ts:46:35 </code></pre> <p>READMEに</p> <blockquote> <p>The default value is true</p> </blockquote> <p>とあったのでデフォルトがあると思っていたのですが、よくよく続きを読むと</p> <blockquote> <p>The default value is true, but using the default has been deprecated</p> </blockquote> <p>ということで無指定はdeprecatedでした。よくよく調べ直して設定してみたところうまく動くようになりました。とりあえずsaveUninitializedがデフォルトと違うようなのでこちらが問題だったのかもしれません(正直このオプションによる挙動の違いはマニュアルを見ても全くわからないのですが…)。何にしろ同様のパターンでうまくいかない方はこのあたりを調整してみると良いかもしれません。</p> <p>ちなみに、express-mysql-sessionを使用した時はresaveはtrueにしないと保存したセッション情報を何度も別のもので上書きしてしまうようでした。使用しているstoreによっても変わってくるようなので適宜確認して見る必要がありそうです。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14506 2018-08-16T12:02:02+09:00 2018-10-30T15:42:25+09:00 https://crieit.net/posts/Nuxt-js-Express-Twitter Nuxt.js+Expressで簡単にTwitter認証 <p>Nuxt.jsとExpressでアプリケーションを作成する場合のTwitter認証でのログインを試してみました。Node.jsでのTwitter認証はPassportというライブラリを使用すると非常に簡単でした。細かく検証はしていませんがSSR(サーバーサイドレンダリング)の場合でも問題ないと思われます。</p> <p>このあたりで作ったプロジェクトをベースにして試しています。</p> <p><a href="https://crieit.net/posts/Nuxt-js-Express-ORM">Nuxt.js+Expressでとにかく簡単にORM</a></p> <ul> <li>Nuxt.js v1.4.2</li> <li>Passport-twitter v1.0.4</li> </ul> <h2 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h2> <p>何はともあれまずはインストールします。Passport本体とTwitter用の拡張です。</p> <p>基本的なところは公式のマニュアル通りですので、よくわからなくなった場合やバージョンが違う場合はそちらを参考にしてください。</p> <p><a target="_blank" rel="nofollow noopener" href="http://www.passportjs.org/docs/twitter/">Documentation: Twitter</a></p> <pre><code class="sh">yarn add passport passport-twitter </code></pre> <h2 id="初期設定"><a href="#%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A">初期設定</a></h2> <h3 id="Passportの設定"><a href="#Passport%E3%81%AE%E8%A8%AD%E5%AE%9A">Passportの設定</a></h3> <p>まずはplugins/passport.jsとして初期化スクリプトを作成します。<code>TWITTER_CONSUMER_KEY</code>等は環境変数として指定しておいてください。(dotenv等を使うと便利です)</p> <p>下記はSequelizeでユーザーデータとして保存する場合のサンプルも入れています。</p> <pre><code class="javascript">var passport = require("passport"), TwitterStrategy = require("passport-twitter").Strategy; module.exports = function(app) { const models = app.get("models"); const User = models.User; passport.use( new TwitterStrategy( { consumerKey: process.env.TWITTER_CONSUMER_KEY, consumerSecret: process.env.TWITTER_CONSUMER_SECRET, callbackURL: `${process.env.APP_URL}/auth/twitter/callback`, includeEmail: true // メールアドレスが必要な場合 }, async function(token, tokenSecret, profile, done) { let user = await User.findOne({ where: { twitter_id: profile.id } }); if (!user) { user = User.build({ unique_id: profile.username, name: profile.displayName, email: profile.emails[0].value, location: profile._json.location, bio: profile._json.description, url: profile._json.url, image: profile.photos[0].value, twitter_id: profile.id, twitter_token: token, twitter_secret: tokenSecret }); await user.save(); } done(null, user.get({ plain: true })); } ) ); passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(user, done) { done(null, user); }); return passport; }; </code></pre> <h3 id="認証用ルーティング"><a href="#%E8%AA%8D%E8%A8%BC%E7%94%A8%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">認証用ルーティング</a></h3> <p>認証用のルーティングを作成します。今回はroutes/auth.jsとしてファイルを分けました。アクションにそのままPassportの処理を指定するようなので下記のようにしてルーティング設定全体を関数化して読み込む形にしました。</p> <pre><code class="javascript">const { Router } = require("express"); module.exports = function(app, passport) { const router = Router(); router.get("/twitter", passport.authenticate("twitter")); router.get( "/twitter/callback", passport.authenticate("twitter", { successRedirect: "/", failureRedirect: "/" }) ); app.use("/auth", router); }; </code></pre> <h3 id="アプリケーションに組み込み"><a href="#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF">アプリケーションに組み込み</a></h3> <p>これらをserver/index.jsで読み込んでアプリケーションに組み込みます。</p> <pre><code class="javascript">const passport = require("../plugins/passport")(app); app.use( session({ secret: "your secret" }) ); app.use(passport.initialize()); app.use(passport.session()); require("../routes/auth")(app, passport); </code></pre> <p>基本的な部分はこれで完了です。あとは実際に動かしていくための処理を入れていきます。</p> <h2 id="Nuxt.jsと連携させる"><a href="#Nuxt.js%E3%81%A8%E9%80%A3%E6%90%BA%E3%81%95%E3%81%9B%E3%82%8B">Nuxt.jsと連携させる</a></h2> <p>PassportでのログインはExpress側のセッションに保存しているだけのため、これをNuxt.js側にも渡します。アプリケーション全体に絡むことのため、Storeを使ってNuxt.js上のどこからでもアクセスできるようにします。Nuxt.jsの場合は下記のように適当にファイルを一つ追加するだけで簡単にStoreが利用できます。</p> <p>store/index.js</p> <pre><code class="javascript">export const state = () => ({ authUser: null }); export const mutations = { setUser(state, authUser) { state.authUser = authUser; } }; export const actions = { nuxtServerInit({ commit }, { req }) { if (req.session.passport && req.session.passport.user) { commit("setUser", req.session.passport.user); } } }; </code></pre> <p>Storeの場合<code>setUser</code>でなく<code>SET_USER</code>が使われることが多い気がします。</p> <p>あとはもう下記のようにゆるふわな感じで適当にログインしているか判断できます。</p> <pre><code class="html"> <div v-if="$store.state.authUser"> <img :src="$store.state.authUser.image"> </div> <span v-if="!$store.state.authUser"> <a href="/auth/twitter">ログイン</a> </span> </code></pre> <p>スクリプト側でも<code>this.$store.state.authUser</code>みたいな感じで利用できます。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>荒い感じの書き方も一部あるかもしれませんが、とりあえず非常に簡単にTwitterログインを行ってNuxt.jsと連携させることができました。また動きのおかしいところなどあれば随時追記していきます。</p> <p>他にもGoogleアカウントのログインなども公式のドキュメントに書かれています。</p> だら@Crieit開発者