AndroidでAppAuthを使ってOAuth 2.0認証を行う

これはアソビュー! Advent Calendar 2022の24日目です。 いよいよクリスマスイブまで来ました。

アソビューでバックエンドエンジニアをしている上中です。

はじめに

アソビュー!では2022年の夏に、待望のスマホアプリをリリースしました。 tech.asoview.co.jp

ECサイトアソビュー!のユーザ認証はOAuth 2.0の認可コードフローで行っており、アプリではiOS/Android共にAppAuthというライブラリを利用してOAuth認証を実現しています。
AppAuthは、Android/iOSのネイティブアプリにおけるOAuth認証の認証フロー実装をよしなに抽象化してくれるライブラリです。 そこで今回の記事では、 AndroidでAppAuthを用いてアクセストークンを取得するまでの実装について簡単にご説明します。
なお、本稿ではOAuth 2.0の仕様に関する詳しい説明は省きます。

ネイティブアプリでのOAuth認証

OAuth 2.0はRFC6749で標準化が進められていますが、ネイティブアプリでのOAuth認証はRFC8252で現状のベストプラクティスが公開されています。
AppAuthはこのRFC8252に準拠しており、自前で実装するよりも楽に、かつベストプラクティスに準拠した形でネイティブアプリのOAuth認証を実現することができます。

認証の流れ

今回ネイティブアプリでOAuth認証を行うフローはざっくり以下のようになります。

認可コードを取得するまでのクライアントとサーバとのやりとりは、実際にはブラウザ(アプリ内ブラウザ)にお任せすることになります。
認可コードが取得できた時点でネイティブアプリに戻ってくるため、その後アクセストークンを取得する流れになります。

実装

環境

  • Android: targetSdk 33
  • Kotlin: 1.7.20
  • AppAuth: 0.11.1

Gradle

まずはGradleに依存関係を追加します。

implementation 'net.openid:appauth:0.11.1'

アプリ内ブラウザを起動するまで

次はアプリ内ブラウザを起動して認可プロセスを開始するところまでの実装です。
なおRFC8252の8.12では、ネイティブアプリに組み込んだWebViewで認証エンドポイントとやりとりを行うことはMUST NOTとなっており、アプリ内ブラウザで行う必要があります。AppAuthを使えばその辺りも実装者が意識する必要はありません。

gist.github.com

まずはAuthorizationServiceConfigurationで認証を行う上で必要なエンドポイント等の情報を設定します。 上記の例では認可エンドポイントとトークンエンドポイントを設定しています。
あるいは以下のようにディスカバリエンドポイントを用いて設定をすることもできます。

gist.github.com

上記の実装例ではloginButtonを用意しています。Listenerの中で、AuthorizationRequestに認証に必要なパラメータをセットした上でIntentを作成し、アプリ内ブラウザを起動して認証を開始します。
AuthStateでは、認証情報を保存しておくことができます。OAuth認証で必要となるエンドポイントや認可コード、アクセストークンその他のパラメータなどを、OAuth 2.0の仕様に沿った形で保存し、必要な時に情報を取り出して利用することができます。

認可コード取得〜アクセストークン取得まで

ブラウザ側で認可決定まで行ったのち、結果の認可コードをregisterForActivityResultにてIntentで受け取ります。 ただしその前に、リダイレクトURLによってディープリンクでアクティビティを起動できるようにしておく必要があります。 今回の例では、リダイレクトURLに指定したasoview://www.example.com/oauth2redirect/でアクティビティが起動できるように、マニフェストファイルにディープリンクの設定を入れます。 (本当はHTTPSリダイレクトURIでアプリリンクにしたかったのですが、なぜかリダイレクトされたりされなかったりと挙動が安定しなかったため、カスタムスキームのディープリンクとしています)

gist.github.com

registerForActivityResultの実装は以下のようにします。

gist.github.com

認可決定エンドポイントの結果として、Intentで認可コードが返されます。 AuthState#updateで状態を更新することで、認可コードとその他のレスポンス結果も保存されます。 そしてperformTokenRequestでアクセストークンエンドポイントにアクセスし、tokenResponseCallbackでその結果を受け取ります。

gist.github.com

最後にAuthState#updateで状態を更新してアクセストークン取得完了です!

最後に

OAuth認証は冒頭でも述べたとおりプロセスが標準化やベストプラクティスが公開されているため、AppAuthなどのRFCに準拠したライブラリを利用して実装を行うことで、独自に実装して脆弱性を埋め込むリスクを避けることができます。 また、今回実装してみて非常に楽だったので、機会があれば使ってみると良いかと思います。

顧客を身近に感じる開発を通じて「生きるに、遊びを。」を実現する仲間を絶賛募集中です! お気軽にお話しましょう!

www.asoview.com