passport-google-oauth20 - Express

passport-google-oauth20 とは

passport-google-oauth20 は、SNSアカウントとの連携によって会員登録やログインを簡単に行えるソーシャルログイン機能です。

利用したパッケージ

  • cookie-parser@1.4.6
  • debug@2.6.9
  • ejs@3.1.10
  • express-session@1.18.0
  • express@4.19.2
  • http-errors@1.6.3
  • morgan@1.9.1
  • passport-google-oauth20@2.0.0
  • passport@0.7.0

インストール

npm install passport passport-google-oauth20 express-session

フォルダ構成

Express Generator で作成します。

  • example
    • bin
      • www
    • middleware
      • middleware.js
    • node_modules
      • ...中略...
    • public
      • images
      • javascripts
      • stylesheets
    • routes
      • content.js
    • views
      • content
        • index.ejs
        • sub.ejs
      • error.ejs
    • app.js
    • package-lock.json
    • package-json

Google 認証

Google 認証を利用するには、Google APIを使用するための設定が必要になります。

Google Developer Console から Google Search Console API の設定を行います。

修正するファイル内容

views/content/index.ejs

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>content/index</h1>
  <p><a href="/logout">logout</a></p>
</body>
</html>

views/content/sub.ejs

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>content/sub</h1>
</body>
</html>

routes/content.js

var express = require('express');
var router = express.Router();
const md = require('./../middleware/middleware');

router.get('/', md.authCheck, function(req, res, next){
  res.render('content/index');
});

router.get('/sub', md.authCheck, function(req, res, next){
  res.render('content/sub');
});

module.exports = router;

middleware/middleware.js

module.exports = {
  authCheck: function(req, res, next){
    if(req.isAuthenticated()) {
      next();
    } else {
      res.redirect('/');
    }
  }
}

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var passport = require('passport'); // +
var GoogleStrategy = require('passport-google-oauth20').Strategy; // +
const session = require('express-session'); // +

var contentRouter = require('./routes/content'); // +

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize()); // +

// +
app.use(session({
  secret: 'secret',
  resave: false,
  saveUninitialized: false,
  cookie:{
    httpOnly: true,
    secure: false,
    maxage: 1000 * 60 * 30
  }
})); 

app.use(passport.session()); // +

// +
passport.serializeUser(function(user, done) {
  done(null, user);
});
// +
passport.deserializeUser(function(user, done) {
  done(null, user);
});
// +
passport.use(new GoogleStrategy({
    clientID: "Google のクライアント ID",
    clientSecret: "Google のクライアントシークレット",
    callbackURL: "設定したロールバック URL", // ここでは、http://localhost:3000/login と設定
    passReqToCallback: true,
  }, function(req, accessToken, refreshToken, profile, done){
      process.nextTick(function(){
        return done(null, profile);
      });
  }
));

app.use('/content', contentRouter); // +
app.get('/', passport.authenticate('google', { scope: ['email', 'profile'] }), (req, res) => {}); // +
app.get('/login' , passport.authenticate('google', {failureRedirect: '/'}) , (req,res)=>{ res.redirect('/content') }); // +

// + google アカウントのログアウトをしないとログアウトできません。
app.get("/logout", (req, res, next) => {
  req.logout((err) => {
    if (err) return next(err);
  });
  res.redirect("http://localhost:3000/");
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

メソッド

serialize : シリアライズ

連載する、順番に並べる

authenticate : オーセンティケーション

認証する

failure : フェイルァ

失敗

uninitialized : アンイニシャライズ

未初期化

done : ダン

終わり、完了、焼けた、[well done(ウェルダン:よく焼けた)]

複数の要素を一列に並べる操作や処理のこと

デシリアライズは、シリアライズされたデータを元のオブジェクトやデータ構造に復元すること。

serializeUser

ユーザー情報をセッションに保存する

deserializeUser

セッション情報からユーザー情報を取得する

isAuthenticated

認証を通過している場合は true を、そうでない場合は false を返します。

profile に入っているデータ

profile にGoogleのアカウントの情報が入ります。

{
  id: 'ID番号',
  displayName: 'Google アカウントの名前',
  name: { familyName: 'Google アカウントの名前', givenName: 'Google アカウントの名前' },
  emails: [ { value: 'Google のメールアドレス', verified: true } ],
  ... 中略 ...
}

routes 内のファイルでメールアドレスを取得するには、「 req["user"]["emails"][0]["value"] 」で取得できます。

req に profile のデータが入っています。「 req["user"]["emails"][0]["value"] 」は、取り出しやすいようにセッションに入れ直したほうがいいと思われます

次のコードはサンプルです。

app.js - 48行目

passport.use(new GoogleStrategy({
    clientID: "Google のクライアント ID",
    clientSecret: "Google のクライアントシークレット",
    callbackURL: "設定したロールバック URL", // ここでは、http://localhost:3000/login と設定
    passReqToCallback: true,
  }, function(req, accessToken, refreshToken, profile, done){
      process.nextTick(function(){
        profile["email"] = profile.emails[0]["value"]; // +
        return done(null, profile);
      });
  }
));

routes/content.js

var express = require('express');
var router = express.Router();
const md = require('./../middleware/middleware');

router.get('/', md.authCheck, function(req, res, next){
  let data = req["user"]["email"]
  res.render('content/index', { data: data });
});

router.get('/sub', md.authCheck, function(req, res, next){
  res.render('content/sub');
});

module.exports = router;

views/content/index.ejs

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>content/index</h1>
  <p>こんにちは、<%= data %></p>
  <p><a href="/logout">logout</a></p>
</body>
</html>