🆔

FlutterでのOpenID ConnectクライアントSDK開発記録

2024/12/11に公開

Advent Calendar

この記事は GENDA Advent Calendar 2024 11日目の記事です。

https://qiita.com/advent-calendar/2024/genda

はじめに

GENDAが提供する各アプリでは、GENDA IDでログインできるように対応が進んでいます。
GENDA IDはOpenID Connect (OIDC) を採用しており、そのID Provider (以下 IdP)として機能しています。
FlutterアプリでGENDA IDを利用できるよう、OIDCのRelying Party (クライアント) として動作するSDKを開発しました。
本記事では、SDK開発を振り返り、その概要や実装時に注意したポイント、開発の過程で直面した課題について共有します。

作成したもの

FlutterアプリでGENDA IDによるログインを簡単に実装するためのOIDCクライアントSDKです。このSDKを利用することで、開発者がOIDCの詳細な知識を持たなくても、ログイン機能を実装できるように設計されています。
注意: 本SDKは社内アプリ向けに開発されたため、公開リポジトリはありません。

筆者について

iOSアプリ開発歴が長いエンジニアです。Flutter歴は1年ほど。

開発の背景とモチベーション

GENDA IDを用いたログインを導入するサービスが増加しており、今後もその需要は拡大すると予想されています。一方で、OIDCの理解には一定の学習コストが伴います。そのため、サービス開発者がOIDCの詳細を理解せずとも導入できるSDKを提供することが重要だと考えました。

OpenID Connect とは?

OpenID Connect (以下 OIDC) は、OAuth 2.0をベースにした認証プロトコルです。本SDKは、このプロトコルに基づいてRelying Party(クライアント)として動作し、以下の機能を提供します。

  • FlutterアプリからGENDA IDの新規登録/ログイン
  • ログアウト/退会
  • プロフィール情報の読み書き

以下に、このSDKが提供するログイン/新規登録機能の流れ (Authorization Code Flow) を簡単に示します。

SDKの特徴と設計のポイント

普段アプリ開発でSDKを利用することはあっても、SDKをフルスクラッチで開発する経験はあまりありません。
アプリ開発者がユーザーになることを意識し、以下のことについて気を配っていました。

開発者体験の向上

導入のしやすさを重視し、以下のドキュメントやサポートを整備しました。

APIリファレンスをdart docで自動生成

SDK 利用者が利用できるクラス、メソッド等は、コード上のドキュメントコメントを元に dart doc で生成されたドキュメントで参照できるようにしました。

発生しうるExceptionを明記

私はSwiftでのiOSアプリ開発が長いため、Dartでの開発において、メソッドが例外を投げるのかがよくわからないことが不安でした。
この経験から、メソッドのドキュメントコメントに返しうるExceptionクラスを明記するようにしました。

iOS/Android両プラットフォームで動作するサンプルアプリを同梱

SDKを使ってログイン機能を実装したFlutterプロジェクトをリポジトリに配置しています。
SDK利用者は、最初にサンプルアプリを手元で動かしてSDKの使い勝手を確かめることができます。

アプリ開発への制約を最小限に

以下の方針でライブラリを選定しました。

アプリが利用するライブラリとのコンフリクトを最小限に抑える

SDK の開発の都合で気軽にサードパーティーライブラリを導入してしまうと、SDK 導入時にアプリ側のライブラリやその他の依存と競合してしまうことがあります。
その中でも、 OIDC の iOS/Android の実装差分を吸収するために flutter_appauth と、ID Token の署名検証処理のために jose を採用しました。
また、導入したサードパーティライブラリに強く依存しないよう、必要な機能のインターフェースだけ露出させるようなラッパークラスを用意しました。ライブラリを import する箇所を最小限に抑え、仮にそのライブラリに不具合が生じても、別のライブラリに置き換えたり、自前で書き直すことをできるようにしています。

開発中の課題とその解決策

慣れない言語やプロトコルを扱うSDK開発ではたくさんの課題が出てきました。そのうちのいくつかを解説します。

知識の習得

Flutter, Dart, OIDCのいずれも経験が浅かったため、日本語の書籍や公式ドキュメントを活用して基礎を固めました。
ざっくり知識の基盤ができることで、公式ドキュメントがスラスラ理解できるようになりました。

一人開発の不安

本SDKの開発担当者は私一人だけでした。一人開発の不安を払拭するため、GitHub Actions 上でCI環境を整え、テストコードを仕様書のように充実させました。
まっさらなプロジェクトであることと、OIDCの仕様が決まっていることから、テストの導入はスムーズに行えました。

IdP環境の構築

IdP と SDK を並行して開発していたため、SDK 開発中 IdP との結合テストができませんでした。そのため、開発中はKeycloakというIAM (Identity and Access Management)をIdPとしてローカル環境で動かしました。
モバイルアプリ開発者としてあまり利用することがなかったDockerとも格闘した結果仲良くなれました。

ID Token の検証

開発初期、JWT形式のID Tokenの署名検証処理を自前で書こうとしていました。
慣れているSwiftで実装してみたものの、複数ある署名アルゴリズムのうちRS256だけの対応でもかなり複雑になってしまいました。
ひとまず動作していますが、詳細に理解していないところもあり、メンテナンスが困難と判断しjoseの導入に至りました。
cf. OpenID Connect の JWT の署名を自力で検証してみると見えてきた公開鍵暗号の実装の話 #認証 - Qiita

プライベートリポジトリでホストされるライブラリの扱い

本SDKは社内のプロジェクトでの利用を想定しており、GitHubのプライベートリポジトリでホストしています。
弊社では複数Organizationを管理しており、SDKを利用するアプリのリポジトリから見て、OrganizationをまたがるプライベートリポトリをGitHub Actions上で取得するのに苦労しました。
この実現方法についてはまた別途記事を書きたいと思います。

おわりに

本SDKの開発を通じて、OIDCやFlutter開発の理解を深め、SDK開発の流れを知ることができました。
ハマりどころはFlutter以外のプラットフォームでも共通することは多いはずで、この経験を元にさらなるプラットフォームでの展開を進めていこうと考えています。
今回の記事で、SDK, ライブラリを開発される方の一助になれると嬉しいです。

参考にした情報や書籍

GENDA

Discussion