こんにちは、最近ブルグミュラー15曲目(原曲No.20)が終わった内波です。 「じゃあ小学校3年生くらいだね」と言われて少しだけしょんぼりした瞬間もありましたが楽しくやっています。
このブログではだいぶご無沙汰していましたが、最近、我々の会社の立場がかなり稀有なもので、そこで得られている情報をもっと多くの人に広く共有していくべきだな、と感じるようになったので、久々に筆をとりました。 何がそんなに珍しい立場かというと、他社が提供するAPI、その中でも特に、単純な「機能」としてのAPIではない、ユーザーの認可を受けてその代理人として利用するAPIを、これほど多く扱っている企業はなかなかないんじゃないかな、というものです。
はじめに
この記事で取り扱うAPI
API(アプリケーション・プログラミング・インターフェース)という単語は非常に広い意味で利用されるものですが、この記事でフォーカスするものを一言で述べると「第三者向けユーザー認可型」のWeb APIになります。
第三者(サードパーティ)向け
昨今いろんな場所で聞くマイクロサービス化の文脈で登場するAPIは、まずは自社内で利用されることを前提にして作られているものですが、サードパーティ向けAPIはそうした内部向けのAPIではなく、APIを公開する企業とは異なる会社・人が利用するためのAPIのことです。 場合によってはそのAPIを開発するうえで、API利用者となりそうな第三者に対して、事前のヒアリング等が行われるケースもありますが、そのようなヒアリングとは全く無関係だと思われていたような利用者が急に現れることもあります。
そして、そうした第三者向けに公開されているAPIを利用するアプリケーションをある観点で見たとき、僕が勝手に「役務提供型」「機能補完(拡張)型」「相乗効果型」と呼んでいる見方があります。
役務提供型
「役務提供型」とは、例えば Google マップのAPIや Vision API(画像を投げるとその中身を解析した結果を返してくれるAPI)のような、第三者が自前で作ろうとすると大変なコストがかかるようなある「サービス」を、APIを通じてそのまま利用させてくれるようなものです。 この見方ができるAPIは基本的に、その機能自体が、価値を届けるべきユーザーとは独立して存在しています。すなわち、Google アカウントを全く持っていない消費者に対しても、地図機能や画像解析機能を使った価値を提供することができます。
ユーザー認可型
そうではない「ユーザーに依存したAPIって何?」と思われる方もいるかもしれませんが、それがまさに今回の話のターゲットである「ユーザ認可型」のAPIのことで、「機能補完(拡張)型」「相乗効果型」利用シーンで登場します。 「機能補完(拡張)型」「相乗効果型」は、そのAPI提供元のサービスにも利用者が存在しており、API利用アプリケーションがそのユーザーの認可を受けることによって、「ユーザーの代わりに、ユーザーの代理人となって」APIを利用させてもらうようなものです。
機能補完(拡張)型
その中で「機能補完(拡張)型」とは、API提供元サービスとAPI利用アプリケーションに「主従」に近い関係性があり、ユーザーがその2つのサービスを介して受ける体験が、主に「API提供元サービスを利用している中で便利な拡張機能がある」と感じるようなものです。 例えば、Salesforce のアプリストアに並ぶアプリの多くは、ユーザーから「Salesforce にこのアプリをインストールすることでより便利になる」というイメージで利用されていることが多く、Salesforce から離れたアプリ単体で意識されるシーンは少ないと思いますが、実体としてはユーザーから認可を受けてもらったトークンを使い Salesforce のAPIを利用することで、ユーザーの「Salesforce をより拡張する手足」となっています。 あるいは、アプリストアに並べずとも、単に公開されているAPIを使って利用者自身の特別な業務オペレーションを効率化する、という使い方も、この「機能補完(拡張)型」に近い利用です。 少なくともそのアプリケーション自体は「ユーザーの代わり」になって何かを操作するが、その提供価値がAPI提供元サービスに大きく依存しているもの、それを「機能補完(拡張)型」と(勝手に)呼んでいます。
相乗効果型
一方、この「ユーザーの代理人」となって動くアプリケーションの中には、API提供元・先であまり「主従」の関係がなく、ユーザーにとって、API提供元サービスも利用先サービスもそれぞれ独立した異なるサービスである、と感じられる利用シーンがあり、そこでのAPIの利用を「相乗効果型」と(勝手に)呼んでいます。 例えば、マネーフォワードでの代表的な第三者向けAPI利用シーンである、「銀行口座とのデータ連携」機能はそういった使われ方の一つです。 ユーザーがマネーフォワード MEアプリ内で、複数の銀行口座や複数の証券口座を連携し、自分の資産残高をひとまとめに見ることができる、という体験をするとき、その裏ではマネーフォワードはユーザーの認可を受け、各銀行のAPIを利用していますが、ユーザー自身が、複数ある銀行のどこか一行で「銀行機能拡張としてマネーフォワードMEをインストールしている」と感じることは稀ですし、一方でマネーフォワード MEの中の一部機能としてその銀行がある、と感じていることもないと思います。 あくまで銀行とマネーフォワード MEは独立して存在し、その間での代理人としての働きが、今までになかったタイプの体験をユーザーに生み出しています。
なお、この勝手な呼び名は、あくまでAPIの利用シーンによる見方なので、ある公開されたAPIがどちらの型か、ということは言えません。ある一つのAPIが、あるアプリケーションで使われる場合は機能補完型だし、別のアプリケーションで使われるときは相乗効果型である、ということがあり得るためです。
2つのサービスが連携することによるUX
ただ、いずれにせよ、ユーザー認可型のAPIとそれ以外のAPIでは、価値を届けるべきユーザーが受ける体験をどのようにコントロールするか、という点で決定的な違いがあります。 役務提供型のような、あるAPIを単なる道具としてだけ利用することができるAPIの場合、たとえ他社のAPIであろうとも、アプリケーションを利用するユーザーの体験(UX)を原理的には自分達だけでコントロールすることが可能ですが、ユーザーの代理となってAPIを利用する形の場合、どうしてもそのAPI提供元でのUXを切り離すことができず、さらにそれが「相乗効果型」=API提供元サービスが主体的にUXをコントロールする立場ではない場合、統合されたユーザー体験を自分達で構築する難易度が高くなる、というのがこれまで多くのAPIに対応してきた僕の感覚です。
今回の記事では、そのようなAPI利用シーンにおける「認可」、すなわちユーザーから代理人となる権限を受け取る部分の仕組みとUXの関係性を、数多くのAPIに対応してきたマネーフォワード社ならではの視点からまとめてみました。
認可の仕組み
最近はご存じの方も増えてきましたが、「ユーザーの認可をもらってAPIを利用させてもらう」ための仕組みには世界的な標準仕様が存在しています。 OAuth 2.0 や OpenID Connect(OIDC)、Financial-grade API(FAPI)と呼ばれるもので、マネーフォワードが利用させてもらっている各銀行のAPIでも、認可部分はこの標準仕様に従うことが推奨されています。 これらの標準に関する解説は別記事に任せますが、この仕組みに従うと、あるサービスAの利用者は、第三者に対してそのサービスの機能を自分の代わりに利用することができる「鍵(トークンと呼ばれます)」を安全に渡すことができます。
この認可の仕組みを解説した記事は多くあり、例えば僕自身、OAuth についてまだほとんど知らないメンバーに対する日本語の説明のとっかかりとして、ネット上にある優れた技術記事を紹介することはあります。 しかしこうしたWeb上の記事はあくまで「概要を理解する」ために利用するためのものであり、実際の実装において参照すべき先は RFC 等の標準技術仕様以外にはありません。誤った情報をもとに実装することが無いように公式のドキュメントを参照しましょう。
この記事でも、誤った理解になることを避けるために、どのようにして認可サーバーやクライアントアプリケーションの認可処理を実装するか、という点については具体的な言及は避けています。 では何の話をするかというと、こうした RFC 等で定められている「標準仕様」に乗っていない部分の仕様が、どのようにユーザー体験(UX)に影響するか、と言ったような話になります。 タイトルが「標準仕様範囲外の仕様」と書ていますが、「標準仕様に違反している」という意味ではなく、標準仕様では定義されていない、API開発者によって自由にできる部分を、どう設計すればより優れたUXを実現できるかを検討しています。
マネーフォワード社の特異性
皆さんが Web でサービスを提供している企業におられる場合、自社で OIDC や OAuth 2.0 に準拠した認可サーバーを持っている、という方が大勢いらっしゃるでしょう。
我々マネーフォワード社でも、自社サービスのID基盤である「マネーフォワード ID」において OIDC を使った認証基盤を構築していたり、事業者向けサービス「マネーフォワード クラウド」シリーズで、バックオフィス業務を顧客企業特有のオペレーション用に自動化・カスタマイズできるようなオープンAPIを OAuth 2.0 により提供していたりします。
一方で、そのような公開されている他社のサードパーティ向けAPIを、実際に利用・運用されている方はどうでしょうか。 Google ログインや Apple サインインに対応することは他社の Web API を利用している代表的なケースです。ただそれらはあくまで「認証機能」として利用されるに留まり、実際にそのトークンを使ってユーザーの代理として何かを行うことは少ないかもしれません。
100を超えるアプリケーション
実はマネーフォワード社では、銀行をはじめとした、多くのお金に関わるサービスと Web 上でのAPI連携を行っており、数で言うと軽く100を超えるサービスのクライアントID(アプリケーション)を保有しています。
「数は多くても銀行は統一仕様で動いてるでしょう?」とお思いの方もいるかもしれませんが、インターネット上で仕様を公開されているいくつかの先進的な企業/銀行のAPI仕様を見ていただければわかる通り、たとえ銀行業界であっても、緩く OAuth 2.0 や OIDC, FAPI 準拠、といった流れがある以外、それぞれかなり異なる作りとなっています。 OAuth 系の仕様の中でも「どの」仕様を使うかということだけでなく、その標準仕様に定められていないものは各社が自由にできる部分でもあるため、それぞれ特徴が違っているのです。 さらに、ある銀行とのAPI連携一つをとっても、単に情報の照会を行うシーンだけでなく、APIを使って振込を行うためのいわゆる「更新系」APIの利用では全く異なるUXが存在します。
銀行以外との連携も
また、銀行以外では、証券会社や仮想通貨取引所、クレジットカードや電子マネーといった決済サービス、保険会社の契約情報からECサイトの購入履歴にふるさと納税履歴、さらには事業を営んでいるユーザー向けにネット上のショッピングモールでの売上情報や店舗の決済・POS情報といった、様々な外部サービスの機能をAPIで扱っています。
あまりこの数や範囲の意味について誰かと話したことはないのですが、おそらく日本で一番この手の Web API を多く利用し・運用し続けている会社であり、世界的に見てもかなり稀な立場なのではないかと思っています。
なお、僕が通常の業務で触れるAPIの仕様は、提供元によってはオープンなAPIではないものも含まれるため、この記事では特定の企業が提供するAPI仕様の詳細には触れず、エンドユーザーから見える挙動と一般的な課題のみを例に挙げながらまとめています。 そのため「あそことの連携のあれはどうなっているの?」といった質問にはお答えできないことの方が多いですが、より一般的な質問であれば回答できることもあるかもしれないので、お気軽にお声がけください。
様々な観点からのUX
セキュリティ
繰り返しになりますが、この記事ではユーザー体験にフォーカスするため、具体的なセキュリティ対策については触れません。 なぜなら、OAuth 2.0 や OIDC, FAPI の標準仕様やその拡張を議論するコミュニティにおいて、多くの専門家の厳しいレビューを通ったものが RFC 等にまとめられており、それを正しく理解して、適切なフローやオプションを組み合わせること以上に、セキュリティを担保するより良い方法がないからです。 もし標準仕様を読み、「この仕様ではこのケースの安全を担保できないのではないか?」という疑問を持たれた場合、是非それをコミュニティに相談してみてください。 本当にそのケースのケアが漏れているのであれば、世界中の人々が使う仕組みをより安全にすることに貢献できますし(まさにそのようにして仕様がブラッシュアップされてきました)、そうではなく単に勘違いをしていただけであれば正しい方法を教えてもらうことができるでしょう。
例えば、「この認可コードというものはブラウザのアドレスバーに表示されるのか。万が一漏れてしまっても大丈夫なようにしよう。」と思ったのであれば、「認可コードを不正に使われない方法」を検索していくことで、PKCE という適切な仕組みに到達できると思います。 そこでいきなり、専門家やコミュニティへの確認を飛ばして「ここの部分をこうすればいいんじゃないかな」と独自のロジックを入れ始めると、仮にそれが本当にセキュリティを担保するものであったとしても、APIを利用する側に負担を生み、せっかく作ったAPIが使われづらくなる、という不幸な結果を生んでしまいます。
一点、UXについて言えることがあるとすれば、これらの標準仕様はまず大前提としてセキュリティを担保することが目的にあるため、優れたUXにすることを最優先にしているとは言えないかもしれませんが、それでも間違いなくUXを考慮して設計されています。 使いづらい仕様では誰にも利用されずに意味がないから、ということも当然一つの理由だと思いますが、認可のフローにおいてユーザーがコントロール(操作)する部分を極力減らすことが、セキュリティとUX両方に良い効果があるからなのかな、というのが僕の感覚です。
認可時のセッション断絶の罠
OAuth 系の認可フローは基本的に、ユーザーが操作するエージェント(代表的なものがウェブブラウザ)を介して、API提供元のサービスからクライアントアプリケーションに鍵が渡されます。 一般的な認可コードフローでは、認可エンドポイントにアクセスしたユーザーが承認を行うことで、認可コードをURLパラメータに抱えてクライアントアプリケーションにリダイレクトされ、その認可コードを使ってアプリケーションがトークンを取得します。
ここで重要になってくるのは、ユーザーが他人に認可コードを奪われたり、逆に他人の認可コードを意図せず使わされたりすることを避けるセキュリティ対策のため、基本的に最後のコールバックはフローが開始された際に利用されていたエージェント(ブラウザ)に戻ってくる必要がある、ということです。 単一の端末・ブラウザでしか双方(API提供元・先)のサービスを利用するシーンを考えていないと気づきにくいのですが、これが断絶するシーンは意外にあります。
ブラウザのチェンジ
例えば、API提供元サービスが利用可能なブラウザを制限している場合、ユーザーはまず権限を与えたいと思っているアプリケーション側を普段使いのブラウザでスタートするものの、認可画面に飛んだあと「お使いのブラウザには対応していません」という表示に遭遇します。その後のユーザーの自然な行動が何になるか想像がつくでしょうか?一番多い行動は、その時のアドレスバーのURLをコピーし、推奨ブラウザを立ち上げ、そのURLを開く、です。
仮にそのアクセスが有効(認可エンドポイントへのリクエストパラメータが全部ついたURLであれば有効)であろうと、無効(認可エンドポイントからリダイレクトをかけてステートフルになっていると無効)であろうと、結局その後の認可処理が正常に完了することはありません。
ブラウザによる制限以外にも、認可サーバー側へのアクセスに「クライアント証明書が必要」「IPアドレス制限を入れている」(馴染みがないかもしれませんが、事業者向けサービスではそれなりにあります)等の制約がある場合も似たようなことが発生します。 ユーザーがこの操作(アドレスバーのURLをコピーして別の端末・エージェントに持っていく)をした結果、最終的にどこかでエラーになる、という体験が発生すると、普段ユーザーが自然に体得している「アドレスバーにあるURLをブラウザに張り付ければそのページが見られる」という感覚から外れた体験であるため、原因を理解してもらうことが難しいです。 特にURLペーストしたアクセスが有効な場合、認可画面は正常に表示され、ユーザーがそのページで「アプリZにこの権限を委譲しますか?」に対して「OK」を押した後、リダイレクト先のアプリケーションサイト側でエラーが表示されることになるため、本来の「やってはいけない操作」からかなり離れたところ(何個か画面遷移をした後)でエラーメッセージを見ることになり、何がまずかったのか・どうすれば正常に処理を完了させることができるのかの説明が難しくなります。
回避方法は「クライアントアプリケーション自体へのアクセスを、API提供元サービスにアクセス可能な端末・ブラウザを使って、処理を開始してください」というものになりますが、このメッセージをユーザーが「やってはいけない操作」をしたその瞬間に出すことができれば、ユーザーからの問い合わせを減らすことができるでしょうし、もっと理想的には認可フロー時にそのような制限が入らないと好ましいです。 特にブラウザの制限は、本来API提供元サービスとは独立して存在するはずのアプリケーションサービス側でもそのブラウザをサポートする必要があることに注意しましょう。
認証操作時の断絶
またこのセッションの断絶は、アクセス制限以外でも発生するケースがあります。認証時に操作対象のエージェント以外の操作が挟まるケースです。 「認証と認可は別物だ」というのは、OAuth をはじめとした認可の仕組みを理解する際に最初に学ぶことの一つですが、認可を行うユーザーを認証する必要があるため、認可フローの中に必ず認証が挟まります。 昨今では、ブラウザからの ID/PW 送信による認証以外に様々な新しいタイプの認証手段が採用されてきています。
そうした技術を利用する際、例えばスマホ認証のようなユーザーが認可リクエストを投げているエージェント内で処理が続く場合は問題がありませんが、ログインリクエスト元エージェントとは異なるエージェントが立ち上がるような認証手段があると気を付ける必要があります。
また、API提供元がスマホアプリメインのサービスである場合、ユーザーの端末内でログイン済みのアプリを利用することで、ID/PW 入力のような面倒な認証操作をスキップさせるケースもあります。 認可リクエストをどのようにしてスマホ内のアプリに投げるかは別の記事に譲るとして、アプリ内で認可を行った後に「当初操作されていたスマホ内のエージェント」に戻ってこられるか、には注意が必要です。 例えば何も考えずスマホの標準ブラウザに戻るような作りにしてしまうと、ユーザーが当初操作していたエージェントがそれとは違っていた、ということもあり得るためです。
こうしたセッションの断絶による認可が失敗する問題は、原理的には、サーバーサイドで動くアプリケーション、かつユーザーがログイン済セッションを持っている状態から「のみ」認可フローをスタートできる場合、アプリケーション側でセッションに紐づけるべき情報(state や PKCE のパラメータ等)をユーザーIDとセットでデータベースに管理することで、コールバック時に再ログインが行われても検証を担保することができるかもしれません。 ただそもそも、OIDC で最もメインに利用されるシーンである「認証機能」としての利用の場合は、前提であるログイン済セッションがないため当然このようなことは無理ですし、どのようなフローで認可が始まるかはAPI提供元で管理できることでもないため、認可時に発生する「認証」においてセッションの断絶が生まれにくい設計が望ましいでしょう。
トークンの有効期限
トークンの有効期限をどうすべきか、という点はAPI提供者にその決定権限が委ねられています。 ですが一般的には、トークン漏洩時のリスクを考慮し、それ単体でAPIをコールすることが可能なアクセストークンの有効期限は極力短くし、そのアクセストークンを再発行することができるリフレッシュトークン(その利用には別途クライアント認証を課すことで、リフレッシュトークンの漏洩にも耐性を持たせる)の期限を長く取ります。 ただし、リフレッシュトークンそれ自体の有効期限を無期限にしてしまうと、利用されないことが確定している(しかしトークン管理サーバーではそれを知ることができない)トークンも未来永劫保持し続ける必要があり、無駄なデータが残り続けてしまうため、リフレッシュトークンにもある程度の有効期限を持たせ、その期限内でリフレッシュされ続ける限りは無期限に利用可能にする(トークンリフレッシュを行った際に再発行されるリフレッシュトークンの有効期限がまた新たに再定義される形)、という構成がUXを最大化する一般的な構成ではないかと思います。
行われた「認可」自体には期限がなく、ユーザーが明示的にそれを拒絶しない限りは利用可能な状態が続く方が、ユーザーの代理人たるアプリケーションが、バックグラウンドでも常に働き続けてくれることが保証されますし、ユーザーの無駄な操作も不要になります。 おそらく皆さんが触れる多くの一般的なWeb APIはそのように作られていると思います。
もちろん、なんらか法的な要件があるような場合はそれに合わせて認可に期限を設けるべきです。 現実の世界では例えば、遺言状には有効期限はないそうですが、有効期限のある行政手続きの委任状のようなものをAPI化する場合は当然それがトークンの期限にも適用されるでしょう。
安全性の観点
ですが、そうした明確な目的指標がない場合においては、セキュリティ上も認可の有効期限は長い方が安全であると考えています。 というのも、パスワードの定期変更がもはや安全対策として推奨されないどころか、むしろ逆にセキュリティを低下させてしまう圧力にもなり得ると言われるように、ユーザーが定期的な認可操作をルーチンワークと考えてしまうと、ふとしたきっかけで、フィッシング等の被害にあって誤ったアプリケーションに認可を行ってしまう可能性が高まってしまうと考えているからです。
例えば、家事代行サービスを契約しており、週二回自宅マンションにプロが家事をしに来てくれます。 しかしマンションの管理会社からは「住人以外に与えられる鍵は最長1か月しか使えない。必要なら都度再発行手続きをしてください。」と言われました。 毎月その鍵再発行作業を行っていたところ、ある時ちゃんと確認せずに承認をしてしまって、本当の家事代行者以外のものに鍵が渡ってしまった、というような被害です。
「私の提供するAPIは、厳選された開発者のみが使えるような制限を入れているため、悪意あるアプリケーションにトークンが渡ることはない」という反論があるかもしれませんが、何らかのシステム不備・考慮漏れ、フィッシング等により「正当なアプリケーション」内で「悪意あるユーザー」にトークンを取られる可能性や、ユーザー自身は認可操作をあなた以外のサービス上でも行っているために、あなたのサービスによって定期認可に慣れた結果、他の似たサービスで油断して認可をしてしまう、というよう影響もあり得ます。
こうした被害を防ぐためにも、ユーザーには極力「認可操作とは非常に特別なものであって頻繁に行うものではない」と認識してもらい、認可時は注意して操作してもらえるような常識が生まれるような標準ができればと考えています。 セキュリティはまず技術で担保し、それ以外の運用部分では、ユーザーに極力操作をさせないこと、そしてユーザーに安全な行動をとってもらえるようなマインドを植え付けることで高めていく、という道です。
トークンの数
トークンを管理するテーブルのスキーマ設計が標準仕様に出てくることはありませんが、API提供側がAPIのユースケースとして自社サービスに「インストール」されるようなアプリ(「機能補完(拡張)型」的な利用シーン)しかイメージしていないと陥りがちな穴があります。
ユーザーによる認可行為は「"ある"アプリケーションに対する認可であり、そのアプリへ発行するトークンは1つで良い(再度同一アプリに対して認可されたら、古いトークンは消しても良い)」というのはなんとなく自然に思えます。 特に、ユーザーに見せるトークンの失効画面(いわゆる「連携アプリケーション一覧」みたいなもの。GitHubでいう所のこれ https://github.com/settings/applications )のUIから考えると、まさにそのように見えますし、その設計にすることでトークンのデータ容量も推定が容易になります。
とは言えもちろん、クライアントアプリケーションが、ユーザー保有端末で動作するアプリの場合、スマホ2台持ちユーザーが双方の端末にアプリを入れるシーンがあるため、あるユーザーがあるアプリケーションに対して発行するトークンが複数になることを許容する設計が必要になります。 あるいは、同一のアプリケーションIDで異なる scope の認可を取る利用シーンがあれば、その際にもそれぞれの scope で有効なトークンが求められます。
サーバーサイドアプリケーションでの様々なトークン管理シーン
しかし、たとえアプリケーションをサーバーサイドで動くものしか許容せず、かつマイナンバーのように認可を行える「人」が原理的に地上に一人しかいないものであり、scope も1つしかなかったとしても、同一のアプリケーションから複数の有効なトークンを求められるケースはあります。
クラウドサービスによる複数アカウント
例えば、中小企業向けに、その従業員のマイナンバーを管理する業務システムをクラウドで提供するようなサービスZを考えてみましょう。 ある人Xさんは、企業Aと企業Bに所属しており、それぞれの企業に対して自分のマイナンバーを提供する必要があります。 その際、A社もB社も同じマイナンバー管理クラウドサービスZを導入しており、そのサービスZではマイナポータル(国が提供する go.jp サイト)のAPIを利用してユーザーの認可を受けることによりマイナンバーの情報を取得する仕組みになっています(※そのようなマイナンバーAPIは存在しませんが、イメージはしやすいと思うのであると仮定してください)
当然、Xさんは企業Aの従業員アカウント x@A.co.jp@
でログインした後、マイナポータルでZに対する自己情報の閲覧認可を行いますし、また企業B用のアカウント x@B.co.jp
でも同様のことを行います。
その結果、同一のアプリケーションZに対してXさん個人のトークンが複数発行され、それぞれ独立して管理されるのです。
「そのようなケースでは、クラウドサービスZがAPIを利用するとしても、企業Aと企業Bそれぞれで異なるアプリケーションを発行すべきである」というのは一つの指摘としてあり得ます。 しかし、この国に300万も存在する中小企業のすべてが、このAPIと呼ばれる複雑な仕組みのアプリケーション管理者となることを期待することは現実的ではありませんし、しかも結局、このトークンを利用するのはA社やB社のような企業自身ではなくクラウドサービスZですから、企業自身にアプリケーションを管理させるのも不自然です。 そうなると、そのクラウドサービスZを提供する事業者に対して、100万を超えるアプリケーションを任意に発行できるような別のAPIを公開すべきでしょうか?
単一サービス内での複数アカウント
この手のいわゆる SaaS 型のクラウドサービスはどんどん増えてきていますが、こうしたサービス提供形態以外でも、もっとシンプルに、ある人があるアプリケーション内で複数のアカウントを持っている、ということはいくらでもあります。 例えば動画配信サービスが、そのユーザーの年齢や所在地を確認するためにマイナポータルAPI(※これもあくまで仮定のものです)を利用するとします。年齢は登録時だけでもいいかもしれませんが、住所は常に最新のものを知りたい場合は、一度認可してもらったトークンがしばらく利用できる必要があります。 一方、その動画配信サービスは、ユーザーがお金さえ払ってくれれば、全く同一の「人」が複数のアカウントを持っていても特に気にしません。むしろ、家族で楽しむためのアカウントと、家族にも閲覧を秘密にしたい作品を見るプライベートアカウントを2つ持っている人が大勢いるかもしれません。
提供側・利用側それぞれの注意点
このようなケースまで考慮すると、同一アプリケーションに対して複数のトークンが払い出されるシーンがイメージできるでしょう。 自社で提供するAPIが、自社サービスを単に補完(拡張)するためだけのものだと考えると、こうした要件が浮かばないこともあるかもしれませんが、自分たちのサービスとは全く関係がない第三者のサービスから新たな機能としてそのAPIが利用される場合、自分達でも想像がつかないようなユースケースが存在し、そして何よりそこにこそイノベーションが生まる種があるかもしれない、ということを考慮に入れておきましょう。
なお、APIを利用するクライアントアプリケーション側では、仮にトークンが一つしか発行されないAPIを利用する場合でも、自前でリソースオーナー単位にトークンをマージして使う(先の例で言うと、マイナンバーの番号で判断して「同じXさんだから」と、企業A用アカウントでも企業B用アカウントでも同一のトークンを利用する)、というのは決してやってはいけない設計です。 正当なリソースオーナーによる正常な認可しか行われていないケースでは問題が無いかもしれませんが、ひとたび何らかの理由によって第三者に認可が渡ってしまったとき、どうすればその不正な認可を取り消して正常な利用に戻せるか、を考えるとその理由が想像できるでしょう。
認可サーバーとリソースサーバーの関係
マイクロサービス化は様々な場所で議論されているため、今更この記事でその価値について触れることはしませんが、そのアーキテクチャにおける関心の分離がユーザーに思わぬ混乱を生むケースについて説明をします。 これまでの経験的に、サードバーティ向けのAPIを公開しようとするサービスでは、認可サーバーとリソースサーバーが業務的に分離されて作成されることが多いですが、この二つを完全に独立したものとみなしてシステムを構築してしまうと、ユーザーが認可を行った際に不快な経験をする場合があります。
意味のないトークン
例えば、ユーザーの認可は正常に完了し、トークンは発行できたのだが、いざそのトークンを使ってAPIをリクエストしてみると、scope で指定した業務を何も実現することができず、リソースサーバーからただエラーが返るだけ、というような事象です。
認可サーバーとリソースサーバーのそれぞれの責務からすると、こうしたことが発生するのは当然のことのように思えますし、この設計が誤っている、というわけではありません。 しかし一方で、APIクライアント側としては「こんなトークンをもらっても何もできないんだから、そもそも認可のときに止めてユーザーに警告して欲しい」という感想を持ちます。
あまり問題にならないシーン
ただこれは、もしこのAPIクライアントが、リソースサーバーの機能を「拡張する」ような使われ方をするもので、ユーザーにとってそれが自然に見える場合はあまり問題にはなりません。 例えば、Salesforce のアプリストアで公開されている、Salesforce APIを利用したサードパーティの多くのアプリは、ほとんどのユーザーにとって Salesforce の機能を拡張するものであって、Salesforce の一部である、と認識されます。 その場合、ユーザーがそのアプリケーションに対して Salesforce の何からの権限を認可した結果、そのユーザーの Salesforce 内での契約ステータスの影響(例えば Salesforce の特定プランに入っていないため利用不可、等)で取得されたトークンに価値がなかった時、アプリケーション側でそのエラー内容に応じたユーザーコミュニケーション(先の例であれば「あなたはproプラン未満なので使えません」というメッセージを出す)を取ってもそれほど不自然ではなく、ユーザーは何をすべきかを理解することができます。
より問題になるシーン
一方、「相乗効果型」での利用シーンを見てみましょう。 例えば、何らかの新しいスマホアプリをインストールして、利用を開始しようとしたユーザーが初期登録において「Googleで登録」しました。 その際、アプリケーションは発行された Google のトークンを使って(認証だけでなく、それ以外のリソース取得のための)APIを実行した結果、ユーザーの Google 側のステータスの問題によってそのAPIがエラーになった場合、アプリケーションはユーザーに「Google アカウントが〇〇なので直してください」という説明を自アプリ内で表示する必要があります。
一見 Salesforce の時とそれほど違いがないようにも思えるかもしれませんが、自分自身の Google アカウントに対する問題が表示されるサイトが、Google の本サイトなのか、それともそれとはまったく違う所なのか、という差異は、理解のしやすさに大きく影響を与えるのではないかと思います。 なぜならユーザーは Google とは関係のない「そのアプリケーション」を使っているつもりであり、それがなぜ自分の Google アカウントの状態についてどうこう言ってくるのか、不自然だと感じるためです。
もちろん、APIのエンドポイントが複数ある場合、認可サーバーもリソースサーバーも、発行されたトークンによってクライアントアプリケーションが利用しようとするエンドポイントが何かを事前に知ることができません。 そのため、ユーザーの認可時に認可サーバーで完璧に「その認可意味ないですよ」という警告を出すのは困難で、アプリケーション側でもうまくコントロールする必要は当然あります。
認可サーバーとリソースサーバーの断絶
この事象は、API提供元企業に複数のサービスがあり、それと独立した統合ID基盤が存在しているような構成のシステムにおいて、認可の役目をID基盤に持たせようとした場合に起こりがちです。 せっかく OIDC 準拠の基盤を作ったのだから、他の scope のトークン払い出しもそこでやろう、と考えるのは自然ですし、そのID基盤は各サービスに依存しないことを第一に設計されているはずで、リソースサーバー側のステータスを考慮してくれ、というのは無茶な要望だからです。
ユーザーが感じること
ただそうはいっても、ユーザーがどのような体験をするか、ということを考えると、完全に知らんぷりして素通りさせる、というのもまた避けるべきことなのではないかと思います。 特に、通常 OAuth 系の認可フローでは「これこれの権限を渡しますよ?OK?」と書かれているページで承諾を行うものであり、ユーザーはその承諾を行える=その権限を今自分は持っていて与えることができる、と認識するのが自然なため、それを行った「後」に「やっぱりその権限ないです」と言われても、理解にはワンクッションが必要なはずです。
例えば、子供に留守番をさせて海外旅行中、自宅マンションの管理会社から電話がかかってきて「部屋から何時間もドンドン音が鳴ってうるさいそうです」と言われました。 電話で子供に連絡を取ろうとしますが出ません。心配になったのでその担当者に「近くに住んでいる兄をそちらに向かわせたので、部屋の鍵をその人に渡してください」と伝えます。 管理会社は「本当にその人が部屋に入ってもいいんですね?」と改めて聞いてきたので、もちろん「はい」と答えました。 その1時間後、マンションに向かった兄から電話があって「管理会社から借りた鍵は1Fのオートロックしか突破できなくて、お前の部屋には入れないって管理人さんが言ってる」と言われました。 「え?じゃあなんであの時に『本当にいいのか』なんて聞くんだよ!入れないならその時言えよ!」と思うのが自然ではないでしょうか。
もちろんこれはあくまで一つの例であり、これをもってすべての認可サーバーが、scope で指定された全エンドポイントの利用可否をチェックすべし、というわけではありません。 ただ、なるべくこういった部分が根本でコントロールができれば、APIを利用するアプリケーションを作る多くの開発者が横並びに同じことをやらずに済み、そのAPI圏が全体としてエコになるのではないかと考えています。
最後に
長々と書いてきましたが、「相乗効果型」のような、異なる思想を持った2つのサービスがユーザーの認可を通して連携しあう時、どうしても混乱したUXが生まれ得る溝があります。 そうした溝を極力避け、なるべく統合されたユーザー体験となるように、認可サーバーとクライアントアプリケーションの認可機能双方がなだらかに無色で普遍的に繋がりつつ、ユーザーとコミュニケーションする部分ではしっかりと責務を果たしあえるような設計を、これからも考え、提案していきたいと思います。
また時間があれば、冒頭で述べたような数多の外部APIに、マネーフォワード社がどのようにして素早く対応し、そして生産性高く運用しているのか、という裏話もしていければと思っています。
そして最後に、これを読んで 「マネーフォワードって他では触れないものを一番に使える面白そうな環境だな!」と思われた方や、 「自分ならその立場を利用してもっと高い価値を出せる!」といった心意気を持った方、 「無数のAPIを使ってユーザーの完璧な代理人となるとはこういうことだ!」という哲学を持たれている方は是非、 一度オンラインでもお話ができれば嬉しいです。 以下からどしどしご連絡ください!
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【サイトのご案内】 ■マネーフォワード採用サイト ■Wantedly ■京都開発拠点
【プロダクトのご紹介】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android
■ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』
■だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』