本エントリーは 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 もあって良さがある。