Keycloakでリソースの保護（Node Adapter編）


## はじめに

Keycloakでリソースを保護しようとするとき下記の２つが存在するみたい。

* Webサーバと連携してプロキシを設定し特定のURL等を保護する
* Client Adapterで言語レベルで組み込む

ここではNode Adapterを利用してAPIの保護設定を試してみる。
Node以外では基本Java系になってしまうようだ。

プロキシによるリソース保護については[こちら](https://qiita.com/zaburo/private/d7c09c0b4f21a1abe9af)を御覧ください。
Keycloakの設定自体は[こちら](https://qiita.com/zaburo/private/6df185803cb019cd257c)をご覧ください。


## 準備

### 作業場所の確保

どこでもよいです。

```bash
mkdir keycloak-node
cd keycloak-node
```

### 必要なモジュールのインストール

```bash
npm init -y

npm install --save keycloak-connect
npm install --save express
npm install --save express-session
```

### Redirect URLの変更（Keycloakのclient設定）

リダイレクト先は変わるので以下の用にします。

![スクリーンショット 2021-03-15 14.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/7765ceda-ba9d-dc43-f4dd-245f308ea78a.png "スクリーンショット 2021-03-15 14.27.41.png")

> nodeサーバ（RP）をport 3000で立てる想定。


## 実装

では、実装していきますが、その前にkeycloakが標準で読むkeycloak.jsonファイルをカレントディレクトリに作成しておきます。

```json:keycloak.json
{
    "realm": "master",
    "resource": "test",
    "auth-server-url": "http://localhost:8080/auth",
    "credentials": {
        "secret": "2e7ac465-f8ad-49f1-9550-016ff57c934a"
    }
}
```

実装します。

* / は誰でも見える
* /privateは保護領域（API）
* /private/grantではreq.kauth.grant.id_token.contentから値を取得
* /private/sessionではsession['keycloak-token']から値を取得

とします。

```js:index.js
const express = require("express");
const session = require("express-session");
const Keycloak = require("keycloak-connect");

const sessionStore = new session.MemoryStore();
const keycloak = new Keycloak({ store: sessionStore });

const app = express();

app.use(session({
    secret: "12345", //何でも。一意なもの。
    resave: false,
    saveUninitialized: false
}));

app.use(keycloak.middleware());

//public
app.get("/", (req, res) => {
    res.send("Welcome");
})

//private(grant)
app.get("/private/grant", keycloak.protect(), (req, res) => {
    res.send(`ログインユーザー名： ${req.kauth.grant.id_token.content.preferred_username}`);
    console.log(req.kauth.grant.id_token.content);
})

//private(session)
app.get("/private/session", keycloak.protect(), (req, res) => {
    res.send(req.session['keycloak-token']);
})

//listen
app.listen(3000, () => {
    console.log("start server on port 3000.");
})
```

保護したいリソースにはミドルウエアでkeycloak.protect()をかませる。


## 動作確認

### 起動

```bash
npm index.js
```

### 保護領域へのアクセス

下記にアクセスしようとすると、

http://localhost:3000/private/data

ログインが求められます。


![スクリーンショット 2021-03-15 14.34.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/302175d7-6ab7-c2c3-b82f-68d9688189e0.png "スクリーンショット 2021-03-15 14.34.31.png")

### ログイン後

#### /private/grant

とりあえずadminで入ってみた結果。


![スクリーンショット 2021-03-15 14.34.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/d81239c6-cda3-1c0c-f6a1-4efc93cbaeee.png "スクリーンショット 2021-03-15 14.34.43.png")

console.log()の出力。

```json
{
  exp: 1615840336,
  iat: 1615840276,
  auth_time: 1615839806,
  jti: '5226f2b7-ad0f-41da-abe8-29e11ed87a32',
  iss: 'http://localhost:8080/auth/realms/master',
  aud: 'test',
  sub: '04925620-0e9f-44d0-b44a-080e1fb0edf0',
  typ: 'ID',
  azp: 'test',
  session_state: '67dfe94a-3564-4dc8-aac1-28171517c78e',
  at_hash: 'NOcznbG4yh3ma3WTW1orjA',
  acr: '0',
  email_verified: false,
  preferred_username: 'admin'
}
```

#### /private/session

![スクリーンショット 2021-03-16 5.24.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/8d0bfb0a-9001-7dba-e13b-190e90f9d456.png "スクリーンショット 2021-03-16 5.24.35.png")


いろいろ気になるところはあるが、一旦ここまで。

## 参考

* https://kazuhira-r.hatenablog.com/entry/20180410/1523371622
* https://medium.com/devops-dudes/securing-node-js-express-rest-apis-with-keycloak-a4946083be51