Auth0 の Node.js サンプルプログラムを IBM Cloud にデプロイする

本エントリーは [IBM Cloud Advent Calendar 2017](https://qiita.com/advent-calendar/2017/bluemix) 16日目のエントリーです。
16日目の今日は、少し趣向を変えて認証基盤サービス (IdMaaS) である Auth0 の紹介と、サンプルアプリを IBM Cloud へデプロイしてみたいと思います。よろしくお願いいたします。

# 本エントリーの対象
認証基盤サービス (IdMaaS) である Auth0 を知らない人、クロスプラットフォームで簡単に認証認可周りを実装したい人向けのエントリーになります。
このサービスを先日 ~~Security-JAWS の勉強会に行った際、~~初めて知り感動しました。
今まで使っていた [Safari Books Online](https://www.safaribooksonline.com/) や [Linux Academy]()、[A Cloud Guru](https://acloud.guru/) などで使われているということで、よく見てみるとたしかにログイン時の URL に auth0.com がついています。これらのサービスは、Webとモバイルアプリを出していて、こういうサービスでは Auth0 のありがたさをより実感できそうです。

<img width="320" alt="IMG_7510.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/73881/ad048930-40cb-edb9-b5e3-a23a34eb81f8.png">

<img width="320" alt="IMG_7511.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/73881/1c1a692f-ba5d-3561-1ca3-262613aea7f7.png">


# 本エントリーを読んでできるようになること
Auth0 のすばらしさに感動する

# 本エントリーの執筆時の実行環境
```shell:
$ 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)](https://www.slideshare.net/HideyaFuruta/auth0aws-82091034)

Auth0 は認証基盤サービス (IdMaaS) をやっている会社の名前でありサービスの名前です。既にいろいろなサービスで使用されており、パブリッククラウドの1サービスである SSO サービスを使用するよりも安心感があり品質も高いです。また、アプリが IBM Cloud や AWS、iOS からアクセスされるようなものであってもプラットフォームを跨いだ認証認可機能を構築できます。

認証認可周りは、開発すると全体の開発工数の 30% くらいになるそうです。
スライド内のアンケートでは Auth0 を使うと 94% の企業が1ヶ月以内に Auth0 を実装できたと言っています。
~~Cognito 使うと、Webとアプリ両方の実装に時間がかかるらしいです。~~

Auth0 社は認証だけでなく、[Webtask](https://webtask.io/)というサーバーレスコンピューティング環境も提供しているそうです
http://martinfowler.com/articles/serverless.html

結構な Open Source プロジェクトがあります。
全部見れてないですが、Open Source になっているのは SDK とかでしょうか。いろんなプラットフォームで実装しやすいように整えられている感じがあります。Xamarin 向けとかもあります。
https://auth0.com/opensource

# サンプルプログラムをローカルで動かす
## Auth0 に登録する
ポチポチ登録していきます

SIGN UP
<img width="1159" alt="Screen Shot 2017-11-15 at 0.04.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/dc2ed0c3-bced-ccd2-5fb8-434834f98662.png">

今回は GitHub アカウントで登録しました。
22 日間フル機能利用可能。それ以降は Free ライセンスとして継続利用かのうとのこと。
<img width="1158" alt="Screen Shot 2017-11-15 at 0.04.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/f9344211-64f6-d1cd-5ebf-21d1359c66ce.png">

<img width="1157" alt="Screen Shot 2017-11-15 at 0.04.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/77fac3ca-ef6a-5df3-5acd-82fc38452649.png">

テナントドメインはログイン時のURLになり変更不可なので慎重に。
リージョンを選択します。確か、Auth0 は AWS 上と、一部 Heroku 上で動いていると聞いた気がします。
<img width="1155" alt="Screen Shot 2017-11-15 at 0.06.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/1b49e611-e7c6-a046-d6b3-f40a089fbad1.png">

<img width="1158" alt="Screen Shot 2017-11-15 at 0.06.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/867036d6-d32f-3438-3526-67ab7b20c640.png">

+ NEW CLIENT
<img width="1109" alt="Screen Shot 2017-12-02 at 10.59.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/4efb2841-77d8-f24e-bfdb-9e280789a6ec.png">

適当な名前を付けて、今回は Node.js なので、Single Page Web Applications を選択。 
<img width="1107" alt="Screen Shot 2017-12-02 at 11.00.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/00a025e6-5294-7ce2-3ead-1b55b287dbf3.png">

API キー的なのが表示されるので、これらを後で使用します。
<img width="1104" alt="Screen Shot 2017-12-02 at 11.00.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/614add87-bc49-1b8c-8e2e-34a7a3386c94.png">

この Settings タブを下の方にスクロールしていくと `Allowed Callback URLs` という欄があります。ハンズオンではここに Callback URL をセットします。
<img width="1078" alt="Screen Shot 2017-12-09 at 8.43.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/312e93ed-362b-f3de-3504-7ec202ea5538.png">

## Node.js のサンプルプログラムをダウンロードする
Node.js のサンプルプログラムの公式の紹介ページは[こちら](https://auth0.com/docs/quickstart/webapp/nodejs/01-login)
ここに書いてある説明通り、GitHub のリポジトリ [auth0-samples/auth0-nodejs-webapp-sample](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
```

## 設定ファイルなどを見てみる

```javascript:package.json  
{
  "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` が実行されるみたいです。

```sh: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)](https://stackoverflow.com/questions/33509816/what-exactly-does-usr-bin-env-node-do-at-the-beginning-of-node-files)

```:.env.example
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` を設定します

<img width="1073" alt="Screen Shot 2017-12-09 at 9.19.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/ce57d668-efe4-5165-950d-e040fceeb920.png">

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` に書いた内容をプラグラム内から環境変数として取得できます。

```:.env
AUTH0_CLIENT_ID=xxxxxx.auth0.com
AUTH0_DOMAIN=aAbBcCdD
AUTH0_CLIENT_SECRET=eEfFgGhH
```

起動

```
npm start
```

ブラウザで `http://localhost:3000` にアクセスすると、Auth0 を使用してログインできます。

<img width="897" alt="Screen Shot 2017-12-02 at 11.48.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/ad9b84c9-765a-9f8d-fd82-414f9a0f3e33.png">

<img width="1054" alt="Screen Shot 2017-12-02 at 11.50.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/2ffd7ed9-2e4d-e6a6-aadd-a1d61b1e1140.png">

<img width="1052" alt="Screen Shot 2017-12-02 at 11.49.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/387b4446-56f5-2ed9-6060-31f557907e1e.png">

<img width="1054" alt="Screen Shot 2017-12-02 at 11.49.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/d30cc7f8-54e7-30dc-7d4f-15179d785b5a.png">


# サンプルプログラムを IBM Cloud 上で動かす
先程のサンプルプログラムを ~~Bluemix~~ IBM Cloud にデプロイします。

## IBM Cloud で Node.js アプリをデプロイする
カタログから選んでデプロイするだけなので、割愛します。

## Callback URL の設定
Callback URL を `https://<YourApp>.mybluemix.net/callback` に変更します

<img width="1075" alt="Screen Shot 2017-12-09 at 10.44.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/73881/af6d1635-e8e1-62e0-370d-67cffed2cfc8.png">

`.env` に `AUTH0_CALLBACK_URL` を追加します

```:.env
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` になります。

```js:app.js(一部抜粋)
  {
    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` を作成します。

```yaml:manifest.yml
applications:
- name: <your-app-name>
  random-route: true
  memory: 128M
```

デプロイして動作確認します。

cf ではなく `bx` コマンドを使いましょう。

```sh
bx login
bx app push
```

# 終わりに
認証認可周りはかなりコアな部分なので、IBM Cloud のサービスを使用して実装するのは少し心配な気がします。Auth0 を使用しておけば気持ちが楽になります。

本エントリーでは Node.js のチュートリアルを動かしただけですが、ネイティブアプリと Web アプリ両方で動かすのとかも面白そうです。

Kotlin + Spring Boot + Auth0 とか楽しそう。
[Developing RESTful APIs with Kotlin (Auth0 の developers blog)](https://auth0.com/blog/developing-restful-apis-with-kotlin/)
developers blog のコード公開用の [auth0-blog](https://github.com/auth0-blog) という Organization もあって良さがある。