Wantedly Engineering Handbook
  • Wantedly Engineering Handbook
  • まえがき
  • 第一部:開発チームへの案内
    • 技術とアーキテクチャ
    • プロダクト概要(未執筆)
    • 開発チームの構造
    • コミュニケーションの全体
    • ドキュメンテーション(未執筆)
    • カレンダー
    • 障害対応の心構え
    • 効率的な社内知識の調べ方
    • 外部発信(執筆中)
  • 第二部:技術領域への案内
    • Apps
      • アプリを提供するプラットフォーム
      • デザインシステム入門
      • Web アプリのアーキテクチャ
      • プロダクトデザイナーと上手に協働するための心得
      • Web アプリのデザインシステムライブラリ
      • Web アプリ共通ライブラリ "React Shared Component" の紹介
      • モバイルアプリのアーキテクチャ
      • モバイルアプリのデザインシステムライブラリ(未執筆)
    • The System
      • protobuf スキーマと gRPC 通信
      • 実践: gRPC in Ruby
      • 実践: gRPC in Go
      • GraphQL Gateway - アプリ向けに API を公開する
      • Wantedly Visit で BFF GraphQL サーバーを辞めた理由
      • 実践: GraphQL スキーマ設計(未執筆)
      • API での認可処理 (Authorization)
      • マイクロサービス共通ライブラリ "servicex" の紹介
      • 非同期メッセージング処理入門(未執筆)
      • バッチ処理入門(未執筆)
    • Infrastructure
      • Infrastructure Squad
      • プロダクト開発のための Kubernetes 入門
      • インフラ構成概要
      • リリース・デプロイ戦略を支える技術
      • SaaS を活用する:New Relic, Honeybadger, Datadog
    • Data
      • データ基盤入門
      • レコメンデーション
      • Looker 入門
      • 推薦システムの開発に使っているツール
    • 開発プロセス
      • Git の慣習
      • ポストモーテムの取り組み
      • 負債返済日の取り組み
      • プロダクトの課題発見及び解決
      • ソフトウェアデザインの基礎
      • コードレビュー
      • コーディング規約
      • リリース・デプロイ戦略
      • 上長承認が必要な作業
      • アーキテクチャディシジョンレコード(ADR)
      • 作業ログを残す意味
      • 多言語化対応(i18n)
      • メール開発
    • 開発ツール
      • 社内利用している開発ツールの最新状況
      • kube
      • Code Coverage
      • Kubefork
  • おわりに
    • ロードマップ(未執筆)
    • Handbook の書き方
    • コントリビューター
  • 付録
    • 社内用語集
    • 主要な GitHub レポジトリのリスト(未執筆)
    • 今後の挑戦・未解決イシュー(未執筆)
    • プロダクト開発組織のバリュー(未執筆)
    • 採用についての考え方(未執筆)
GitBook提供
このページ内
  • TL;DR
  • 前提となる枠組み
  • APIエンドポイントの分類
  • システム全体の動作
  • 規約
  • 規約1:API の種別の表明
  • 規約2:「呼び出し元ユーザー」の入力インターフェイス
  • 規約3:認可に失敗した場合の表明
  • 補足: 規約に含めないと判断した項目
  • 規約の適用・実装に関して

役に立ちましたか?

  1. 第二部:技術領域への案内
  2. The System

API での認可処理 (Authorization)

誰がどのように、システムが持つリソースを取得・変更して良いのかの権限チェックを認可処理といい、システム上では何らかの認可処理が必要となります。

認可処理について、チームによって異なる方法を取っていたり、共通の認識がなかったりすると、チェックの漏れや重複が発生し問題となります。本章では、マイクロサービスアーキテクチャを前提とした、単純な認可処理の方針を定めています。

TL;DR

  • 認可処理はどこで行うか? → 該当データのオーナーとなるマイクロサービスの API を境界として、API の実装に認可処理を含める。

  • 認可処理はいつ必要があるか? → デフォルトでは行うが、行わないことを API 単位で表明して認可処理をスキップすることもできる。

前提となる枠組み

APIエンドポイントの分類

全ての API のエンドポイントは、以下の二つのいずれかに分類できます。

exposable…呼び出し元のユーザーに基づいたデータの取得・変更の権限チェックが行われているもの(デフォルト)

unexposable…そのような制御が行われておらず、任意のデータの取得・変更が行えるもの ... internal

exposable な API は暗黙的に「呼び出し元のユーザー」という入力をインターフェイスに持ちます。例えば、プロフィール情報の取得 API であれば、つながりなどの公開範囲設定に応じて返すプロフィール情報が変わるように実装します。

unexposable な API は日時のバッチ処理からの呼び出し(システム内部での処理)や管理画面など、入力内容も含めて API 呼び出し全体が信頼できると仮定できる前提で用意するものです。

これらの分類は、直接的な認可処理に限らず、API の責務全体を定義します。例えば、ActiveRecord で生成される updated_at はシステムの内部情報なので、仕様として公開することを意図していない限り exposable な API で返すべきではありません。

デフォルトでは exposable なものとしてエンドポイントを分類・実装するようにします。これは「各マイクロサービスが自分自身の持つデータについて取得・変更の権限を確認するようにする(別のサービスに任せない)」ということでもあります。

一方で unexposable なものとして実装されたエンドポイントは、basic authentication(!= authorization)やネットワーク的に隠すなど、内部の者で信頼できることを示す何らかの証拠を求めるべきです。

システム全体の動作

我々の理想とするアーキテクチャにおいて、エンドユーザーからのAPI呼び出しはまず GraphQL gateway が引き受けます。言い換えれば、それ以外の API(システム API と呼ぶことにする)は全て隠蔽されていて、エンドユーザが直接叩くことができません。

この gateway は、cookie session や token などのクライアント情報を元に「呼び出し元のユーザー」を認証します。そして、システム API を認証済みの「呼び出し元のユーザー」を入力として叩きます。

システム API が他のシステム API を叩く場合は、バケツリレー方式でこの認証情報を伝搬し、同じインターフェイスで入力に含めます。

通常は、gateway が引き受ける API 呼び出しから間接的に呼び出される API は全て exposable なものとなる はずです。

規約

以上のような枠組みをサポートする、いくつかの規約を導入します。

規約1:API の種別の表明

API の種別を proto で次のように表明するようにします。skip_authorization = true であれば unexposable、skip_authorization = false であれば exposable、何も書かなければゼロ値で false なのでやはり exposable、となります。つまり、デフォルトは exposable とすることを意図しています。

rpc Echo(SimpleMessage) returns (SimpleMEssage) {
  option (wantedly.api.rpc).skip_authorization = true;
}

API を実装するコードレビューの観点では、skip_authorization = true という表明がない限り、呼び出し元のユーザーに応じて適切な取得・変更の制御を行なっていることをチェックすべきです。

API を呼び出すコードレビューの観点では、その呼び出し元の API 自体が信頼できる呼び出し元から呼び出されているのでなければ、skip_authorization = true の表明をしている API につなぎこまないようにチェックすべきです。

規約2:「呼び出し元ユーザー」の入力インターフェイス

「呼び出し元ユーザー」の入力は、x-current-user-id という名前の HTTP / gRPC ヘッダーに入れるようにします。"current user id" よりは "authenticated user id" の方が本来の意味と近いですが、既存のコードを鑑みてこのようにしています。

社内マイクロサービス共通ライブラリである servicex にも特別のインターフェイスを用意して、バケツリレーが必要な場合や、信頼できる呼び出し元からの実行を助けていくのが今後の方針です。実際に規約をどう実現するかに関しては、言語・F/Wごとにデザインすることになるので本章では触れません。

例:

YashimaApi.new(current_user_id: current_user.id).get(...)

規約3:認可に失敗した場合の表明

以下のステータスコードを返すようにします。

  • HTTP: 403(Forbidden)

  • gRPC: PERMISSION_DENIED

補足: 規約に含めないと判断した項目

以下は将来的に組織がスケールしたりプロダクトが成長した場合に導入される可能性があるものです。現時点ではコストに見合わないと判断しています。

  • 「スコープ」のような高級な抽象化 ... スコープのリストと意味自体をアプリケーションとシステムで意識する必要が出てくる

以下は案として出たが期待する問題をうまく解決してくれるかわからないのでひとまずやらないとなったものです。

  • 「会社」を認証対象にする(current-company-id のようなもの)... 複数の認証対象を取り扱う必要が出てくる

以下はやらない方が今後も良いだろうと判断しているものです。

  • current-user-id のような認証情報を暗黙的に伝播すること ... 例えば Sidekiq のようなバックグラウンドジョブに渡すときには、常に明示的に渡しているが困っていないため

規約の適用・実装に関して

方針と規約はアーキテクチャ上の決定として用意しますが、各言語・コーディングレベルでどのようにその規約を適用するかはある程度自由に考えることとします。

例えば、ある言語・フレームワークでは、unexposable な API を特定の名前空間にまとめる、ということが自然である場合はそのようにするのが良いです。実際に、wantedly/wantedly の gRPC services 以下は、そのような実装になっています。

話を聞きに行きたい

もっと知りたい

前へ実践: GraphQL スキーマ設計(未執筆)次へマイクロサービス共通ライブラリ "servicex" の紹介

最終更新 2 年前

役に立ちましたか?

Slack: ,

#backend_chapter
#microservices
ユーザのデータアクセス権限の認可をどうしていくべきか · Issue #431 · wantedly/dev (internal)
システムが持つデータの取得・変更の権限チェックの仕方(認可:Authorization) · Issue #487 · wantedly/dev (internal)