Node.js
Express.js
OAuth2.0

nodeでシンプルにOAuth2認証(google, facebook)

OAuth2の認証フローを知りたかったので、ソーシャルログイン後、メールアドレスを取得するところまでを実装してみました。
抽象化を防ぐため、可能な限りサードパーティのライブラリは使ってません。

事前準備

Google, Facebookそれぞれのdeveloperページでアプリを作成し、クライアントIDクライアントシークレットを取得しておく。
またリダイレクトURL(今回はhttp://localhost:3000/oauth2callback)も忘れずに登録しておく。

Google: https://console.cloud.google.com/apis/
Facebook: https://developers.facebook.com/apps/

ググったら親切なページがいっぱい出てくるので、具体的なやり方については割愛します。


また、プロジェクトディレクトリで必要なライブラリをインストールしておきます。

$ npm i -S express node-fetch

Google

google.js
const express = require('express')
const fetch = require('node-fetch')
const qs = require('querystring')

const client_id = '{クライアントID}'
const client_secret = '{クライアントシークレット}'
const redirect_uri = 'http://localhost:3000/oauth2callback'
const response_type = 'code'
const scope = 'email'

const auth_uri = 'https://accounts.google.com/o/oauth2/v2/auth'
const token_uri = 'https://www.googleapis.com/oauth2/v4/token'
const email_uri = 'https://www.googleapis.com/oauth2/v3/userinfo'

const app = express()

app.get('/', (req, res) => {
  const params = qs.stringify({
    client_id,
    redirect_uri,
    response_type,
    scope,
  })
  res.redirect(302, `${auth_uri}?${params}`)
})

app.get('/oauth2callback', (req, res) => {
  fetch(token_uri, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: qs.stringify({
      client_id,
      client_secret,
      code: req.query.code,
      grant_type: 'authorization_code',
      redirect_uri,
    })
  }).then(res => res.json()).then(json => {
    return fetch(email_uri, {
      headers: {
        Authorization: `Bearer ${json.access_token}`,
      }
    })
  }).then(res => res.json()).then(json => {
    res.end(json.email)
  })
})

app.listen(3000, () => console.log('listening on port 3000'))

$ node google.js

ブラウザから http://localhost:3000 にアクセス

Facebook

facebook.js
const express = require('express')
const fetch = require('node-fetch')
const qs = require('querystring')

const client_id = '{クライアントID}'
const client_secret = '{クライアントシークレット}'
const redirect_uri = 'http://localhost:3000/oauth2callback'
const response_type = 'code'

const auth_uri = 'https://www.facebook.com/v2.12/dialog/oauth'
const token_uri = 'https://graph.facebook.com/v2.12/oauth/access_token'
const email_uri = 'https://graph.facebook.com/me'

const app = express()

app.get('/', (req, res) => {
  const params = qs.stringify({
    client_id,
    redirect_uri,
    response_type,
  })
  res.redirect(302, `${auth_uri}?${params}`)
})

app.get('/oauth2callback', (req, res) => {
  let params = qs.stringify({
    client_id,
    client_secret,
    code: req.query.code,
    redirect_uri,
  })
  fetch(`${token_uri}?${params}`)
    .then(res => res.json())
    .then(json => {
      const access_token = json.access_token
      params = qs.stringify({
        access_token,
        fields: 'email',
      })
      return fetch(`${email_uri}?${params}`)
    })
    .then(res => res.json())
    .then(json => {
      res.end(json.email);
    })
})

app.listen(3000, () => console.log('listening on port 3000'))
$ node facebook.js

ブラウザから http://localhost:3000 にアクセス

まとめ

passportやnode-oauthを使った事例はよく見かけるのですが、内部が抽象化されていてフローを理解できなかったので、fetchのみで実装してみました。
リクエストメソッドやパラメータの渡し方に違いはありますが、規格は同じなのでやはり実装は似通っていますね。

何かの参考になれば幸いです。ありがとうございました。