I have just installed Google.Cloud.Firestore 1.0.0-beta14 yesterday and started playing with it without setting up any sort of authentication. I soon discovered that I was able to access parts of my firestore database that had security rules around them.
To eliminate any sort of confusion on my part, I created a brand new Firebase project with a Firestore database in a locked mode. Sure enough, I'm able to both read & write things to it via my .net app. Here a sample code:
using System;
using System.Threading.Tasks;
using Google.Cloud.Firestore;
namespace ExerciseImagesFirestoreUploader
{
class Program
{
static async Task Main(string[] args)
{
var db = FirestoreDb.Create("my-project-id");
var collection = db.Collection("admins");
var newDoc = collection.Document("ZYpky483yuQlfcv9iVg5oROQbJn6");
await newDoc.CreateAsync(new { test = "123" });
var qs = await collection.GetSnapshotAsync();
foreach (var doc in qs.Documents)
{
Console.WriteLine("DocID: " + doc.Id);
}
}
}
}
Could someone please explain why this is happening?
The server SDK uses service account credentials, and doesn't use the Firestore-specific security rules.
From the documentation
Note: The server client libraries bypass all Cloud Firestore Security Rules and instead authenticate through Google Application Default Credentials. If you are using the server client libraries or the REST or RPC APIs, make sure to set up Cloud Identity and Access Management for Cloud Firestore.
Basically, those libraries are designed for situations where you're giving service-based access to your data rather than user-based access.
Hi @jskeet, but wouldn't I need to take some steps to provide access for my console app to my firestore database? I have done something similar in NodeJS where I had to create a service account key. I believe in .NET there are similar steps, and I also need to set my environment variable GOOGLE_APPLICATION_CREDENTIALS to point to the key file. I haven't done any of these. I had created a blank Firestore database in locked mode and I'm still able to access it?
Are you running your code on a GCP instance? If so, you're using the default Compute credentials you get that way. If you're not, and you're not specifying the credentials any other way, I'd be surprised.
I'm running the code on my local machine.
Then I believe you must have default credentials of some form, either specified through code, or an environment variable, or through the gcloud SDK. Without credentials, we wouldn't even get as far as setting up a connection with the server.
@LeXa777 If you intend to play with Firestore from a .NET application from an user account, this is what you need:
I will give you an example. But before that, keep in mind that this Firestore .NET SDK is designed for server-side use. I have been using it for .NET client-side applications (WPF, Win Forms, Xamarin) for pretty a much a year, and I can tell from my experience that it works just fine for client applications too. But it is NOT officially supported, and it misses many client-side features that other official Firestore SDKs provide.
``` c#
public FirestoreDb CreateFirestoreDbWithEmailAuthentication(string emailAddress, string password, string firebaseApiKey, string firebaseProjectId)
{
// Create a custom authentication mechanism for Email/Password authentication
// If the authentication is successful, we will get back the current authentication token and the refresh token
// The authentication expires every hour, so we need to use the obtained refresh token to obtain a new authentication token as the previous one expires
var authProvider = new FirebaseAuthProvider(new FirebaseConfig(firebaseApiKey));
var auth = authProvider.SignInWithEmailAndPasswordAsync(emailAddress, password).Result;
var callCredentials = CallCredentials.FromInterceptor(async (context, metadata) =>
{
if (auth.IsExpired()) auth = await auth.GetFreshAuthAsync();
if (string.IsNullOrEmpty(auth.FirebaseToken)) return;
metadata.Clear();
metadata.Add("authorization", $"Bearer {auth.FirebaseToken}");
});
var credentials = ChannelCredentials.Create(new SslCredentials(), callCredentials);
// Create a custom Firestore Client using custom credentials
var grpcChannel = new Channel("firestore.googleapis.com", credentials);
var grcpClient = new Firestore.FirestoreClient(grpcChannel);
var firestoreClient = new FirestoreClientImpl(grcpClient, FirestoreSettings.GetDefault());
return FirestoreDb.Create(firebaseProjectId, null, firestoreClient);
}
```
The code above is just a sample. It needs additional work, especially when it comes to handling eventual exceptions that authProvider.SignInWithEmailAndPasswordAsync may throw (invalid email/password, connection issues, etc).
Optionally, you can authenticate to Firebase using Firebase REST API.
Closing as everything is working as intended as far as I can see.
@pauloevpr Have you managed to get that working in UWP app? I tried your sample code, only modified to use SignInWithGoogleIdTokenAsync instead of SignInWithEmailAndPasswordAsync. Attempting to read data from Firestore fails after a timeout to exception "Status(StatusCode=Unavailable, Detail='DNS resolution failed')". Do you have any idea what could be wrong?
@kinex: That sounds like an entirely separate problem to me - please file a new issue, but check all your networking first as it does sound like a network problem.
@kinex: Also note that UWP is not a supported platform.
I never used Firestore with UWP, only with WPF. But it should make no difference anyway.
The exception suggests your computer was unable to resolve a hostname. The only hostname I am setting manually is firestore.googleapis.com. it is unlikely that this or any other Google hostnames have changed. So there is probably a network issue with your computer.
I would suggest you to try from a different network or computer.
@jskeet Yes it is a separate issue, but I wanted to ask it here as my question is related to the code sample presented here. I know UWP is not an officially supported platform, but I haven't found any reason so far why it wouldn't work. If you can give me a good reason why it won't work I can stop trying and find some alternative solution :) But I really need to access Firestore from my existing UWP app in a way or another. I would prefer using this library. And as this library targets netstandard2.0, which is implemented by Windows 10.0.16299 or later, it _should_ work. I guess that's the whole idea of .Net Standard.
I was finally able to resolve the issue, I had to add capability "privateNetworkClientServer" to the app manifest. So this is progressing.... thank you @pauloevpr for your great code sample, it helped a lot.
I strongly suspect you'd get the same error with any sample. I'd be surprised if it were related to this one. In terms of why it wouldn't work - I don't have any concrete reasons why it shouldn't, but because it's an unsupported platform I don't have time to try to diagnose it.
I don't know that, difficult to try other samples if you cannot authenticate your app. Actually this is the only working sample I have found from the whole Internet which demonstrates how to authenticate a client app using this library.
No, I'd expect you to see the same issue with any sample using default application credentials. No separate authentication is required in order to test that. Try to get that working before anything else. But again, this is an unsupported scenario, so I won't be able to spend any time looking into it, and I'd request that you don't add more comments to this issue when the problem you're facing isn't directly related to it.
I am not sure, why this is not working in Winform Application, my code stays in running mode when it reached the point GetSnapshotAsync and keep on running, no exception no return value. dont know why.
Anyone have any idea.
@BidyaSagarJena: I don't think that's related to this topic at all. I've seen the issue you reported in the dotnet-docs-samples repo, and I'll investigate that, but please don't add comments to unrelated issues.
Most helpful comment
@LeXa777 If you intend to play with Firestore from a .NET application from an user account, this is what you need:
I will give you an example. But before that, keep in mind that this Firestore .NET SDK is designed for server-side use. I have been using it for .NET client-side applications (WPF, Win Forms, Xamarin) for pretty a much a year, and I can tell from my experience that it works just fine for client applications too. But it is NOT officially supported, and it misses many client-side features that other official Firestore SDKs provide.
``` c#
public FirestoreDb CreateFirestoreDbWithEmailAuthentication(string emailAddress, string password, string firebaseApiKey, string firebaseProjectId)
{
// Create a custom authentication mechanism for Email/Password authentication
// If the authentication is successful, we will get back the current authentication token and the refresh token
// The authentication expires every hour, so we need to use the obtained refresh token to obtain a new authentication token as the previous one expires
var authProvider = new FirebaseAuthProvider(new FirebaseConfig(firebaseApiKey));
var auth = authProvider.SignInWithEmailAndPasswordAsync(emailAddress, password).Result;
var callCredentials = CallCredentials.FromInterceptor(async (context, metadata) =>
{
if (auth.IsExpired()) auth = await auth.GetFreshAuthAsync();
if (string.IsNullOrEmpty(auth.FirebaseToken)) return;
```
The code above is just a sample. It needs additional work, especially when it comes to handling eventual exceptions that
authProvider.SignInWithEmailAndPasswordAsyncmay throw (invalid email/password, connection issues, etc).Optionally, you can authenticate to Firebase using Firebase REST API.