2015年2月28日土曜日

Kindle

Kindleを買った。
今までは、iPhoneにもKindleアプリを入れているので、わざわざ買う理由がなかったのだが、iPhoneでは画面が小さすぎるのでKindle購入に踏み切った。
今回の購入は自分のためではなく、スマホが使えない人向けだ。
紙に比べて便利な点が多くある。
1. 字を拡大できる
2. その場で辞書を引くことができる
スマホに比べても利点がある。
3. 画面が大きい。
4. 消費電力が小さい。
5. 価格が安い。
もちろん欠点も多々ある。しかし、価格や消費電力とのバランスの問題だ。

Kindleを家族で使い分けるには、アカウントで接続した後、コンテンツをダウンロードし、使わせる前にアカウントの接続を削除すればよい。

ここで、電子書籍について改めて考えてみたい。
もしも、すべての書籍が電子化されたらどうなるだろう。
90%は問題がないどころか、便利になるだろう。
しかし、残り10%が解決できない限り十分とはいえない。

例えば、絵本は難しい。
PC並みの大画面のデバイスを使えば、従来以上の絵本ができるのはわかりきっている。しかし、絵本を見る子どものためにそのような高価なデバイスを買う親がどれだけいるだろう。絵本だけでは、デバイスの価格に対して十分な価値があるとはいえないだろう。
むしろ、スマートTVのアプリとして位置づけた方がよいかもしれない。
メディアに応じて出版部門の再編が必要かもしれない。

次は付録付き雑誌だ。
付録というリアルなモノが主役なので、電子書籍にはなりえない。

低学年向き学習教材も電子化が難しい。
ユーザの年齢が、デバイス自体を使えないほど低いからだ。
とはいえ、小学校1年生でもタブレットは十分に使える。
学校でタブレットを使わないのは、単に導入コストが大きいからだ。
タブレットがあれば、問題なく使える。
実際、ベネッセは教材としてタブレットを提供している。個人的にはアプリで十分だと思うが。
教科書会社が教科書に沿った学習教材をアプリとして販売すればよいだろう。
このように家庭では教材の電子化が進むだろう。しかし、学校では電子化はあまり進まないかもしれない。主に経済的な理由により。
低学年向き学習教材は、すぐには電子化されないが、徐々に電子化されるだろう。それでも年齢的な限界はある。

絵本や雑誌だけでは本屋の商売は成り立たないだろう。
Amazonだけがあればよいという未来が垣間見える。


2015年2月14日土曜日

Firebaseで認証付きアプリを作るには

Firebaseはバックエンドサービスです。BaaS(Backend as a Service)と言われますが、Firebaseのサイトには直接BaaSという言葉はなさそうです。
一言で言うと、ドキュメント型データベースとそのライブラリからなるシステムです。ドキュメント型データベースは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});
    }
  });
}]);