Plaidは、統一されたAPIを介して財務データへのアクセスを提供することで、fintech分野のイノベーターを支援します。 エンドユーザーが銀行データをfintechアプリに接続できるようにするために、Plaidは、サポートされているすべての銀行の資格情報の検証、多要素認証、エラー処理を処理するドロップインモジュールであるLinkを開発しました。
Android開発者は以前、WebViewsでリンクUrlを開く必要がありました。 Linkを使用したいすべてのアプリには多くの開発者の努力が必要であり、標準化がなければ、いくつかの実装にバグがあるはずでした。 開発者の経験を向上させるために、私たちは最近、数行のコードでWebViewを任意のアプリに簡単に統合できるようにするSDKをリリースしました。
さらに、Webビューは、リンクフローに機密情報を入力するエンドユーザーにとって完全に安全ではありません。 SDKを可能な限り安全にするために、Android WebViewsの代わりにChromeカスタムタブを使用して実装しました。 この記事では、なぜその決定を下したのか、そして途中で遭遇した技術的な問題をどのように克服したのかを説明します。
オプション
をSDKとして評価すると、コードは他の開発者のアプリケーション内で実行され、アプリケーションプロセス内で実行されます。 WebviewでLink webアプリを実行するには、Javascriptを有効にする必要があります。 これにより、ユーザー名とパスワードを傍受しようとするコールバックの登録など、他のアプリが悪意のあるコードを実行するドアが開きます。 さらに、悪意のあるアプリは、フィッシング詐欺の試みでリンクフローを模倣する別のwebページを開く可能性があります。
開発者のための統合が容易で、エンドユーザーのための直感的で、安全なソリューションを探しているとき、我々はいくつかのオプションを評価しました:
-
ネイティブリンクフローの構築:ネイティブリンクフローは別のアプリのプロセスでも実行されるため、精通した開発者はリフレクションを使用して入力EditTextsを検索し、WebviewのJavascriptと同様の方法でコールバックを登録することができます。
-
別の認証アプリの構築: しかし、多くのユーザーはPlayストアから追加のアプリをダウンロードしたくないでしょう。 これは、アプリのダウンロードを拒否するユーザーのためのフォールバックソリューションが必要であることを意味します。
-
別のブラウザウィンドウでリンクを開く:これはサンドボックス化された安全なソリューションです。 ほとんどすべてのユーザーがブラウザをインストールしていますが、アプリからブラウザへのコンテキストの切り替えは、特にローエンドのデバイスでは、顕著な遅延を導入します。
-
Chromeカスタムタブの使用: これは、上記の欠点のどれも持っていなかったので、私たちが選んだ解決策です。
私たちの選択:Chromeカスタムタブ
Chromeカスタムタブ(CCT)は、アプリが軽量プロセスでウェブサイトを開くことを可能にするために、Androidフレームワークと統合Chromeブラ CCTはブラウザよりも速く開き、ウォームアップコールを介してプリロードされた場合、WebViewよりも高速になる可能性があります。 Javascriptはまだ実行されていますが、独自のプロセスにあり、アプリが悪意のあるコードを実行するのを防ぎます。 さらに、CCT UIには、ロードされているページのURLを示すアクションバーと、安全なページ用のSSL検証ロックアイコンが用意されています。 これにより、正しいページが表示されていることをユーザーに安心させます。
すべてのユーザーがChromeをインストールしているわけではありませんが、大多数はインストールしています。 そうでない人のために、私たちは上記のブラウザのフォールバック方法(オプション3)を使用しています。 前に述べたように、ブラウザのフォールバックは、待ち時間のために理想的なユーザーエクスペリエンスではありませんが、チェック柄が必要とする高レベ
私たちはCCTソリューションを開発したとき、いくつかの合併症に遭遇し、対処しました:
-
イベントデータの取得
-
最終結果の取得
-
CCTアクティビティ&プロセスの制御
イベントデータの取得
ユーザーがリンク内の画面間を移動すると、リダイレクトが発生し、URLパラメータでデータが開発者に提供されます。
このリダイレクト情報は、ユーザーの行動を理解するのに役立つため、開発者にとって貴重です。 CCTが独自のプロセスで実行され、リダイレクトコールバックを提供していない場合、この情報は通常アクセスできなくなり、カスタムWebView実装よりも開発者
CCTからこの情報を提供するために、代わりにRedisデータストア内のサーバー上のリダイレクトイベントを記録しました。 SDKがリンクを開くと、サーバーへのブートストラップ呼び出しが行われ、ユーザーごとに選択されたチャネルIDと秘密鍵が提供されます。 次に、SDKはアプリスコープのworkerオブジェクトを作成し、RX間隔ストリームを使用してサーバーをポーリングします。 各ポーリングコールでは、最新のイベントを取得するために、チャネルID、秘密鍵、および最後のイベントのUUID(またはnull)をサーバーに提供します。
観察可能。interval(interval,TimeUnit.秒)。購読(スケジューラ.計算()).observeOn(AndroidSchedulers.メインスレッド()).flatMapSingle(makeNetworkCall()).購読する({// メッセージの処理},{// エラーの処理})
Androidフレームワークは、チェック柄のSDKを使用してアプリを含む、任意のプロセスを強制終了することができます。 ワーカーオブジェクトはアプリケーションに関連付けられているため、ワーカーは停止されます。 ユーザーがフローを続行した場合、SDKはチャネルへの最終的な呼び出しを行い、残りのイベントを取得します(成功または失敗のいずれか)。 イベントは、ユーザーがフローを中止し、強制的にアプリを強制終了した場合にのみ失われます。
最終結果の取得
イベントデータをクライアントに渡すのと同様に、LinkはURLを使用してユーザーがフローを完了したことを通知します。 関連するURLには、公開鍵やエラーコードなどの必要なデータが含まれています。
CCTではURLにアクセスできないため、最終結果を同じチャネルIDでRedisに保存しました。 これは、リンクが終了したときにポーリングワーカーが知っていることを意味しますが、ワーカーがまだ生きているという保証はありません。 それが生きていたとしても、ユーザーは結果が配信されるために次のポーリング呼び出しまで待たなければならないかもしれません。
結果がタイムリーに配信されるように、ディープリンクを使用してSDKを再度開きます。 このアプリIDは、クライアントシークレットに関連付けられ、開発者ダッシュボードでホワイトリストに登録されている必要があります。 これに加えて、デバイス上の1つのアプリだけが同じアプリIDを持つことができるという事実は、他のアプリがリダイレクトを傍受しないことを 次に、リンクwebアプリは、リンクフローの最後に起動されるインテントURIを構築します。
インテント://リダイレクト/#Intent;scheme=格子縞;package=package packageName;end;
SDKにはURIを処理するためのインテントフィルタが含まれているため、アプリケーションは再び開き、すぐにチャネルに直接呼び出しを行います。
<intent-filter><アクションandroid:name="android.意図。演技はじめ!.ビュー" /><カテゴリandroid:name="android.意図。カテゴリ。デフォルト" /><カテゴリandroid:name="android.意図。カテゴリ。閲覧可能" /><データandroid:host="redirect"android:scheme="チェック柄" /></intent-filter>
CCTを制御する
CCTは、アプリのインターフェイスを欠いています:
-
ユーザーがアクティビティを終了したときにリッスンします
-
ユーザーが”ブラウザで開く”オプションをクリックしたときに検出します
-
強制的に閉じる
私たちはこれらの欠点をすべて回避することに成功しました。
まず、ユーザーがアクティビティを閉じたときに待機するために、startActivityForResultを使用してCCTを開き、要求コードを渡します。 ユーザーがシステムの戻るボタンの左上隅にあるXを使用してCCTを閉じると、提供した要求コードとアクティビティの結果コードでonActivityResultコールバックがトリガーされ結果_CANCELED。 データインテントには情報は含まれていませんが、残りのイベントを取得するためにチャネルへの最終呼び出しを行うことができます。 次に、それらをクライアントアプリに渡し、LinkCancellationオブジェクトを返し、リンクがユーザーによって意図的に閉じられたことを通知します。
次に、ユーザーが”ブラウザで開く”をクリックしたときに検出する潜在的な懸念は、ユーザーが別のアプリケーション、すなわちブラウザでフロー全体を通過できる ポーリングとインテントシステムは同じ方法で動作し続け、必要なデータを取得できるため、これは私たちにとって問題ではありません。
最後に、ユーザーがフローを正常に完了し、結果のインテントが起動されると、CCTプロセスは開いたままになり、ユーザーのタスクリストに表示されます。 このファントムプロセスは無駄になるだけでなく、「最近のタスク」システムボタンを押したときにユーザーが混乱する可能性もあります。 したがって、フローが完了したときにCCTを強制的に閉じる方法が必要です。 これを行うために、OpenId AppAuth For Androidライブラリに示されているパターンを使用しました。OpenID AppAuth for Androidライブラリに表示されているパターンを使用しました。 リンクを開くアクティビティで結果を処理する代わりに、インテントフィルタを別のアクティビティに配置します。 この第二のアクティビティは、webアプリからのすべてのリダイレクトを処理します。: 成功した完了、フローを閉じるエラー、oAuthまたはアプリからアプリへのリダイレクト、および一般的なシステムエラー。 その後、アクティビティは、インテントとインテントを使用して、データをopeningアクティビティに戻します。FLAG_ACTIVITY_SINGLE_TOPおよびIntent。FLAG_ACTIVITY_CLEAR_TOPフラグ。 一緒に使用されて、彼らはCCTを含む開口部の活動の上にスタック上のすべてをクリアします。
val intent=Intent(activity,LinkActivity::class.java)when(state){はRedirectStateです。ChromeCustomTabsComplete-> {意図。putextra(LINK_CHROME_CUSTOM_TABS_COMPLETE_REDIRECT,true)インテント。putExtra(LINK_RESULT_CODE, state.resultCode)intent.putExtra(LINK_RESULT, state.result)}is RedirectState.UserInitiatedChromeCustomTabsViewClose -> {intent.putExtra(LINK_CHROME_CUSTOM_TABS_USER_CLOSE_REDIRECT, true)}is RedirectState.OAuth -> {intent.putExtra(LINK_OAUTH_REDIRECT, true)intent.putExtra(LINK_OAUTH_STATE_ID, state.oauthStateId)}is RedirectState.RedirectError ->intent.PUTEXTRA(LINK_REDIRECT_ERROR,true)}意図。flags=Intent.FLAG_ACTIVITY_SINGLE_TOPまたはIntent。FLAG_ACTIVITY_CLEAR_TOPインテントを返す
CCTに関する最終的な考え
WebViewsやネイティブフローでは利用できない安全でサンドボックス化されたエクスペリエンスを提供するために、CCTは実行可能な 開発者のためにと統合することは容易で、軽量の性質および速度のおかげでブラウザウィンドウを開けるよりよいユーザーの経験を提供する。
CCTのサンドボックス化された性質と限られたAPIは、その欠点がないわけではありません。 URLリダイレクトを聞いて、最終的な結果を取得し、CCTプロセスを制御するには、すべての創造的なソリューションを考え出すために私たちを必要とし これらのソリューションは、Androidの組み込み機能、特にintentフレームワークの理解に依存していました。
開発者と消費者にとっての利点は、必要な努力に値するものであり、高速で安全な統合のために他のアプリやSdkでCCTを使用することをお勧めします。 さらに、ここで提供されているヒントを使用して、ユーザー(および開発者)の経験を向上させることができます。
何千人もの開発者と何百万人もの消費者が使用するユニークな問題を解決することに興味がある場合は、採用情報ページをご覧ください。