Okta

オクタ

はじめに

フロントエンドからバックエンドのリソースにアクセスする際、アクセストークンを用いることで、より安全にサービス間のやり取りを実現できます。具体的な動きとして、フロントエンドは、バックエンドに対してアクセストークンと共にAPIリクエストを行います。バックエンドは、受け取ったアクセストークンの有効性を確認し、応答可否を判断します。
Auth0では、Auth0とフロントエンドをOpenID Connect(OIDC)で連携することで、フロントエンドへのアクセストークン発行に加えて、ログインユーザのロールに割り当てられた権限をアクセストークンに付与できます。これにより、ロールに応じたアクセス制御が可能となります。ユーザのロールに基づくアクセス制御を、ロールベースアクセス制御(RBAC)と呼びます。

RBAC

本ブログでは、RBACの概要とRBACを用いたBackend APIのアクセス制御を実現するための設定例をご紹介します。

前提

設定例で利用するOkta社提供サンプルアプリ側の変更により、修正すべきコード内容が本ブログ記載の情報から変更となる可能性があります。

また、本ブログ記載の機能及び設定に関する内容は、20231月現在の情報となります。

ロールベースアクセス制御(RBAC)とは

RBACとは、システム内のユーザのロール/役割に応じてアクセス権を割り当てる方法です。同じロールを付与されたユーザは同一の権限セット、異なるロールを付与されたユーザは異なる権限セットを持ちます。
企業が持つ情報(機密文書、お客様情報等)の保護において、ユーザ毎に適切なアクセス権の付与を行い、情報に対して必要なユーザのみアクセス可能とする必要があります。この適切なアクセス権付与において、ユーザ単位ではなくロールに基づくことが重要とされています。

ロールベースアクセス制御(RBAC)とは

RBACを利用すれば、ユーザに付与されたロールに基づいてアクセス許可/拒否が決まるため、ユーザ単位での権限設定と比較して、以下のメリットがあります。

  • 権限設定の簡素化による対応工数の削減
    • ロール付与/変更のみでユーザの権限設定を完結
    • ロールに付与された権限変更によるユーザ権限の一括変更が可能
  • 権限設定ミスの回避
    • 設定簡素化による意図しない権限付与発生の防止

設定例:RBACを利用したBackend APIのアクセス制御

Okta社提供サンプルアプリを活用し、RBACを利用したBackend APIのアクセス制御を実現します。本設定例では、SPAのプログラムとしてJavaScript、Backend APIはNode.js(Express)を利用します。

ユーザがAuth0へログインした際、フロントエンド(SPA)はAuth0からJSON Web Token(JWT)形式のアクセストークンを取得します。取得したアクセストークンでBackend APIにアクセスし、Backend API側でアクセストークンを検証し、許可されたリソースを返す流れとなります。

設定例:RBACを利用したBackend APIのアクセス制御

実現にあたり、必要な設定の流れは以下の通りです。

0. 事前準備
1. SPAサンプルアプリ起動
2. Backend API起動
3. Permission追加
4. ロール作成とPermission付与
5. ユーザ作成とロール付与
6. Backend APIに対する認可要求を設定
7. Backend APIリクエストボタン設置

ここから、具体的な設定方法をご紹介します。

0. 事前準備

以降の設定を行う前に、以下を準備しましょう。

  • MacOSまたはWindows PC
  • Node.js, Node Package Managerのインストール
  • Auth0無料トライアルアカウントの発行(参考: [Auth0トライアル利用方法]

1. SPAサンプルアプリ起動

[Auth0とサンプルアプリケーションの連携設定方法]を参考に、Okta社が提供するSPAサンプルアプリ(JavaScript)を起動します。

2. Backend API起動

  • Auth0管理画面で、[Applications] > [APIs]をクリック
Auth0管理画面で、[Applications] > [APIs]をクリック
  • [+Create API]をクリック
[+Create API]をクリック
  • 各項目を設定後、[Create]をクリック
  • Name:例) My API (任意の名称)
  • Identifier:例) api://my-auth0-workshop-api (APIの識別子)
各項目を設定後、[Create]をクリック
各項目を設定後、[Create]をクリック
  • Express.js API applicationページへ遷移後、[Log in]ボタンをクリック
Express.js API applicationページへ遷移後、[Log in]ボタンをクリック
  • ログイン後、設定対象のAuth0テナントを選択し、ページ内[①Define permissions]の[Download Sample]をクリック
ログイン後、設定対象のAuth0テナントを選択し、ページ内[①Define permissions]の[Download Sample]をクリック
  • ドロップダウンリストから③で作成したAPIを選択し、[DOWNLOAD]をクリック
ドロップダウンリストから③で作成したAPIを選択し、[DOWNLOAD]をクリック
  • ダウンロードしたファイルを任意のフォルダで解凍
  • 解凍したフォルダで以下コマンドを実行し、Backend APIを起動
$ npm install
$ npm start

[補足] 以下のエンドポイントを持つAPIサーバが起動します

エンドポイント 説明
/api/public
  • 認可不要で利用可能
/api/private
  • スコープ無しのアクセストークンによるリクエストで利用可能
  • checkJwtミドルウェアによるJWT形式のアクセストークン検証を実施
/api/private-scoped
  • スコープ“read:messages”有りのアクセストークンによるリクエストで利用可能
  • checkJwtミドルウェアによるJWT形式のアクセストークン検証に加えて、checkScopesによるscope検証も実施

3. Permission追加

「2. Backend API起動」で作成したAPIのPermission(スコープ)に、“read:messages”を追加します。その後、RBAC制御を有効化します。

  • Auth0管理画面で、[Applications] > [APIs]をクリック
  • 「2. Backend API起動」で作成したAPIを選択後、[Permissions]タブに移動
[+Create API]をクリック
  • 各項目を設定後、[+Add]をクリック
  • Permission (Scope):例) read:messages
  • Description:例) read messages(任意の説明文)
各項目を設定後、[+Add]をクリック
  • [Settings]タブでRBAC Settings:[Enable RBAC]を有効化し、画面下部の[Save]をクリック
[Settings]タブでRBAC Settings:[Enable RBAC]を有効化し、画面下部の[Save]をクリック

4. ロール作成とPermission付与

ロールを作成し、「3. Permission追加」で追加したPermissionを付与します。

  • [User Management] > [Roles]へ移動し、[+Create Role]をクリック
[+Create API]をクリック
  • 各項目を設定後、[Create]をクリック
  • Name:例) admin (任意の名称)
  • Description:例) admin (任意の説明文)
各項目を設定後、[Create]をクリック
  • [Permissions]タブへ移動し、[Add Permissions]をクリック
[Permissions]タブへ移動し、[Add Permissions]をクリック
  • 各項目を設定後、[Add Permissions]をクリック
  • Select permissions from existing APIs:My API(「2. Backend API起動」で作成したAPI)
  • Permissions:read:messagesにチェック
各項目を設定後、[Add Permissions]をクリック
  • Permissionsに”read:messages”が追加されたことを確認
Permissionsに”read:messages”が追加されたことを確認

5. ユーザ作成とロール付与

サンプルアプリへのログインユーザを作成します。その後、ユーザに対して「4. ロール作成とPermission付与」で作成したロールを付与します。

  • Auth0管理画面で、[User Management] > [Users]をクリック
  • [+Create User]をクリック
[+Create User]をクリック
  • 任意のEmailとPasswordでユーザを作成
任意のEmailとPasswordでユーザを作成
  • 作成したユーザの[Roles]タブで、[Assign Roles]をクリック
作成したユーザの[Roles]タブで、[Assign Roles]をクリック
  • 「4. ロール作成とPermission付与」で作成したロールを指定し、[Assign]をクリック
「4. ロール作成とPermission付与」で作成したロールを指定し、[Assign]をクリック
  • 該当ユーザにロールが付与されたことを確認
該当ユーザにロールが付与されたことを確認

6. Backend APIに対する認可要求を設定

サンプルアプリへログインした際、サンプルアプリからAuth0へ認可を要求するように、サンプルアプリのコードを修正します。

  • サンプルアプリの「public/js/app.js」ファイルを任意のエディタで開く
  • audience及びscopeをパラメータとして追加するため、以下の通りコードを修正(赤文字範囲を追加)
(略)
const configureClient = async () => {
  const response = await fetchAuthConfig();
  const config = await response.json();
  auth0Client = await auth0.createAuth0Client({
    domain: config.domain,
    clientId: config.clientId,
    authorizationParams: {
      audience: "api://my-auth0-workshop-api",
      scope: "profile email read:messages"
    }
  });
};
(略)

7. Backend APIリクエストボタン設置

サンプルアプリ画面上に、Backend APIを呼び出すボタンを設置します。

  • サンプルアプリの「index.html」ファイルを任意のエディタで開く
  • Backend APIを呼び出すボタンを配置するため、以下の通りコードを修正(赤文字範囲を追加)
<body class="h-100">
  <div>
    <button class="btn btn-primary" id="publicApiRequestBtn" onclick="publicApiRequest()">
      Public API Request
    </button>
    <button class="btn btn-primary" id="privateApiRequestBtn" onclick="privateApiRequest()">
      Private API Request
    </button>
    <button class="btn btn-primary" id="privateScopedApiRequestBtn" onclick="privateScopedApiRequest()">
      Private-Scoped API Request
    </button>
  </div>
<div id="app" class="h-100 d-flex flex-column">
(略)
  • Webブラウザでサンプルアプリ(http://localhost:3000)にアクセスし、Backend APIを呼び出すボタンの追加を確認
Webブラウザでサンプルアプリにアクセスし、Backend APIを呼び出すボタンの追加を確認

※この時点ではボタンは機能しません

  • サンプルアプリの「public/js/app.js」ファイルを任意のエディタで開く
  • ボタンクリック時にBackend APIを起動させるため、以下のコードを追加
// /api/publicへのリクエスト
const publicApiRequest = async () => {
  const myHeaders = new Headers();
  const response = await fetch("http://localhost:3010/api/public", {
    method: "GET"
  })
    .then((res) => {
      return res.json();
    })
    .catch((err) => {
      console.log(err);
    });
  alert(JSON.stringify(response));
};
// /api/privateへのリクエスト
const privateApiRequest = async () => {
  const myHeaders = new Headers();
  const accessToken = await auth0Client.getTokenSilently();
  myHeaders.set("Authorization", "Bearer " + accessToken);
  const response = await fetch("http://localhost:3010/api/private", {
    method: "GET",
    headers: myHeaders
  })
    .then((res) => {
      return res.json();
    })
    .catch((err) => {
      console.log(err);
    });
  alert(JSON.stringify(response));
};
// /api/private-scopeへのリクエスト
const privateScopedApiRequest = async () => {
  const myHeaders = new Headers();
  const accessToken = await auth0Client.getTokenSilently();
  myHeaders.set("Authorization", "Bearer " + accessToken);
  const response = await fetch("http://localhost:3010/api/private-scoped", {
    method: "GET",
    headers: myHeaders
  })
    .then((res) => {
      return res.json();
    })
    .catch((err) => {
      console.log(err);
    });
  alert(JSON.stringify(response));
};
  • Webブラウザ上でサンプルアプリのページを更新し、デベロッパーツールを起動※Google Chromeの場合は[F12]キーをクリックします
Webブラウザ上でサンプルアプリのページを更新し、デベロッパーツールを起動
  • 未ログイン状態の場合、[Public API Request]ボタンクリック時に以下ダイアログが開くことを確認
    「{"message":"Hello from a public endpoint! You don't need to be authenticated to see this. "}」
Webブラウザ上でサンプルアプリのページを更新し、デベロッパーツールを起動
  • [Private API Request]及び[Private-Scoped API Request]クリック時、コンソール上でエラー(Error: Login required)が表示されることを確認
[Private API Request]及び[Private-Scoped API Request]クリック時、コンソール上でエラー(Error: Login required)が表示されることを確認

Backend APIリクエスト動作確認

Permission(スコープ):”read:messages”を持つロールが付与されたユーザと、付与されていないユーザで、Backend APIの呼び出し結果が異なることを確認します。

  • 「5.ユーザ作成とロール付与」で作成したユーザで、サンプルアプリへログイン
  • [Private-Scoped API Request]ボタンクリック時、以下ダイアログが開くことを確認
    「{"message":"Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this."}」
[Private-Scoped API Request]ボタンクリック時、以下ダイアログが開くことを確認
  • ログアウト操作後、Permission(スコープ):”read:messages”を持つロールを付与されていないユーザでログイン
  • [Private-Scoped API Request]ボタンクリック時、以下ダイアログが開くことを確認
    「{"message":"Insufficient Scope"}」※スコープ不足を示すメッセージが表示されます
[Private-Scoped API Request]ボタンクリック時、以下ダイアログが開くことを確認

おわりに

本ブログでは、ロールベースアクセス制御(RBAC)の概要と、RBACを用いたBackend APIのアクセス制御設定例をご紹介しました。
Auth0の活用により、簡単にRBACの実装が可能であることを実感いただければ幸いです。

参考

お問い合わせ・資料請求

株式会社マクニカ  Okta 担当

月~金 8:45~17:30