APIキー取得処理の変更(1)
前回は、起動時にユーザーシークレットからAPIキーを読み込む形にしていましたが、今回はこの処理を変更して、プラットフォームごとにAPIキーの取得元を切り替える実装パターンにしてみます。
Web環境ではユーザーシークレットを読み込み、モバイルやWindowsアプリ環境では、SecureStorage(APIキーはソースコード埋め込み)を利用する形とします。
作成するサービスと実装クラスの構成
今回作成するインターフェイスと実装クラスは、次のとおりです。
- IApiKeyStorageインターフェイス:キーの取得場所を抽象化する。取得メソッドのみを定義
- ApiKeyServiceクラス:IApiKeyStorageインターフェイスを使ってキーを取得する
-
ApiKeyStorage実装クラス
- Web: IConfigurationを利用してユーザーシークレットからキーを取得
- Android/Windowsアプリ:SecureStorageからキーを取得。キーがなければソースコード内の定数を(保存後)参照
ApiKeyServiceクラスは、IApiKeyStorageの実装クラスを利用、つまり依存する構成になっています。IApiKeyStorageインターフェイスは、APIキーを取得する処理を抽象化して、実装オブジェクトでプラットフォームごとの分岐を吸収するようにします。実装オブジェクトは、それぞれのプロジェクトで注入される形とします。
インターフェイスの定義
まずは、ImageGenerator.SharedプロジェクトのServicesフォルダに、IApiKeyStorageインターフェイスを新規作成します。ここは、単にキーを取得する非同期メソッドの宣言だけです。
public interface IApiKeyStorage
{
Task<string> GetKeyAsync(); // 非同期で取得
}
共有する実装クラスの作成
次に、各プロジェクトで共有する実装クラスとなるApiKeyServiceクラスを、ImageGenerator.SharedプロジェクトのServicesフォルダに新規作成します。
public class ApiKeyService(IApiKeyStorage storage)
{
public async Task<string> GetApiKeyAsync()
{
// 具体的な保存場所は IApiKeyStorageオブジェクトに任せる
var key = await storage.GetKeyAsync();
if (string.IsNullOrEmpty(key))
{
throw new Exception("not key");
}
return key;
}
}
ApiKeyServiceクラスは、IApiKeyStorageインターフェイスを実装したクラスのメソッドを呼び出すだけです。このようにすると、同じメソッドの呼び出しで、各プロジェクトに応じた処理を切り替えることができます。
Web環境の実装クラスの作成
IApiKeyStorageインターフェイスを実装したWebKeyStorageクラスを、ImageGenerator.WebプロジェクトのServicesフォルダに新規作成します。
public class WebKeyStorage(IConfiguration configuration) : IApiKeyStorage
{
public Task<string> GetKeyAsync()
{
// WebではUserSecrets から取得
var key = configuration["OpenAI:ApiKey"];
return Task.FromResult(key ?? string.Empty);
}
}
これは、ユーザーシークレットから取得する処理です。前回では、ユーザーシークレットのJSON文字列をオブジェクトにマッピングしましたが、今回は、MAUIで用意されているIConfigurationオブジェクトのメソッドを使って、直接、読み出しています。IConfigurationオブジェクトは、フレームワークで注入されるため、明示的に作成する必要はありません。
なお、ImageGenerator.Webプロジェクト用に、ユーザーシークレットを作成する必要があります。ソリューションエクスプローラーでImageGenerator.Webプロジェクトを右クリックし、[ユーザーシークレットの管理]を選択し、前回同様のAPIキーを設定しておきます。
{
"OpenAI": {
"ApiKey": "sk-proj-...."
}
}
モバイル/Windowsアプリ環境の実装クラスの作成
同様に、IApiKeyStorageインターフェイスを実装したMauiKeyStorageクラスを、ImageGeneratorプロジェクトのServicesフォルダに新規作成します。
public class MauiKeyStorage() : IApiKeyStorage
{
private const string ApiKey = "sk-proj-...";
private const string KeyName = "openai_api_key";
public async Task<string> GetKeyAsync()
{
// MAUI固有のSecureStorageを使用
var key = await SecureStorage.GetAsync(KeyName);
if (!string.IsNullOrEmpty(key))
{
return key;
}
await SecureStorage.SetAsync(KeyName, ApiKey);
return ApiKey;
}
}
ここでは、APIキーをソースに埋め込んでいます。これは、あくまで開発用であり、Gitなどのバージョン管理システムにコミットする際は、キー文字列を削除してください。
また、実際の本番環境では、外部にキーが漏れないように、ソース埋め込みではない方法を採用すべきです。本番環境に対応した処理は、次回以降で説明する予定です。
SecureStorageとは
今回利用しているSecureStorageとは、プラットフォーム固有のセキュアな(暗号化された)ストレージ機構をラップしたMAUIのライブラリです。.NET MAUI アプリからは、統一された ISecureStorageインターフェースとして見えますが、内部では各OSのネイティブAPI(Keychain、Keystoreなど)を呼び出しています。開発者が暗号化アルゴリズムや鍵管理を実装しなくても、OSが提供するセキュリティ機能を利用できます。
今回作成したMauiKeyStorageのGetKeyAsyncメソッドでは、SecureStorageで保存されたAPIキーを参照(SecureStorage.GetAsyncメソッド)しています。保存されていない場合は、ソースコード内の定数を保存(SecureStorage.SetAsyncメソッド)して、その値を返します。
SecureStorageのデータ構造は、文字列のみで、いわゆるKey-Value形式です。バイナリなどを保存したい場合は、Base64 エンコードなどで文字列に変換する必要があります。
