Auth0を使って「サードパーティにAPIを公開する」システムを作る #Auth0JP
「サードパーティにAPIを公開する」システムを作りたい場合
本記事では、認証・認可プラットフォームである「Auth0」を利用して 自社サービスをサードパーティにAPIを公開する というユースケースの実現方法を考えてみたいと思います。
- Auth0を認証基盤とした自社サービスを持っている
- サードパーティの開発者向けに、自社サービスを使ったWebアプリを作ってもらいたい(Mashupのイメージ)
上記の要件だけであれば比較的シンプルに作れますが、以下の要件を加えて考えたいと思います。
- 自社サービスにはオーガナイゼーション機能があり、1ユーザーが複数のオーガナイゼーションに加入できる
- サードパーティWebアプリをユーザーが使用するには、どのオーガナイゼーションで使うか選ぶ(選んだオーガナイゼーションのみで利用できる)
ポイントは ログインを行わないとオーガナイゼーションを選択できない という点です。ログイン前に、ログインしようとしているユーザーがどのオーガナイゼーションに所属しているかは分かりません。そのため 2段階の認可 を行うようにする必要があります。
アーキテクチャ
メタデータ
Auth0のユーザーにはメタデータを持たせることができます。ユーザーが読み書きできる user_metadata
と、ユーザーからは読み取りのみできる app_metadata
のいずれかにキーバリュー形式で自由にユーザー情報を加えることができます。
今回は app_metadata
の中にオーガナイゼーションの情報を入れるようにします。
RBAC
Auth0のRBAC (Role-Based Access Control)を使うと、ユーザーから許可されたパーミッションを含めたアクセストークンを取得することができます。
独自のAPIに対してのパーミッションを自由に定義し、そのパーミッションをユーザーに割り当てます。その上でWebアプリを認証フローの中でユーザーに割り当てたパーミッションへのアクセスを許可してもらうことで、アクセストークンに含めることができます。
今回は各オーガナイゼーションごとに、アクセス権限となるパーミッションを1つ定義し、所属しているユーザーに割り当てるようにします。
構築
それでは構築する手順をご紹介したいと思います!
今回は Organization A, Organization B という2つのオーガナイゼーションを取り扱うこととします。
テナントの作成
まずテナントを作成し、サンプルアプリケーションが動作するところまで行なっておきます。
手順は本記事では割愛しますが、以下の導入記事を公開しておりますので参考にしてください。
今回はVue.jsのサンプルアプリケーションを使います。
RBACの設定 - APIとPermissionの定義
RBACの設定を行います。Auth0 Dashboardから「API」を選び、「CREATE API」からAPIを作成します。
「Permissions」タブで、各オーガナイゼーションへのアクセス許可となるPermissionを1つずつ作ります。
今回は手作業で追加しましたが、例えばオーガナイゼーションが作成される処理の中でAuth0 Management APIを使って自動生成する形で組んだりすると良いでしょう。
ユーザーへのメタデータ設定とPermission付与
今回は手作業で、テスト用のユーザーをオーガナイゼーションに所属している状態を作っていきます。
今回のアーキテクチャでは、ユーザーのメタデータを使ってオーガナイゼーションに所属していることを表現します。
まず「Users」から「CREATE USER」でテストユーザーを作成します。
「Details」タブの「app_metadata」を以下のようにし、保存します。
{ "orgs": ["a", "b"] }
次に「Permissions」タブを開き「ASSIGN PERMISSIONS」を押し、先ほど定義したPermissionを付与します。
次に、Ruleを使ってユーザーのメタデータがID Tokenに含まれるようにします。「Rule」を開き、「CREATE RULE」をクリックし「empty rule」を選びます。
以下のコードを記述します。
function (user, context, callback) { if (user.app_metadata && user.app_metadata.orgs) { context.idToken['https://example.com/orgs'] = user.app_metadata.orgs; } callback(null, user, context); }
これでログイン後、ID Tokenに https://example.com/orgs
というネームスペースに値が入ってくるようになります。
Webアプリの実装
次に、Webアプリのソースコードを修正します。ナビゲーションバーのメニューに、オーガナイゼーションの切り替えメニューを用意します。管理画面でよくある機能イメージです(例 : AWS Management ConsoleのSwitch Role)。
src/components/NavBar.vue
を開き、ナビゲーションにアイテムを追加します。
<li class="nav-item dropdown" v-if="$auth.isAuthenticated"> <a class="nav-link dropdown-toggle" href="#" id="profileDropDown" data-toggle="dropdown" > <img :src="$auth.user.picture" alt="User's profile picture" class="nav-user-profile rounded-circle" width="50" /> </a> <div class="dropdown-menu dropdown-menu-right"> <div class="dropdown-header">{{ $auth.user.name }}</div> <router-link to="/profile" class="dropdown-item dropdown-profile"> <font-awesome-icon class="mr-3" icon="user" />Profile </router-link> <a id="getAccessTokenBtn" href="#" class="dropdown-item" @click.prevent="getAccessToken(item)" v-for="item in $auth.user['https://example.com/orgs']" v-bind:key="item"> <font-awesome-icon class="mr-3" icon="building" />Switch to {{ item }} </a> <a id="qsLogoutBtn" href="#" class="dropdown-item" @click.prevent="logout"> <font-awesome-icon class="mr-3" icon="power-off" />Log out </a> </div> </li>
なお、上記に font-awesome-icon
を使っていますが、同じように使用する場合は src/main.js
を以下のように修正してください。
import { faBuilding, faLink, faUser, faPowerOff } from "@fortawesome/free-solid-svg-icons"; ... library.add(faBuilding, faLink, faUser, faPowerOff);
次に、同じく NavBar.vue
コンポーネントに getAccessToken()
メソッドを作ります。
methods: { async getAccessToken(org) { const accessToken = await this.$auth.getTokenWithPopup({ audience: 'https://example.com/orgs', scope: `write:org_${org}` }); console.log(accessToken); } }
アクセストークンの取得には getTokenWithPopup()
メソッドを使います。こうすることで scope
を要求する際に、認可画面をポップアップウインドウで表示することができます。
動作確認
それでは、一連の動作確認を行ってみましょう。まずは先ほど作成したユーザーでログインします。
次にナビゲーションバーのメニューを開き、まずは「Switch to a」をクリックします。
認可が表示され、アクセストークンが取得できました。
アクセストークンを jwt.io を使ってデコードすると scope
と permission
が取得できていることがわかります。scope
では write:org_a
を要求していることが分かります。
{ "iss": "https://dev-cw0xum-z.auth0.com/", "sub": "auth0|5dd21c1b07ef470efc08d0bf", "aud": [ "https://example.com/orgs", "https://dev-cw0xum-z.auth0.com/userinfo" ], "iat": 1574058601, "exp": 1574145001, "azp": "7TuIkOkquB7QexeUMQSgVe0Dl0EPfPI0", "scope": "openid profile email write:org_a", "permissions": [ "write:org_a", "write:org_b" ] }
次にナビゲーションバーのメニューを開き、まずは「Switch to b」をクリックします。
今度は scope
が変わり、write:org_b
を要求していることが分かります。
{ "iss": "https://dev-cw0xum-z.auth0.com/", "sub": "auth0|5dd21c1b07ef470efc08d0bf", "aud": [ "https://example.com/orgs", "https://dev-cw0xum-z.auth0.com/userinfo" ], "iat": 1574058601, "exp": 1574145001, "azp": "7TuIkOkquB7QexeUMQSgVe0Dl0EPfPI0", "scope": "openid profile email write:org_b", "permissions": [ "write:org_a", "write:org_b" ] }
ちなみに、以下のリクエストを行った場合は scope
は付与されません。
- そもそも存在しない Permission を指定した場合
- User が持っていない Permission を指定した場合
まとめ
Auth0のRBAC機能を使って、選択した所属(オーガナイゼーション)に対する権限付与を実装してみました。メタデータやPermissionなどは定義が自由に行えるので、様々なユースケースにフィットさせることができます。ぜひ活用してください。