一言で言うと、ドキュメント型データベースとそのライブラリからなるシステムです。ドキュメント型データベースはMongoDBが有名です。Firebaseはデータ全体がJSONです。Firebaseのデータをビジュアルに編集できるWebページを提供しています。
これだけなら単にMongoDBをWebサービスにしても同じことです。しかし、Firebaseが面白いのはAngularJSなどのクライアントサイドフレームワークとの相性が抜群だということです。AngularJS用にAngularFireというライブラリを提供しています。AngularFireでは、MVCをクライアントサイドで実現します。AngularFireのModelは、DOM、Firebaseと3方向に同期されます。その結果、クライアント単体でアプリケーションを実現します。つまり、アプリケーショサーバがいらないのです。
Firebaseにはホスティングの機能があり、Webサーバとしても働きます。しかし、それはWebページを配信するためのものであり、サーバ側で処理するアプリケーションサーバを意味しません。
Webアプリケーションは3層モデルを採用してきました。3層からアプリケーションサーバが除かれると2層のクライアントサーバに戻ります。しかし、アプリケーション配信が不要などWebアプリのメリットは維持しています。
なお、その他のJavaScriptでも同じく2層モデルを構成できます。しかし、JavaScript以外の言語では従来通り3層モデルとなります。
2層化にはセキュリティの問題があります。クライアントに自由にデータベースを操作することを許すので、不正アクセスも簡単です。よって、読み取り専用データを除けば、必ず認証(Authentication)・承認(Authorization)と組み合わせる必要があります。
FirebaseではOAuthのライブラリも提供しています。GoogleでログインするWebアプリも簡単に作れます。OAuthのクライアントIDおよび秘密鍵はFirebase側に登録され、リダイレクトページも用意されます。承認はFirebase側でセキュリティルールを設定することで実現します。一般的には、ログインしたユーザだけがアクセスできるように設定します。
問題となるのは、クライアントコードが不正に改変された場合です。特にOAuthを使うと、例えばGogoleのユーザなら誰でも不正アクセスすることができます。そのため、OAuthに加えて独自の絞り込みが必要になります。なお、OAuthには送信元が登録されるので、クライアントコードをコピーしてもOAuth認証を回避できるわけではありません。
実際に、AugularFireで認証付きアプリを作成しようとすると、情報が少ないことに驚きます。そこで、具体的な方法を紹介します。ただし、この方法は試行錯誤の結果なので、必ずしもベストあるいは公式に推奨される方法とは限りません。
ポイントは$firebaseと$firebaseAuthの2つを使う点です。
$firebaseAuthからFirebaseデータにアクセスできるかと思って試したのですが、どうやら認証専門のようです。二重の参照は美しくありませんが、やむをえません。$firebaseAuthが$firebaseを継承してくれればよいのですが。
Googleのメールアドレスの中で@toyo.jpを持つユーザだけに限定しています。auth.uidは意味不明の数字なので、{scope: "email"}がかかせません。該当しないユーザは$unauthしてしまえばログインを拒否したことになります。ちなみにメールアドレスはFirebaseのnameにできません。メールアドレスに含まれる特殊記号がはじかれます。
var app = angular.module("sampleApp", ["firebase"]);
app.controller("SampleCtrl", ["$scope", "$firebase", "$firebaseAuth", function($scope, $firebase, $firebaseAuth) {
var ref = new Firebase("https://minoru-uehara.firebaseio.com/");
var auth = $firebaseAuth(ref);
$scope.logout = function() {
auth.$unauth();
};
$scope.login = function() {
auth.$authWithOAuthPopup('google', {scope: 'email'});
};
auth.$onAuth(function(authData) {
$scope.authData = authData;
if (authData) {
var mail = authData.google.email;
if (! (/@toyo.jp$/i).test(mail)) {
auth.$unauth();
} else {
var denys = [
/^[A-Za-z]{2}[0-9]{2}[A-Za-z0-9]+@toyo.jp$/i,
/^[A-Za-z][0-9][A-Za-z0-9]+@toyo.jp$/i
];
for(var i = 0; i < denys.length; i++) {
if (denys[i].test(mail)) {
auth.$unauth();
}
}
}
// unauthさせずに到達すればOK
// 共有データ
$scope.data = $firebase(ref.child('data')).$asObject();
// プライベートデータ
$scope.users = $firebase(ref.child('users').child(authData.uid)).$set({email: authData.google.email});
}
});
}]);
0 件のコメント:
コメントを投稿