step-up-labs / firebase-storage-dotnet

C# library for Firebase Storage
MIT License
140 stars 35 forks source link

Problem authenticating with Service Account #37

Closed marko-mihalic closed 2 years ago

marko-mihalic commented 3 years ago

I'm having trouble authenticating with Service Account.

Could someone please provide example on how to do it?

macleysousa commented 3 years ago
                var bytes = Convert.FromBase64String("base64");
                var stream = new StreamContent(new MemoryStream(bytes)).ReadAsStream();

                var auth = new FirebaseAuthProvider(new FirebaseConfig("api_key"));
                var a = await auth.SignInWithEmailAndPasswordAsync("email", "password");

                var cancellation = new CancellationTokenSource();

                var action = new FirebaseStorage(
                    "bucket",
                    new FirebaseStorageOptions
                    {
                        AuthTokenAsyncFactory = () => Task.FromResult(a.FirebaseToken),
                        ThrowOnCancel = true,
                    })
                    .Child("folder")
                    .Child($"{"file_name"}.{"file_extension"}")
                    .PutAsync("stream", cancellation.Token);
marko-mihalic commented 3 years ago

Thank you but that's Regular Account (Service account doesn't have "email" or "password"). I'm talking about Service Account for which you get Private Key in .json format through Firebase Console.

e.g. I'm using it like this for RealtimeDatabase:

private static async Task<String> AuthenticateAsync(String privateKeyLocation)
{
GoogleCredential googleCredential = GoogleCredential.FromFile(privateKeyLocation)
.CreateScoped(
"https://www.googleapis.com/auth/firebase", // RTDB.
"https://www.googleapis.com/auth/userinfo.email", // RTDB
"https://www.googleapis.com/auth/identitytoolkit", // User management
"https://www.googleapis.com/auth/devstorage.full_control", // Cloud Storage
"https://www.googleapis.com/auth/cloud-platform", // Cloud Firestore
"https://www.googleapis.com/auth/datastore"
);

return await googleCredential.UnderlyingCredential.GetAccessTokenForRequestAsync(); }

But, I'm still getting (403) Forbidden as StatusCode of Response (for Storage, Realtime Database works just fine).

This is rest of Code which is using AuthenticateAsync in case I'm missing something there:

FirebaseStorageTask firebaseStorageTask = new FirebaseStorage(bucketURL, new FirebaseStorageOptions
{
AuthTokenAsyncFactory = () => AuthenticateAsync(serviceAccountPrivateKeyLocation)
})
.Child(@"AppImages")
.Child(Path.GetFileName(filePath))
.PutAsync(stream);
michaelgaultjr commented 3 years ago

Thank you but that's Regular Account (Service account doesn't have "email" or "password"). I'm talking about Service Account for which you get Private Key in .json format through Firebase Console.

e.g. I'm using it like this for RealtimeDatabase:

private static async Task<String> AuthenticateAsync(String privateKeyLocation)
{
GoogleCredential googleCredential = GoogleCredential.FromFile(privateKeyLocation)
.CreateScoped(
"https://www.googleapis.com/auth/firebase", // RTDB.
"https://www.googleapis.com/auth/userinfo.email", // RTDB
"https://www.googleapis.com/auth/identitytoolkit", // User management
"https://www.googleapis.com/auth/devstorage.full_control", // Cloud Storage
"https://www.googleapis.com/auth/cloud-platform", // Cloud Firestore
"https://www.googleapis.com/auth/datastore"
);

return await googleCredential.UnderlyingCredential.GetAccessTokenForRequestAsync(); }

But, I'm still getting (403) Forbidden as StatusCode of Response (for Storage, Realtime Database works just fine).

This is rest of Code which is using AuthenticateAsync in case I'm missing something there:

FirebaseStorageTask firebaseStorageTask = new FirebaseStorage(bucketURL, new FirebaseStorageOptions
{
AuthTokenAsyncFactory = () => AuthenticateAsync(serviceAccountPrivateKeyLocation)
})
.Child(@"AppImages")
.Child(Path.GetFileName(filePath))
.PutAsync(stream);

I found a bit of a workaround using this and the FirebaseAdmin SDK, it could be improved, but it's a good start. (Make sure you Initialize the Admin SDK with FirebaseApp.Create() before running the code below)

string token = await FirebaseAdmin.Auth.FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("[your server id, can be anything]", developerClaims: new Dictionary<string, object>() 
            {
                { "server", true } // Adds a "server: true" claim to the token
            });
FirebaseAuthProvider firebaseAuthProvider = new FirebaseAuthProvider(new FirebaseConfig("[Firebase web API key]"));
var authLink = await firebaseAuthProvider.SignInWithCustomTokenAsync(token);
return authLink.FirebaseToken; // This is the JWT token
marko-mihalic commented 3 years ago

I'm still getting same 403 error. I've tried it like this:

FirebaseApp firebaseApp = FirebaseApp.Create(new AppOptions
            {
                Credential = GoogleCredential.FromFile(serviceAccountPrivateKeyLocation),
                ProjectId = projectID
            });

            string token = await FirebaseAdmin.Auth.FirebaseAuth.GetAuth(firebaseApp).CreateCustomTokenAsync(Guid.NewGuid().ToString(), developerClaims: new Dictionary<string, object>()
            {
                { "server", true } // Adds a "server: true" claim to the token
            });

            FirebaseAuthProvider firebaseAuthProvider = new FirebaseAuthProvider(new FirebaseConfig(apiKey));
            var authLink = await firebaseAuthProvider.SignInWithCustomTokenAsync(token);

            return authLink.FirebaseToken; // This is the JWT token
caneva20 commented 3 years ago

I've faced the same problem yesterday and found a good (and simple) solution just now. Assuming you're using the AdminSDK (nuget) you can get a valid JWT Token with¹:

FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("SOME_UID")

If you are still getting a 403 error, take a look at this SO answer

# Sample:

var options = new FirebaseStorageOptions {
    AuthTokenAsyncFactory = () => FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("SOME_UID")
};

new FirebaseStorage("PROJECT.appspot.com", options)
   .Child("FILE_NAME")
   .PutAsync(SOME_STREAM);

# Sources: 1: https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_the_firebase_admin_sdk 2: https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth

longa78 commented 3 years ago

@marko-mihalic have you found a solution maybe?

marko-mihalic commented 3 years ago

@caneva20 thank you, I'll try with rule separation mentioned in this SO answer when/if I get back to it.

@longa78 I didn't, we decided not to use Firebase Storage after all.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 2 years ago

Closing the issue due to inactivity. Feel free to re-open