Hello,
I know that you officially do not support Xamarin, but I'm using your great library for about 6 months and until now, everything worked very well (thank you for all your efforts on this).
To summarize my problem :
Do you eventually see anything I might forget when implementing transactions ?
Thanks in adavance.
Version with issue: Xamarin.Forms 4.8
IDE: Visual Studio for Mac (Community) 8.6.8
iOS: 13.6
Affected Devices: tested through iPhone Simulator and physical device
Please could you provide some sample code? I will have a look on Monday.
Sure, thanks for looking at it !
Here is how I initialize your FirestoreDb object :
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly IAuthenticationService _authenticationService;
private readonly string FirebaseToken;
private readonly string FirebaseUserId;
public FirestoreDb Database { get; set; }
public string CollectionName { get; set; }
public GenericRepository(IAuthenticationService authenticationService)
{
_authenticationService = authenticationService;
var (authUser, sessionStart) = _authenticationService.GetStoredSessionInfos();
FirebaseToken = authUser.FirebaseToken;
FirebaseUserId = authUser.User.LocalId;
// FirebaseToken is a token generated after authentication succeed with email/password
GoogleCredential credential = GoogleCredential.FromAccessToken(FirebaseToken);
ChannelCredentials channelCredentials = credential.ToChannelCredentials();
Channel channel = new Channel(FirestoreClient.DefaultEndpoint.ToString(), channelCredentials);
FirestoreClientBuilder firestoreClientBuilder = new FirestoreClientBuilder { ChannelCredentials = channelCredentials };
FirestoreClient firestoreClient = firestoreClientBuilder.Build();
Database = FirestoreDb.Create(AppSettings.FIREBASE_PROJECT_ID, client: firestoreClient);
...
}
}
And here is an example of a method that works fine without any transaction :
public async Task<T> GetOneItemByIdAsync(string Id)
{
try
{
DocumentReference documentReference = Database
.Collection(CollectionName)
.Document(Id);
DocumentSnapshot documentSnapshot = await documentReference.GetSnapshotAsync();
if (documentSnapshot.Exists)
{
T item = documentSnapshot.ConvertTo<T>();
if (item != null)
return item;
else
//TODO : throw exception ?
return null;
}
return null;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
For this simple example, I thought that surrounding request with a RunTransactionAsync would be straightforward :
public async Task<T> GetOneItemByIdAsync(string Id)
{
try
{
DocumentReference documentReference = Database
.Collection(CollectionName)
.Document(Id);
T itemFound = await Database.RunTransactionAsync<T>(
async transaction =>
{
DocumentSnapshot documentSnapshot = await documentReference.GetSnapshotAsync();
if (documentSnapshot.Exists)
{
T item = documentSnapshot.ConvertTo<T>();
if (item != null)
return item;
else
//TODO : throw exception ?
return null;
}
return null;
});
return itemFound;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
But instead, I've got the following error :

Status(StatusCode="PermissionDenied", Detail="Missing or insufficient permissions.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1597578950.549411000","description":"Error received from peer ipv6:[xxxx:xxxx:xxxx:xxxx::xxxx]:443","file":"src/core/lib/surface/call.cc","file_line":1055,"grpc_message":"Missing or insufficient permissions.","grpc_status":7}")
And even if I try this simple example, I fall into the same error :
public async Task TestRunAsTransaction()
{
try
{
await Database.RunTransactionAsync(async transaction =>
{
await Task.Delay(2000);
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
(As a side note, you don't need to create the channel - you're not actually using it.)
I see you're using an access token for the authentication. I suspect that's the problem - it may well be that the access token doesn't have transaction permissions.
I would suggest:
It's worth noting that this library is expected to be used for server-side applications, usually with either default credentials on Compute Engine or using a service account. I wouldn't be surprised if credentials which are more user-oriented had some issues.
Thanks for your suggestions, I bet you are right about access token permissions.
I will try as soon as possible, unfortunately not this week.
Hi @Kapusch, any new information on this?
Hi @jskeet , sorry but no.
Be sure that I will try as soon as possible.
Do you prefer that I close this issue until I reproduce it under .NET Core ?
@Kapusch: I'm fine leaving it open if you're likely to get to it in the next week or two. If it's likely to be quite a long time, we could close it and then reopen when you've more information.
Closing this now as I believe my earlier response is the relevant part - but please add a comment if you want me to reopen.
Hello @jskeet , it's been a long time but finally, you were right !
I've been able to reproduce this exception in a .NET Core project, and after I switched from Access Token to JSON Google Credentials, it was ok !
Thank you very much.
Hooray - thanks so much for getting back to me. It's always nice to get a sense of closure :)
Hi to both,
Maybe I should post a different question since my case is slightly different in that I have tried access to Firestore using the JSON private key file downloaded from Firebase Authentication console in UWP App and WPF App. They both fail with Access denied on the JSON file. So it may be purely a UWP/WPF issue (I'm learning from scratch). It works fine as a .NET Console app.
Maybe I'll try Xamarin so if you have any details about the code to read the JSON I'd appreciate it.
I also tried using a FirestoreDbBuilder and pasting in the whole JSON text, but now the code hangs on the Async call to GetSnapshotAsync().
Best regards,
@folsen1969: I would suggest asking a new question, although I'm afraid I don't know much about the Firebase Authentication console and what kind of credential it provides. It sounds like the error is about how you load the file rather than anything Firestore-specific though. I don't have any specific examples for those or Xamarin, but the file loading part should be fixable via tutorials about those platforms without any reference to Google authentication.
In terms of the hanging code, I'd really need to see the code you're using - again, that would be best in a different issue.
Hi @jskeet ,
I remember you said that this library is expected to be used for server-side applications.
And as you said, this is probably why access token doesn't have transaction permissions.
However, I have implemented security rules for Cloud Firestore (and Storage), and these rules are based on Authenticated User Uid. For example :
request.auth.uid
These rules cannot work if I use GoogleService credentials JSON file instead of Access Token, right ?
Do you possibly have any idea or recommendation regarding my willing to use transactions please ?
Thank you in advance.
I'm afraid I know nothing about the security rules for Firestore itself - I make sure the credentials that you provide get to the server correctly, and that's about it.
I don't have any recommendations around the use of transactions in this sort of environment - I would suggest following one of the paths described in the normal Firebase support for advice from experts who know more about the server side and Firebase/Firestore security than I do. Sorry not to be able to provide any more help myself :(
You're kidding, you were very reactive and helpful from the beginning ! :)
Thanks for your very good support, I will ask for some help through Firebase support.
Will get back at you in case I've got anything new related to this denied permission.