ほどよいサンプルがなかったので作った。
Express4 のインストール
WebStorm 10 のテンプレートで「Node.js Express App」を選んで自動的に作成。
Template は Jade。
モジュールはこんな感じ
package.json
{
"name": "NodeExpPassportOAuth2_SocketIO",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.12.4",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"express": "~4.12.4",
"jade": "~1.9.2",
"morgan": "~1.5.3",
"serve-favicon": "~2.2.1",
"passport": "^0.2.2",
"express-session": "^1.11.2",
"mongoose": "^4.0.4",
"passport-google-oauth": "^0.2.0",
"socket.io": "^1.3.5",
"cookie": "^0.1.3",
"passport.socketio": "^3.5.1",
"connect-mongo": "^0.8.1"
},
"main": "app.js",
"devDependencies": {},
"author": "",
"license": "ISC"
}
このような。
MongoDB を準備する
いろいろ保存しておきたいので MongoDB を使う。
データのモデルは models/user.js に作る
modeles/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = new Schema({
// User ID (google account id)
uid: {
type: String,
unique: true
},
// display name
displayName: {
type: String
},
// E-mail
email: {
type: String
},
// icon
icon: {
type: String
}
}, {
// define this collection's name explicitly
collection: "users"
});
module.exports = mongoose.model('User', User);
app.js
var User = require('./models/user.js');
mongoose.connect('mongodb://localhost/passport-google-oauth-example');
var mongoose = require('mongoose');
Passport で Google アカウントと連携する
passport と passport-google-oauth を使う。
app.js
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
// session serializer
passport.serializeUser(function(req, uid, done) {
done(null, uid);
});
// session deserializer
passport.deserializeUser(function(req, uid, done) {
// MongoDB からユーザーを取ってくる
User.findOne({
uid: uid
}, function(err, user) {
done(null, user);
});
});
// Google アカウントで OAuth2 接続
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID, //ご自分のやつをどうぞ
clientSecret: GOOGLE_CLIENT_SECRET, //ご自分のやつをどうぞ
callbackURL: CALLBACK_URL //ご自分のやつをどうぞ
},
function(accessToken, refreshToken, profile, done) {
proce ss.nextTick(function() {
//認証情報を DB に保存
var uid = profile.id;
var displayName = profile.displayName;
var email = profile.emails[0].value;
var icon = profile.photos[0].value;
User.findOneAndUpdate({
uid: uid
}, {
$set: {
uid: uid,
displayName: displayName,
email: email,
icon:icon
}
}, {
upsert: true
}, function(err, user) {
return done(null, uid);
});
}
);
}));
SessionStore に MongoDB を使う
connect-mongo が良いらしい。
app.js
var MongoStore = require('connect-mongo')(session); //Express のセッションを引数に渡す
var sessionStore = new MongoStore({ mongooseConnection: mongoose.connection });
//Express のセッションを設定
app.use(session({
key: 'express.sid', //socket.io から参照する際にキーとして使ってるっぽい
secret: "session_secret",
saveUninitialized: true,
store: sessionStore, //passport-socketio のセッションストアに使えるやつじゃないとエラー出る
resave: false
}));
app.use(passport.initialize());
app.use(passport.session());
router まわりを設定する
routes/index.js
// Google アカウントでログインするためのリンク
router.get('/auth/google', passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/plus.login', "email", "profile"]
}),
function(req, res) {} // this never gets called
);
// Google OAuth のコールバックを設定
router.get('/oauth2callback', passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/login'
}));
/* logout */
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
あとは Socket IO から諸々設定するだけだ!
var io = require('socket.io')(server);
var passportSocketIo = require("passport.socketio");
//Passport のセッションを SocketIO からも使えるように
io.use(passportSocketIo.authorize({
passport : passport,
cookieParser: require('cookie-parser'),
key: 'express.sid',
secret: 'session_secret',
store: sessionStore, //さっきの sessionStore 渡す
success: onAuthorizeSuccess,
fail: onAuthorizeFail
}));
// セッションのAuthorize 成功
function onAuthorizeSuccess(data, accept){
console.log('successful connection to socket.io');
accept(null, true);
}
//セッションのAuthorizeしっぱいしっぱい
function onAuthorizeFail(data, message, error, accept){
if(error) {
throw new Error(message);
}
console.log('failed connection to socket.io:', message);
accept(null, false);
}
//接続があったら
io.on('connection', function(socket) {
console.log('socket-io connect');
var user = socket.request.user; //これでユーザーを参照できる
if(user){
console.log("session data : ", user);
}
});
HTML側から
Google アカウントでログインしたあとに、普通に SocketIO に connect してください。
var socket = io.connect();
ややこしいところ
- Express 3 と 4 で Passport まわりの挙動がいちいち違う。
- Google アカウントの認証モジュールがたくさんあって困る。 しかも先日、 OAuth2しかつながらなくなったので使い物にならないものも多い。 npm のサイトで DL 数の多いやつを選ぶとよいかも。
- 同様に、Socket IO と Passport を繋ぐモジュールがたくさんあって困る。
- Passport の sessionStore では動くけど Socket.IO の sessionStore に指定できないやつが結構ある。
このへん https://github.com/senchalabs/connect/wiki#session-stores を見るといいらしい。