本エントリーは IBM Cloud Advent Calendar 2017 16日目のエントリーです。
16日目の今日は、少し趣向を変えて認証基盤サービス (IdMaaS) である Auth0 の紹介と、サンプルアプリを IBM Cloud へデプロイしてみたいと思います。よろしくお願いいたします。
本エントリーの対象
認証基盤サービス (IdMaaS) である Auth0 を知らない人、クロスプラットフォームで簡単に認証認可周りを実装したい人向けのエントリーになります。
このサービスを先日 ~~Security-JAWS の勉強会に行った際、~~初めて知り感動しました。
今まで使っていた Safari Books Online や Linux Academy、A Cloud Guru などで使われているということで、よく見てみるとたしかにログイン時の URL に auth0.com がついています。これらのサービスは、Webとモバイルアプリを出していて、こういうサービスでは Auth0 のありがたさをより実感できそうです。
本エントリーを読んでできるようになること
Auth0 のすばらしさに感動する
本エントリーの執筆時の実行環境
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.1
BuildVersion:	17B1002
$ npm -v
5.2.0
$ node -v
v6.11.1
Auth0 とは
Auth0 のセールスエンジニアの方が SlideShare にスライドを上げているので、これが一番わかりやすいです。
Auth0でAWSの認証認可を強化 (SlideShare)
Auth0 は認証基盤サービス (IdMaaS) をやっている会社の名前でありサービスの名前です。既にいろいろなサービスで使用されており、パブリッククラウドの1サービスである SSO サービスを使用するよりも安心感があり品質も高いです。また、アプリが IBM Cloud や AWS、iOS からアクセスされるようなものであってもプラットフォームを跨いだ認証認可機能を構築できます。
認証認可周りは、開発すると全体の開発工数の 30% くらいになるそうです。
スライド内のアンケートでは Auth0 を使うと 94% の企業が1ヶ月以内に Auth0 を実装できたと言っています。
Cognito 使うと、Webとアプリ両方の実装に時間がかかるらしいです。
Auth0 社は認証だけでなく、Webtaskというサーバーレスコンピューティング環境も提供しているそうです
http://martinfowler.com/articles/serverless.html
結構な Open Source プロジェクトがあります。
全部見れてないですが、Open Source になっているのは SDK とかでしょうか。いろんなプラットフォームで実装しやすいように整えられている感じがあります。Xamarin 向けとかもあります。
https://auth0.com/opensource
サンプルプログラムをローカルで動かす
Auth0 に登録する
ポチポチ登録していきます
今回は GitHub アカウントで登録しました。
22 日間フル機能利用可能。それ以降は Free ライセンスとして継続利用かのうとのこと。

テナントドメインはログイン時のURLになり変更不可なので慎重に。
リージョンを選択します。確か、Auth0 は AWS 上と、一部 Heroku 上で動いていると聞いた気がします。

適当な名前を付けて、今回は Node.js なので、Single Page Web Applications を選択。

API キー的なのが表示されるので、これらを後で使用します。

この Settings タブを下の方にスクロールしていくと Allowed Callback URLs という欄があります。ハンズオンではここに Callback URL をセットします。

Node.js のサンプルプログラムをダウンロードする
Node.js のサンプルプログラムの公式の紹介ページはこちら
ここに書いてある説明通り、GitHub のリポジトリ auth0-samples/auth0-nodejs-webapp-sample から Clone してきます。
git clone https://github.com/auth0-samples/auth0-nodejs-webapp-sample.git
cd auth0-nodejs-webapp-sample/01-Login
設定ファイルなどを見てみる
{
  "name": "auth0-nodejs-regular-webapp-sample",
  "version": "1.0.0",
  "description": "Auth0 + NodeJS Regular WebApp seed",
  "repository": "git://github.com/auth0/node-auth0",
  "author": "Auth0",
  "license": "MIT",
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "^1.16.1",
    "connect-ensure-login": "^0.1.1",
    "connect-flash": "^0.1.1",
    "cookie-parser": "^1.4.3",
    "debug": "^2.6.1",
    "dotenv": "^4.0.0",
    "express": "^4.16.1",
    "express-session": "^1.11.3",
    "morgan": "^1.8.1",
    "passport": "^0.3.0",
    "passport-auth0": "^0.6.0",
    "pug": "~2.0.0-beta11",
    "serve-favicon": "^2.4.0"
  }
}
npm start を入力したときには node ./bin/www が実行されるみたいです。
# !/usr/bin/env node
/**
 * Module dependencies.
 */
var app = require('../app');
var debug = require('debug')('nodejs-regular-webapp2:server');
var http = require('http');
/**
 * Get port from environment and store in Express.
 */
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
 * Create HTTP server.
 */
var server = http.createServer(app);
/**
 * Listen on provided port, on all network interfaces.
 */
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
  var port = parseInt(val, 10);
  if (isNaN(port)) {
    // named pipe
    return val;
  }
  if (port >= 0) {
    // port number
    return port;
  }
  return false;
}
/**
 * Event listener for HTTP server "error" event.
 */
function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }
  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;
  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}
/**
 * Event listener for HTTP server "listening" event.
 */
function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
  console.log('Listening on ' + bind);
}
#!/usr/bin/env node とは (Stack Overflow)
AUTH0_CLIENT_ID={CLIENT_ID}
AUTH0_DOMAIN={DOMAIN}
AUTH0_CLIENT_SECRET={CLIENT_SECRET}
後で .env.example このファイルをコピーして、Auth0 でクライアントを作成したときの CLIENT_ID などを入力していきます。
以下は、サンプルを Docker で動かすためのファイルで、今回は使用しません。
- .dockerignore
 - Dockerfile
 - exec.ps1
 - exec.sh
 
サンプルプログラムの実行
まず Auth0 のサイトで Callback URL に http://localhost:3000/callback を設定します
package のインストール
# package.json に書いてある passport, passport-auth0, connect-ensure-login のバージョンを最新にする
npm install passport passport-auth0 connect-ensure-login --save
# package.json に書いてある package をインストール
npm install
CLIENT_ID などの設定
cp .env.example .env
vim .env
dotenv package がインストールされているので、.env に書いた内容をプラグラム内から環境変数として取得できます。
AUTH0_CLIENT_ID=xxxxxx.auth0.com
AUTH0_DOMAIN=aAbBcCdD
AUTH0_CLIENT_SECRET=eEfFgGhH
起動
npm start
ブラウザで http://localhost:3000 にアクセスすると、Auth0 を使用してログインできます。
サンプルプログラムを IBM Cloud 上で動かす
先程のサンプルプログラムを Bluemix IBM Cloud にデプロイします。
IBM Cloud で Node.js アプリをデプロイする
カタログから選んでデプロイするだけなので、割愛します。
Callback URL の設定
Callback URL を https://<YourApp>.mybluemix.net/callback に変更します
.env に AUTH0_CALLBACK_URL を追加します
AUTH0_CLIENT_ID=xxxxxx.auth0.com
AUTH0_DOMAIN=aAbBcCdD
AUTH0_CLIENT_SECRET=eEfFgGhH
AUTH0_CALLBACK_URL=https://<YourApp>.mybluemix.net/callback
ちなみに、app.js を見ると Callback URL 取得部分は以下のようになっています。AUTH0_CALLBACK_URL を設定していない時は localhost:3000 になります。
  {
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL:
      process.env.AUTH0_CALLBACK_URL || 'http://localhost:3000/callback'
  },
IBM Cloud へデプロイするため manifest.yml を作成します。
applications:
- name: <your-app-name>
  random-route: true
  memory: 128M
デプロイして動作確認します。
cf ではなく bx コマンドを使いましょう。
bx login
bx app push
終わりに
認証認可周りはかなりコアな部分なので、IBM Cloud のサービスを使用して実装するのは少し心配な気がします。Auth0 を使用しておけば気持ちが楽になります。
本エントリーでは Node.js のチュートリアルを動かしただけですが、ネイティブアプリと Web アプリ両方で動かすのとかも面白そうです。
Kotlin + Spring Boot + Auth0 とか楽しそう。
Developing RESTful APIs with Kotlin (Auth0 の developers blog)
developers blog のコード公開用の auth0-blog という Organization もあって良さがある。

