!slug gsuite_sso_private_gitbook

!date 2017/05/06

!draft false

!tags Node.js,G Suite,SSO,SAML,Single Sign On,gitbook,Express

!! gitbookが便利なので会社のドキュメントをgitbookにしたいけどpublicにするわけにはいかない。gitbook.comのbeta?では有償プランでSSOやってるっぽいのだけど何故かアカウントが作成できません(2018/04/05現在)そこでNode.js + expressでG SuiteをIdpとしたSAML認証を実現してみました。...

gitbookが便利なので会社のドキュメントをgitbookにしたいけどpublicにするわけにはいかない。gitbook.comのbeta?では有償プランでSSOやってるっぽいのだけど何故かアカウントが作成できません(2018/04/05現在)そこでNode.js + expressでG SuiteをIdpとしたSAML認証を実現してみました。

gitbook自体の使い方は 最低限の知識で gitbook を利用する に譲りまして、これで作成した静的ファイルを以下で説明するExpressアプリの public ディレクトリに配置する前提です。

ExpressでG SuiteとSSO

Node.jsのExpressフレームワークを使ってG SuiteのSAMLリクエストを受け付けるWebサーバを構築します。

認証基盤なので express-session, cookie-parser, passport など認証・セッション管理に必要なモジュールを使用します。また、今回はG SuiteのSAML認証に対応するため passport-saml ライブラリを使います。

ソースコードは以下のようになります。

'use strict'
const express = require('express')
const session = require('express-session')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const passport = require('passport')
const SamlStrategy = require('passport-saml').Strategy
const app = express()

// SAML認証で取得するユーザ情報をシリアライズしてセッションに書き込む
passport.serializeUser(function (user, done) {
  done(null, user);
})

// SAMLリクエストにあるユーザ情報をデシリアライズ
passport.deserializeUser(function (user, done) {
  done(null, user);
})

// passport-samlの設定
// entryPoint: SSOログイン時にアクセスするIdPのURL
// issuer: IdPに提供される本アプリケーション(SP)の識別子
// path: IdPでのSSOログインが行われた場合にコールバックされるURL
// cert: SP initiated SSO(SP起点のSSO)に必要なSAML証明書情報
passport.use(new SamlStrategy({
    entryPoint: process.env.ENTRYPOINT,
    issuer: process.env.ISSUER,
    path: '/auth/saml/callback',
    protocol: 'https://',
    cert: process.env.CERT
  }, function (profile, done) {
    return done(null, {
      email: profile.email
    })
  })
)

// Expressでセッションを使用するための設定
app.use(cookieParser())
app.use(session({
  secret: process.env.SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 30 * 60 * 1000
  }
}))

// passportを使用するための設定
app.use(bodyParser.urlencoded({ extended: true }))
app.use(passport.initialize())
app.use(passport.session())

// 認証が通れば `/` にリダイレクト
// 認証NGの場合は `/login` にリダイレクト
app.get('/login', passport.authenticate('saml', {
  successRedirect: '/',
  failureRedirect: '/login'
}))

// IdPでのSSOログイン後コールバックされるURL
app.post('/auth/saml/callback', passport.authenticate('saml', {
  successRedirect: '/',
  failureFlash: true
}), function(req, res) {
  res.redirect('/login')
})

// すべてのリクエストにSAML認証をかける(設定順序注意)
app.all('*', function (req, res, next) {
  console.log(req.isAuthenticated())
  if (req.isAuthenticated() || req.path == '/login') {
    next()
  } else {
    res.redirect('/login')
  }
})

// SAML認証されれば `public` ディレクトリの静的サイトを表示(設定順序注意)
app.use(express.static('public'))

app.listen(process.env.PORT || 3000)

module.exports = app

注意点として app.allapp.use(express.static('public')) は、この順序で設定しないとSAMLリクエストとかコールバックが動かないです。もっと良い方法があるのかもしれませんが、とりあえず動いたので… もっと良い方法あったら教えてください!

とりあえずコレをHerokuにデプロイします。あとは、G Suiteの設定をしてHerokuアプリの環境変数を設定すればOKです。

G Suiteの設定

当然ながらG Suiteは管理者権限が必要です。 https://admin.google.com/<DOMAIN>.com にアクセスします。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b3e1eca7-a4fd-4cb0-a902-823fa52b0191/gsuite-setting1.png

次に、[セキュリティ] -> [シングルサインオン (SSO) の設定] と進みます。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64de0edf-4ed6-49c1-b8e4-d5e77cbe830d/gsuite-setting2.png