2023/08/11

.NET C#でFirestoreの認証とデータ取得

最近全然記事を書いていなかったので久しぶりの投稿。
ここ1週間はタイトルの件でずーーっと四苦八苦してたので、備忘録代わりに記事を残しておきます。
今後誰かしらのお役に立てば幸いです。。

Firestoreとは?

まずFirestoreって何なのか、と言うところから一応…
ChatGPTに説明してもらいました↓

FirestoreはGoogleが提供するクラウドベースのNoSQLデータベースで、リアルタイム同期とオフラインアクセスをサポートしています。これによって、開発者はスケーラブルなアプリケーションを素早く構築することができます。

Firestoreはドキュメントベースのストレージを採用しており、データはドキュメントとコレクションの階層構造で整理されています。ドキュメントはフィールドと値の組み合わせで構成され、様々なデータ型をサポートしています。コレクションはドキュメントのグループで、クエリの操作が容易になります。

セキュリティと柔軟性を提供するために、Firestoreは強力な認証とルール設定のオプションを提供しています。開発者はこれらのルールを使用して、特定のドキュメントへのアクセスを制御することができます。

さらに、Firestoreは複数のプラットフォームで利用可能であり、iOS、Android、Webなどでの開発が容易になっています。リアルタイムの更新が必要なアプリケーションに特に適しており、ゲーム、チャットアプリ、コラボレーションツールなどでよく使用されています。

C#でFirestoreを操作する際には、Googleが提供する公式ライブラリを利用することで、データの取得、更新、削除などの操作が非常に容易に行えます。この技術の組み合わせは、リアルタイム機能が必要な現代の多くのアプリケーション開発において重要な選択肢となっています。

Google Cloudの提供するサービスの1つ?っぽい立ち位置のようです。
NoSQLという、従来のリレーショナルDBとは別物の構造を持っています。表形式ではないので、変更等に柔軟に対応できるメリットがありますが、その代わり集計などのクエリ文は使えなかったんじゃないかなと思います。
他にもなんか色々できるみたいなんですが、まだ触り始めたばかりなのでこれから触っていきたいです。


C#でFirestoreを触る

本題です。
C#からFirestoreのデータを取ってみようというところですね。

ChatGPTはC#でFirestore使える!って言ってますが、Firebase自体がC#そのものには対応しておらず、特に認証あたりが公式ライブラリだけだと難しかったです。
Google Cloudの公式ドキュメントでも環境変数でサービスアカウントキー(秘密鍵)を指定するとかの方法しか説明がなかったので、この記事ではその先の本番環境で運用する際の認証方法などを説明します。

前提として、Google Cloudへの登録やFirestoreなどある程度のプロジェクト作成は終わっているものとします。(この辺りなら多分調べたら出てくると思います)
今回は誰でもデータの読み取りが行えるようにしたいので、Firebase Authenticationの匿名認証を使用します。
もちろんメールアドレスとパスワードによる方式や、Googleアカウントなどを使用したSSO認証もFirebase Authenticationの機能を使用して実装できますので、その場合は適宜置き換えてください。

1. Firebaseの匿名認証を有効化

Firebaseコンソール左のサイドバーからAuthenticationを選択し、sing-in methodタブの「匿名」を選択、有効にしておきます。

2. FirebaseでWebアプリを作成

Firebaseのプロジェクトトップにアクセスすると、「+アプリを追加」というボタンがあると思います。そこからWebアプリを作成してください。
作成時にApiKeyとAuthDomainなるものがコード中に表示されます。後のコードで使用するので、どこかに控えておきます。

3. C#コード

今回の実行環境は下記の通りです。
・Visual Studio 2022
・.NET 7

また、以下のNuGetパッケージをインストールしておきます。
・Google.Apis.Auth
・Google.Cloud.Firestore
・FirebaseAuthentication.net

下記はFirebaseで匿名認証を行い、FirestoreDbのインスタンスを返すメソッドです。(この辺りのコードが中々出てこず、結局いろんな情報をかき集めて組み合わせてなんとかなった所です。。)
_apiKeyと_authDomainフィールドには、上記 2. で作成したWebアプリのApiKeyとAuthDomainをそれぞれ入力します。
private const string _apiKey = "your api key";
private const string _authDomain = "your auth domain";

public static async Task<Firestoredb> Auth_Anonymous()
{
    // Firebase認証クライアントを生成
    var firebaseClient = new FirebaseAuthClient(new FirebaseAuthConfig
    {
        ApiKey = _apiKey,
        AuthDomain = _authDomain,
    });
        
    // 匿名サインイン
    var firebaseCredential = await firebaseClient.SignInAnonymouslyAsync();

    // 認証情報の取得
    var token = firebaseCredential.User.Credential.IdToken;
    var googleCredential = GoogleCredential.FromAccessToken(token);

    // Firestoreクライアントの構築
    var firestoreClient = new FirestoreClientBuilder { Credential = googleCredential }.Build();

    // FirestoreDBのインスタンス作成
    return await FirestoreDb.CreateAsync(_projectId, firestoreClient);
}

FirestoreDbの参照を取得できれば、あとはコレクションやドキュメントの名称を指定して値を取得できます。この辺りは探したら出てくるかと思いますが、ついでなので一例を載せておきます。
private const string _dataCollection = "datacollection";

public static async Task<string> GetLatestUpdate(FirestoreDb firestore)
{
    var collection = firestore.Collection(_dataCollection);
    var docRef = collection.Document("data");
    var snapshot = await docRef.GetSnapshotAsync();

    if (!snapshot.Exists)
    {
        return null;
    }

    Dictionary<string, object> dataDict = snapshot.ToDictionary();
    return dataDict["latestUpdate"].ToString();
}

ちなみに、Google Cloudのサービスアカウントを使用した認証は下記のようになります。
一般的には開発中のみの使用に留まるかと思います。
private const string _serviceAccountKeyLocalPath = "C:/Gcp/serviceKey.json";

public static async Task<firestoredb> Auth_EnvKey()
{
    // 環境変数からロードする場合
    Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", _serviceAccountKeyLocalPath);

    // FirestoreDBのインスタンス作成
    return await FirestoreDb.CreateAsync(_projectId);
}

読み取り専用など、適切にサービスアカウントの権限を管理している場合は、埋め込みリソースとして下記のような本番運用も可能になるかと思いますが、推奨はされていないと思うのであくまで自己責任でご利用をお願いします。
private const string _serviceAccountKeyPath = "MyProject.Infra.Gcp.serviceKey.json";

public static async Task<firestoredb> Auth_ResKey()
{
    // 埋め込みリソースのサービスアカウントキーをストリームとしてロード
    var assembly = Assembly.GetExecutingAssembly();
    using var stream = assembly.GetManifestResourceStream(_serviceAccountKeyPath);

    // 認証情報の取得
    var googleCredential = GoogleCredential.FromStream(stream);

    // Firestoreクライアントの構築
    var firestoreClient = new FirestoreClientBuilder { GoogleCredential = googleCredential }.Build();

    // FirestoreDBのインスタンス作成
    var storage = StorageClient.Create(googleCredential);
    return await FirestoreDb.CreateAsync(_projectId, firestoreClient);
}

最後に

どの認証方法で運用するにおいても、セキュリティルールはしっかり書いておきましょう。
特に匿名認証の場合、意図せぬ書込や課金に繋がる恐れもあるので十分注意する必要があります。

参考にさせていただいたページ:
関連記事

コメント

非公開コメント