2.3. Node.js アダプター
Red Hat Single Sign-On は、サーバー側の JavaScript アプリケーションを保護するために Connect 上に構築された Node.js アダプターを提供します。この目的は、Express.js などのフレームワークと統合できる柔軟性があることでした。
Node.js アダプターを使用するには、まず Red Hat Single Sign-On 管理コンソールでアプリケーションのクライアントを作成する必要があります。アダプターは、パブリック、機密、およびベアラーのみのアクセスタイプをサポートします。どちらを選択しても、ユースケースのシナリオにより異なります。
クライアントが作成されたら、Installation
タブをクリックして Format Option
に Red Hat Single Sign-On OIDC JSON
を選択してから Download
をクリックします。ダウンロードした keycloak.json
ファイルはプロジェクトの root ディレクトリーにあるはずです。
2.3.1. インストール
Node.js がすでにインストールされている場合は、アプリケーションのディレクトリーを作成します。
mkdir myapp && cd myapp
mkdir myapp && cd myapp
npm init
コマンドを使用して、アプリケーションの package.json
を作成します。依存関係リストに Red Hat Single Sign-On の接続アダプターを追加するようになりました。
"dependencies": { "keycloak-connect": "file:keycloak-connect-18.0.7.tgz" }
"dependencies": {
"keycloak-connect": "file:keycloak-connect-18.0.7.tgz"
}
2.3.2. 使用方法
- Keycloak クラスをインスタンス化します。
-
Keycloak
クラスは、アプリケーションとの設定および統合の中心的な場所を提供します。最も単純な作成には引数は含まれません。
プロジェクトのルートディレクトリーに server.js
という名前のファイルを作成し、以下のコードを追加します。
const session = require('express-session'); const Keycloak = require('keycloak-connect'); const memoryStore = new session.MemoryStore(); const keycloak = new Keycloak({ store: memoryStore });
const session = require('express-session');
const Keycloak = require('keycloak-connect');
const memoryStore = new session.MemoryStore();
const keycloak = new Keycloak({ store: memoryStore });
express-session
依存関係をインストールします。
npm install express-session
npm install express-session
server.js
スクリプトを起動するには、package.json
の 'scripts' セクションに以下のコマンドを追加します。
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start" "node server.js" },
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start" "node server.js"
},
これで、以下のコマンドでサーバーを実行できるようになりました。
npm run start
npm run start
デフォルトでは、これはアプリケーションの主な実行ファイルとともに keycloak.json
という名前のファイルを見つけ (ここではルートフォルダー)、keycloak 固有の設定を初期化します (公開鍵、レルム名、さまざまな URL など)。
この場合、Keycloak 管理コンソールにアクセスするために Keycloak デプロイメントが必要です。
使用して Keycloak 管理コンソールをデプロイする方法については、Podman または Docker のリンクを参照してください。
これで、Red Hat Single Sign-On Admin Console keycloak.json
ファイルを取得できます。
ダウンロードしたファイルをプロジェクトのルートディレクトリーに貼り付けます。
この方法を使用したインスタンス化により、妥当なデフォルトがすべて使用されます。または、keycloak.json
ファイルの代わりに、設定オブジェクトを指定することもできます。
const kcConfig = { clientId: 'myclient', bearerOnly: true, serverUrl: 'http://localhost:8080/auth', realm: 'myrealm', realmPublicKey: 'MIIBIjANB...' }; const keycloak = new Keycloak({ store: memoryStore }, kcConfig);
const kcConfig = {
clientId: 'myclient',
bearerOnly: true,
serverUrl: 'http://localhost:8080/auth',
realm: 'myrealm',
realmPublicKey: 'MIIBIjANB...'
};
const keycloak = new Keycloak({ store: memoryStore }, kcConfig);
アプリケーションは、以下を使用して、ユーザーを優先しているアイデンティティープロバイダーにリダイレクトすることもできます。
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
- Web セッションストアの設定
-
Web セッションを使用して認証のサーバー側の状態を管理する場合は、少なくとも
store
パラメーターでKeycloak(…)
を初期化し、express-session
が使用している実際のセッションストアを渡します。
const session = require('express-session'); const memoryStore = new session.MemoryStore(); // Configure session app.use( session({ secret: 'mySecret', resave: false, saveUninitialized: true, store: memoryStore, }) ); const keycloak = new Keycloak({ store: memoryStore });
const session = require('express-session');
const memoryStore = new session.MemoryStore();
// Configure session
app.use(
session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: memoryStore,
})
);
const keycloak = new Keycloak({ store: memoryStore });
- カスタムスコープの値の指定
-
デフォルトでは、スコープ値の
openid
はクエリーパラメーターとして Red Hat Single Sign-On のログイン URL に渡されますが、さらにカスタム値を追加することができます。
const keycloak = new Keycloak({ scope: 'offline_access' });
const keycloak = new Keycloak({ scope: 'offline_access' });
2.3.3. ミドルウェアのインストール
インスタンス化したら、ミドルウェアを接続対応のアプリケーションにインストールします。
これを行うには、まず Express をインストールする必要があります。
npm install express
npm install express
次に、以下で説明されているようにプロジェクトに Express が必要になります。
const express = require('express'); const app = express();
const express = require('express');
const app = express();
また、以下のコードを追加して Express で Keycloak ミドルウェアを設定します。
app.use( keycloak.middleware() );
app.use( keycloak.middleware() );
最後に、以下のコードを main.js
に追加して、3000 ポートで HTTP 要求をリッスンするようにサーバーをセットアップしましょう。
app.listen(3000, function () { console.log('App listening on port 3000'); });
app.listen(3000, function () {
console.log('App listening on port 3000');
});
2.3.4. プロキシーの設定
SSL 接続を終了するプロキシーの背後でアプリケーション実行されている場合は、express behind proxies ガイドに従って、Express を設定する必要があります。誤ったプロキシー設定を使用すると、無効なリダイレクト URI が生成されることがあります。
設定例:
const app = express(); app.set( 'trust proxy', true ); app.use( keycloak.middleware() );
const app = express();
app.set( 'trust proxy', true );
app.use( keycloak.middleware() );
2.3.5. 認証の確認
リソースにアクセスする前にユーザーが認証されていることを確認するには、keycloak.checkSso()
を使用します。これは、ユーザーがすでにログインしている場合にのみ認証されます。ユーザーがログインしていない場合、ブラウザーは最初のリクエストされた URL にリダイレクトされ、認証されていないままになります。
app.get( '/check-sso', keycloak.checkSso(), checkSsoHandler );
app.get( '/check-sso', keycloak.checkSso(), checkSsoHandler );
2.3.6. リソースの保護
- 簡易認証
-
リソースにアクセスする前にユーザーを認証する必要があるように強制するには、単に
keycloak.protect()
の非引数バージョンを使用します。
app.get( '/complain', keycloak.protect(), complaintHandler );
app.get( '/complain', keycloak.protect(), complaintHandler );
- ロールベースの認可
- 現在のアプリケーションのアプリケーションロールでリソースのセキュリティーを保護するには、以下を実行します。
app.get( '/special', keycloak.protect('special'), specialHandler );
app.get( '/special', keycloak.protect('special'), specialHandler );
別 のアプリケーションのアプリケーションロールでリソースを保護するには、以下を行います。
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
レルムロールでリソースをセキュアにするには、以下を実行します。
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
- リソースベースの認可
-
リソースベースの認可を使用すると、Keycloak で定義された一連のポリシーに基づいて、リソースとその特定のメソッド/アクション ** を保護できるため、アプリケーションからの認可を外部化できます。これは、リソースを保護するために使用できる
keycloak.enforcer
メソッドを公開することで実現されます。*
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
keycloak-enforcer
メソッドは、response_mode
設定オプションの値に応じて 2 つのモードで動作します。
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
response_mode
が token
に設定されている場合は、アプリケーションに送信されたベアラートークンで表されるサブジェクトの代わりにパーミッションがサーバーから取得されます。この場合、新しいアクセストークンは、サーバーによって付与されたアクセス許可を使用して、Keycloak により発行されます。サーバーが予想されるパーミッションを持つトークンに応答しなかった場合、要求は拒否されます。このモードを使用すると、以下のようにリクエストからトークンを取得できるはずです。
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) { const token = req.kauth.grant.access_token.content; const permissions = token.authorization ? token.authorization.permissions : undefined; // show user profile });
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) {
const token = req.kauth.grant.access_token.content;
const permissions = token.authorization ? token.authorization.permissions : undefined;
// show user profile
});
アプリケーションがセッションを使用していて、サーバーからの以前の決定をキャッシュし、更新トークンを自動的に処理する場合は、このモードが推奨されます。このモードは、クライアントとリソースサーバーとして動作するアプリケーションに特に便利です。
response_mode
が permissions
(デフォルトモード) に設定されている場合、サーバーは新しいアクセストークンを発行せずに付与されたパーミッションのリストのみを返します。このメソッドは、新しいトークンを発行しないだけでなく、以下のように リクエスト
を介してサーバーに付与されたパーミッションを公開します。
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) { const permissions = req.permissions; // show user profile });
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
const permissions = req.permissions;
// show user profile
});
使用中の response_mode
に関係なく、keycloak.enforcer
メソッドは、アプリケーションに送信されたベアラートークン内のパーミッションを確認します。ベアラートークンで予想される権限がすでに引き継がれている場合は、サーバーと対話して意思決定を取得する必要はありません。これは、クライアントが、保護されたリソースにアクセスする前に予想されるパーミッションでサーバーからアクセストークンを取得できる場合に特に便利です。そのため、増分認可などの Keycloak Authorization Services が提供する機能を使用し、keycloak.enforcer
がリソースへのアクセスを強制している場合に、サーバーへの追加のリクエストを回避できます。
デフォルトでは、ポリシーエンフォーサーはアプリケーション (例: keycloak.json
) に定義された client_id
を使用して、Keycloak Authorization Services をサポートする Keycloak のクライアントを参照します。この場合、クライアントは実際にはリソースサーバーであることをパブリックにすることはできません。
アプリケーションがパブリッククライアント (フロントエンド) とリソースサーバー (バックエンド) の両方として機能している場合は、次の設定を使用して、適用するポリシーで Keycloak 内の別のクライアントを参照できます。
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
フロントエンドおよびバックエンドを表すために、Keycloak で個別のクライアントを使用することが推奨されます。
保護するアプリケーションが Keycloak 認可サービスで有効になり、keycloak.json
でクライアント認証情報を定義している場合は、サーバーに追加の要求をプッシュして決定のためにポリシーで利用できるようにすることができます。そのため、プッシュする要求と共に JSON を返す 関数
を想定する claims
設定オプションを定義できます。
app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], { claims: function(request) { return { "http.uri": ["/protected/resource"], "user.agent": // get user agent from request } } }), function (req, res) { // access granted
app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], {
claims: function(request) {
return {
"http.uri": ["/protected/resource"],
"user.agent": // get user agent from request
}
}
}), function (req, res) {
// access granted
Keycloak を設定してアプリケーションリソースを保護する方法の詳細については、Authorization Services Guide を参照してください。
- 高度な認可
- URL 自体の一部に基づいてリソースを保護するには、各セクションにロールが存在することを前提としています。
function protectBySection(token, request) { return token.hasRole( request.params.section ); } app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
function protectBySection(token, request) {
return token.hasRole( request.params.section );
}
app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
高度なログイン設定:
デフォルトでは、クライアントがベアラーのみでない限り、認可されていないすべてのリクエストは Red Hat Single Sign-On のログインページにリダイレクトされます。ただし、機密またはパブリッククライアントは、閲覧可能なエンドポイントと API エンドポイントの両方をホストする場合があります。認証されていない API 要求でのリダイレクトを防ぎ、代わりに HTTP 401 を返すようにするには、redirectToLogin 関数をオーバーライドします。
たとえば、このオーバーライドでは URL に /api/ が含まれているかどうかを確認し、ログインリダイレクトを無効にします。
Keycloak.prototype.redirectToLogin = function(req) { const apiReqMatcher = /\/api\//i; return !apiReqMatcher.test(req.originalUrl || req.url); };
Keycloak.prototype.redirectToLogin = function(req) {
const apiReqMatcher = /\/api\//i;
return !apiReqMatcher.test(req.originalUrl || req.url);
};
2.3.7. 追加の URL
- 明示的なユーザーがトリガーされたログアウト
-
デフォルトでは、ミドルウェアは
/logout
への呼び出しをキャッチし、Red Hat Single Sign-On 中心のログアウトワークフローを介してユーザーを送信します。これは、logout
設定パラメーターをmiddleware()
呼び出しに指定することで変更できます。
app.use( keycloak.middleware( { logout: '/logoff' } ));
app.use( keycloak.middleware( { logout: '/logoff' } ));
ユーザーがトリガーするログアウトが呼び出されると、クエリーパラメーター redirect_url
を渡すことができます。
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
次に、このパラメーターは OIDC ログアウトエンドポイントのリダイレクト URL として使用され、ユーザーは https://example.com/logged/out
にリダイレクトされます。
- Red Hat Single Sign-On 管理者のコールバック
-
また、ミドルウェアは、Red Hat Single Sign-On コンソールのコールバックをサポートし、単一のセッションまたはすべてのセッションをログアウトします。デフォルトでは、これらの管理コールバックのタイプは
/
のルート URL に関連して行われますが、middleware()
呼び出しにadmin
パラメーターを指定して変更できます。
app.use( keycloak.middleware( { admin: '/callbacks' } );
app.use( keycloak.middleware( { admin: '/callbacks' } );
2.3.8. 完全な例
Node.js アダプターの完全な使用例は、Node.js の Keycloak クイックスタート にあります。