GoInstantのGuest UserとAuthenticated User

Posted by joeartsea on 2014-04-01

GoInstantはリアルタイムアプリケーションのためのプラットフォームでFirebaseが近いです。

具体的に言うと、これまでWebSocketなどで実装していたチャット等のリアルタイムアプリケーションを、少ないコードで簡単に構築することができます。

どれくらい簡単かというと以下のような少ないコードでチャットアプリケーションが動いております。

上記チャットアプリケーションのソースコードはGoInstantのWidgetsを使っています。他にもFormAudio and Videoなどがあります。

GoInstantの基本的な概念にRoomがあります。前述したWidgetsも全てRoomの存在を前提にしています。先ほどのChatアプリケーションのように明示的にRoomを指定しない場合はデフォルトである"lobby"というRoomにjoinします。

1
2
3
4
goinstant.connect(url, function (err, platformObj, roomObj) {
if (err) { throw err; }
console.log(roomObj.name); // lobby
});

明示的にRoomを指定してjoinする場合は以下のようにオプションを指定します。

1
2
3
4
goinstant.connect(url, { room: 'test' }, function (err, platformObj, roomObj) {
if (err) { throw err; }
console.log(roomObj.name); // test
});

ところで、これまでRoomにjoinしていたUserは誰でしょうか?以下で確認するとGuestだと解ります。

1
2
3
4
5
6
goinstant.connect(url, { room: 'test' }, function (err, platformObj, roomObj) {
if (err) { throw err; }
roomObj.users.get(function (err, users, context) {
console.log(users, context.userId); // guest:~
});
});

つまり、明示的にユーザを指定しないでRoomにjoinした場合はGuestとなります。明示的にユーザを指定する場合は以下のようにします(上記までのソースコードを試していたセッションを廃棄した状態で試しましょう)

1
2
3
4
5
6
goinstant.connect(url, { user: { displayName: 'test' }, room: 'test' }, function (err, platformObj, roomObj) {
if (err) { throw err; }
roomObj.users.get(function (err, users, context) {
console.log(users[context.userId].displayName); // test
});
});

もちろん、こんなのだけじゃ本人だと認証されるものではありませんが、例えばゲストユーザへの招待メールで特定のRoomのURLを送り、そのメールから来た人には予め指定していたdisplayNameを自動付与するように作っておけば、かなりの確率で本人であることが担保されるように作ることも可能です。

ただし、上記の方法ではGoInstant側はどこまでいってもGuestでしか識別してくれません。試しに先ほどのメールゲスト招待を実装してみてもセッションが切れた頃に再度URLを踏むとdisplayNameは同じですがuserIdは変わります。そうです、GoInstantは別人だと認識しているのです。

※上のチャットアプリで「できたよー」と発言している私の本名で私のアイコン画像が表示されているユーザもGoInstant上ではGuestなんです。

GoInstantで別人だと認識されて何か弊害があるか?

チャットで会話した数日後に同じRoomで続きを始めた時のことを考えてみてください。セッションが切れていれば別人のメッセージとして扱われます(それでも画面上はdisplayNameが同じなら同一人物に見えるけど)

つまり、チャットをするためにアクセスしてくるユーザの本人確認をして、間違いないことを確認してからGoInstantへ「この人は○○サービスの××さんで間違いないわ」とチャットアプリ側で保証してあげる必要があるということです。

で、「チャットをするためにアクセスしてくるユーザの本人確認」はOpenIDとかOpenID Connect(OAuthは認可であって認証ではない)で、それで得た本人情報をjsonにしてbase64して秘密鍵で署名してGoInstantに送ればOK

こういうのをJWT(JSON Web Token)というらしいです。なんだか難しそうですがJava/Node.js/PHP/Rubyのライブラリがあるので簡単です。以下はnode.js+expressでやってみた例です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var express = require("express"),
Signer = require("goinstant-auth").Signer,
signer = new Signer(process.env.GO_APP_KEY);
var app = express();
app.use(express.logger());
// Configuration
app.configure(function () {
app.set('views', __dirname + '/app');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(__dirname + '/app'));
app.use(app.router);
app.engine('html', require('ejs').renderFile);
});
app.get('/', function (req, res) {
res.render('index.html');
});
app.get('/auth', function (req, res) {
signer.sign({
domain: req.param("domain"),
id: req.param("userid"),
displayName: req.param("username")
}, function(err, token) {
if (err) { console.error(err) }
res.send(token);
});
});
var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log("Listening on " + port);
});

/auth のレスポンスでGoInstantから返ってきたtokenを返しているので、これをクライアントのJSで受け取って以下のようにRoomにjoinする時に設定します。

1
2
3
4
5
6
goinstant.connect(url, { user: token, room: 'test' }, function (err, platformObj, roomObj) {
if (err) { throw err; }
roomObj.users.get(function (err, users, context) {
console.log(users[context.userId].displayName); // test
});
});

これでGoInstantに対して「この人は○○サービスの××さんで間違いないわ」と伝えて「おっしゃ、じゃあこのtokenでアクセスしてくれたら同一人物として扱うわ」というやり取りをした感じです。