4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OSSTechAdvent Calendar 2018

Day 17

OpenAM Policy Agent for Node.js に触れてみる

Last updated at Posted at 2018-12-16

はじめに

ForgeRock の GitHub で OpenAM Policy Agent for Node.js というリポジトリを見つけたので触ってみようと思います。

OpenAM Policy Agent って?

アプリケーションを OpenAM で保護(SSO 化)するために使うモジュールです。アプリケーションへのリクエストをインターセプトして、認証を促し・認可を行う仕組みになっています。

これまでの Agent は Apache や IIS などの Web サーバーや Tomcat などのサーブレットコンテナに対応しており、各 Web サーバーのモジュールやサーブレットフィルターとして実装されていました。

OpenAM Policy Agent for Node.js の登場により、Node.js で Agent を使うことができます。

( ゚д゚)ポカーン

Web サーバー向けのモジュールが JavaScript(Node.js) で動作するというのはどういうことなんでしょうね…
私は Node.js に明るくないのでイメージが湧きません。

デモアプリを触ってみる

イメージをつかむために実際に触ってみようと思います。Agent for Node.js のデモアプリ があったので、こちらを使ってみることにしました。

ちなみに、Agent for Node.js の最新は 5.0.0 の β 版ですが、デモアプリで利用している Agent のバージョンは 3.1.2 と古いようです。ただ、バージョンによる違いが分からないので気にせずに行きます。

環境

Agent の動作には OpenAM が必要になります。今回は OpenAM と Agent でサーバーを分けています。

  • OpenAM
項目
OS CentOS 7.5
ホスト名 openam.example.co.jp
OpenAM のバージョン OpenAM 13.0.0
URL http://openam.example.co.jp:8080/openam
  • Agent
項目
OS CentOS 7.5
ホスト名 app.example.co.jp
Node.js のバージョン v6.14.3 (EPEL の RPM パッケージを利用)
URL http://app.example.co.jp:8080

OpenAM の準備

OpenAM の初期設定を行い、Agent for Node.js 用に Agent の設定を作成しておきます。ここでは名前に demo-agent、パスワードに changeit と入力しています。

agent-setting.png

デモアプリの準備

デモアプリのリポジトリを取得します。

$ git clone https://github.com/ForgeRock/node-openam-agent-demo.git

npm コマンドで依存パッケージをインストールします。

$ cd node-openam-agent-demo
$ npm install

設定ファイルである lib/config.json を編集します。

{
  "serverUrl": "http://openam.example.co.jp:8080/openam", // OpenAM  URL
  "appUrl": "http://app.example.co.jp:8080",              // デモアプリ(Agent)  URL
  "notificationRoute": "/",                               // OpenAM からの通知を受け取るパス?
  "notificationsEnabled": true,                           // 通知の有効化
  "username": "demo-agent",                               // Agent 
  "password": "changeit",                                 // Agent のパスワード
  "realm": "/",                                           // OpenAM のレルム
  "appName": "iPlanetAMWebAgentService",                  // ポリシーセット名
  "logLevel": "info"                                      // ログレベル
}

npm start を実行すると 8080 ポートでデモアプリが起動します。

$ npm start
...
2018-12-06T04:18:25.291Z - info: [5i2h0NCxC] Agent initialized.
Example 1 started on port 8080

デモアプリの動作

デモアプリ起動後、http://app.example.co.jp:8080/members にアクセスしてみます。

OpenAM での認証が済んでいない状態だと 401 の画面が表示されます。他の Agent では認証が済んでいない場合、OpenAM へ認証のためにリダイレクトされるのですが、挙動が違っています。

example1_401.png

認証が済んでいると Hello demo と表示されます。

example1_200.png

デモアプリのコードを確認する

それでは Agnet をどのように使っているのか、デモアプリのソースコード(lib/example1/index.js)を見てみましょう。

  1 var http = require('http'),
  2     openamAgent = require('openam-agent'), // Agent モジュールのロード
  3     PolicyAgent = openamAgent.PolicyAgent, 
  4     CookieShield = openamAgent.CookieShield;
  5 
  6 var agent = new PolicyAgent(require('../config.json')), // Agent オブジェクト
  7     shield = new CookieShield({                         // Shield オブジェクト
  8         getProfiles: true,
  9         noRedirect: true
 10     });
 11 
 12 
 13 var server = http.createServer(function (req, res) {
 14     var middleware = agent.shield(shield); // Agent にクッキーによる保護を設定
 15 
 16     if (req.url === '/members') {
 17         // enforce shield
 18         middleware(req, res, function () { // パスが /members の場合に Agent で保護
 19             // app logic
 20             res.writeHead(200);
 21             res.end('Hello ' + req.session.data.username + '!');
 22 
 23         });
 24     } else {
 25         res.writeHead(404);
 26         res.end('Not found');
 27     }
 28 });
 29 
 30 server.listen(8080, function () {
 31     console.log('Example 1 started on port %s', server.address().port);
 32 });

Web サーバーに導入する Agent と違い、SDK という感じですね。

CookieShield はクッキーで保護するオブジェクトです。これを使うことで OpenAM で認証が済んでいればリソースにアクセスできます。
また、9 行目で noRedirecttrue に設定しているため、認証が済んでいない場合でも OpenAM にリダイレクトされません。

他のデモアプリ

デモに最初に動作を確認したもの(example1)以外に 2 つのデモアプリが含まれています。デモの内容・特徴は次の通りです。

  • example2
    • 認可の方式を変える
    • キャッシュの方式を変える
  • example3
    • 認可の方式のカスタマイズ

それでは特徴毎にコードを見ていきましょう。

認可の方式を変える

example1 で CookieShield が登場しましたが、example2 では他の Shield が登場します。

 46 var cookieShield = new openamAgent.CookieShield({getProfiles: true, cdsso: false, noRedirect: false}),
 47     passThroughShield = new openamAgent.CookieShield({getProfiles: true, passThrough: true}),
 48     policyShield = new openamAgent.PolicyShield(config.appName),
 49     oauth2Shield = new openamAgent.OAuth2Shield(),
 50     basicAuthShield = new openamAgent.BasicAuthShield();
  • CookieShield
  • OpenAM の認証クッキーで保護する
  • passThrough に true を指定すると認証していなくてもアクセス可能(適用されない URL の属性の取得に相当)
  • PolicyShield
  • OpenAM のポリシーで保護する
  • OAuth2Shield
    • OAuth2 のアクセストークンで保護する
  • BasicAuthShield
    • Basic 認証で保護する

各 Shield の動作を試すには npm start example2 を実行して example2 のデモアプリを起動します。起動後、http://app.example.co.jp:8080 にアクセスするとデモアプリのトップページが表示されるのでメニューに従って試してみてください。

example2.png

キャッシュの方式を変える

OpenAM Policy Agent ではセッションや認可情報をキャッシュする仕組みがあります。Apache HTTP Server の Agent ではメモリーで管理していますが、Agent for Node.js ではメモリー以外のバックエンドを選択できるようです。

  4     SimpleCache = require('openam-agent-cache-simple').SimpleCache,
  5     MemcachedCache = require('openam-agent-cache-memcached').MemcachedCache,
  6     CouchDBCache = require('openam-agent-cache-couchdb').CouchDBCache,
  7     RedisCache = require('openam-agent-cache-redis').RedisCache,
  8     MongoCache = require('openam-agent-cache-mongodb').MongoCache,

SimpleCache はメモリーを利用します。その他はクラス名からバックエンドが類推できるので説明は省きます…

認可の方式のカスタマイズ

先ほど 幾つかの認可の方式を紹介しましたが、Shield を自分で拡張することで独自の認可処理を行うこともできます。

example3 では CustomerShield という独自の認可処理が定義されています。ユーザー情報の roles に customer が入っていればアクセスを許可するという内容です。

  5 class CustomerShield extends Shield {
  6     evaluate(req, res, agent) {
  7         const rolesProperty = 'roles';
  8 
  9         return new Promise((resolve, reject) => {
 ..(省略)
 22             if (!req.session.data[rolesProperty].includes('customer')) {
 23                 reject(new ShieldEvaluationError(403, 'not a customer'));
 24             }
 25 
 26             resolve();
 27         });
 28     }
 29 }

まとめ

Node.js で実装された Web サーバーに組み込める Agent を紹介しました。

OAuth 2.0 のリソースサーバーを実装できる点や、認可処理を Agent 側で自由に変更できる点が他の Agent には無い面白さでしょうか。

ただ、使いどころは難しい印象です。既存アプリを SSO 化するのであれば Web サーバーに Agent を導入した方が簡単でしょうし、新規アプリの SSO 化に使うとしても OpenID Connect や SAML 2.0 といったオープンなプロトコルに対応したもの方がロックインされないメリットがあります…

余談(ハマりどころ)

PolicyShield の動作を確認するのに苦労しました。

PolicyShield は OpenAM の policy evaluation REST API を利用するのですが、POST データは次のような JSON になっていました。

{
  "resources": [
    "/admin"
  ],
  "application": "iPlanetAMWebAgentService",
  "subject": {
    "ssoToken": "AQI..."
  }
}

本来 resources には URL を指定してほしいのですが、リクエストにはパスしか含まれていません。そのため、アクセスが拒否されてしまいました。

結局、パスのみのポリシーを特別に用意して回避しました。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?