この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
「サードパーティに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などは定義が自由に行えるので、様々なユースケースにフィットさせることができます。ぜひ活用してください。