I am attempting to connect a local .NET Core project to a Firebase project. I am using the Google documentation at https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Firestore/latest:
private async Task<Dictionary<string, object>> GetData()
{
string projectId = "My-Project";
FirestoreDb db = FirestoreDb.Create(projectId);
Dictionary<string, object> documentDictionary = new Dictionary<string, object>();
...
}
When it hits the call to create an instance of the project (FirestoreDb.Create(projectId)), I receive this error:
Error: System.InvalidOperationException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
at Google.Apis.Auth.OAuth2.DefaultCredentialProvider.CreateDefaultCredentialAsync()
I have exported the service account from Firebase into a JSON file, generating a private key and saving it locally:
I have added the path of the key file to this JSON file in an environment variable:
However, I continue to get the error.
There is one way I have been able to authenticate, and that was using the example code at https://cloud.google.com/docs/authentication/production:
public object AuthExplicit(string projectId, string jsonPath) {
var credential = GoogleCredential.FromFile(jsonPath);
var storage = StorageClient.Create(credential);
var buckets = storage.ListBuckets(projectId);
foreach (var bucket in buckets)
{
Console.WriteLine(bucket.Name);
}
return null;
}
However, I don't know how (or if it's possible) to link that authentication code with Firebase in order to retrieve my data.
Is there any guidance you can provide so I can properly authenticate and pull my data?
The missing step was calling the FirebaseApp.Create() method, as described in the Firebase authentication documentation.
I modified my application to add that method before the call to FirestoreDb.Create(), and that did it. Here is the modified code:
private async Task<Dictionary<string, object>> GetData() {
string projectId = "My-Project";
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.GetApplicationDefault(),
});
FirestoreDb db = FirestoreDb.Create(projectId);
Dictionary<string, object> dd = new Dictionary<string, object>();
...
}
This link provides a reason why this error is being caused.
I – If GOOGLE_APPLICATION_CREDENTIALS is SET and it uses the service
account file path using the value associated with the above
Environment variable GOOGLE_APPLICATION_CREDENTIALS
II – If GOOGLE_APPLICATION_CREDENTIALS is not SET, then the following preferences are set:
ADC uses a service account file that is running the code.
Otherwise, if a service account also does exist, then ADC uses the default service account that Compute Engine, Google Kubernetes Engine, App Engine, and Cloud Functions provide.
Try to debug the application locally by doing the following:
If you already have a service account created, then it applies to an application instead of an individual user. You need to authenticate a service account while accessing your IAP-secured resources.
Please make sure to set Environment Variable GOOGLE_APPLICATION_CREDENTIALS with the secured key JSON file path.
Example
C#:
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", "secured-service-account.json");
Related
I have a Xamarin.Forms application. I need an Azure Storage to store some data. I don't want to store the Shared Access Signature (SAS) in my application, obviously.
In my application, I've develop a KeyVaultService:
private readonly Azure.Security.KeyVault.Secrets.SecretClient _keyVaultSecretClient;
public KeyVaultService()
=> _keyVaultSecretClient = new(
new Uri("https://foo.vault.azure.net/"),
new DefaultCredential());
// ...
public async Task<string> GetSecretAsync(string secretName)
{
// ...
var response = await _keyVaultSecretClient.GetSecretAsync(secretName);
// ...
return response.Value.Value;
}
A AzureStorageService:
private readonly IKeyVaultService _keyVaultService;
public AzureStorageService() => _keyVaultService = DependencyService.Get<IKeyVaultService>();
public async Task<byte[]> GetFooAsync()
{
var connectionString = await _keyVaultService.GetSecretAsync(STORAGE_CONNECTIONSTRING);
_blobContainerClient = new BlobContainerClient(connectionString, CONTAINER_NAME);
}
And it's called like that:
byte[]? fooBytes = await _azureStorageService.GetFooAsync();
await DoSomething(fooBytes);
Tests
The second parameter of SecretClient is a Azure.Identity.TokenCredential derived class. With my _keyVaultSecretClient instance, in my KeyVaultService, I have tried to use some classes (DefaultAzureCredential, ClientSecretCredential, ...):
DefaultAzureCredential and ClientSecretCredential
public KeyVaultService() => new(new Uri("foo...."), new DefaultAzureCredential())
and
public KeyVaultService() => new(new Uri("foo...."), new ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET))
An exception throws:
Azure.Identity.AuthenticationFailedException:
'ClientSecretCredential authentication failed: Confidential Client flows are not available on mobile platforms or on Mac.See https://aka.ms/msal-net-confidential-availability for details.'
Inner Exception:
PlatformNotSupportedException: Confidential Client flows are not available on mobile platforms or on Mac.See https://aka.ms/msal-net-confidential-availability for details.
And I don't want to keep Tenant ID, Client ID or Secret ID in my code
EnvironmentCredential
public KeyVaultService() => new(new Uri("foo...."), new EnvironmentCredential())
I have add 3 environment variables on my Windows: AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID.
AuthenticationFailedException is thrown, again.
ManagedIdentityCredentials
public KeyVaultService() => new(new Uri("foo...."), new ManagedIdentityCredential(CLIENT_ID))
Another exception throws:
Azure.Identity.CredentialUnavailableException:
'ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.'
Where and how can I add an Identity endpoint?
The client ID is in the code!! Nooo :(
Information
Mobile Apps + Key Vault Don't do it
I have read this blog post: https://codemilltech.com/mobile-apps-azure-keyvault-dont-do-it/, But... If an anonymous user found the url of my Azure Function... He can read my Key Vault address... And my application throws the same exceptions... I think is my approach.
Develop an Azure Function which access to Key Vault and his secrets, then sends them back to me through an API? Ooh: All of my secrets can be leaked. No thanks.
I have read the link provided by the exception (https://aka.ms/msal-net-confidential-availability). MSAL? Why for Azure? From what I understood, MSAL allows to connect to Microsoft Graph and Office 365 APIs...
In resume
I understand that my approach is not available on mobile platforms (The exception is self-explanatory enough to guess, I promise) - because a phone is not entirely secure device?
So,
what is the implementation to use an Azure Key Vault or another solution to securely access to my Azure Storage?
In fact,
I want to have not the secrets in my code;
I want access to an Azure Storage with a SAS, to a SAP server with username/password and another external services (stored in the Key Vault - or another securely way).
Someone has an idea?
Thanks in advance :-)
I am creating a SAS key in my C# code, like below.
string returnValue = String.Empty;
// Create a new access policy for the account with the following properties:
SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
Permissions = SharedAccessAccountPermissions.Read | SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.Create,
Services = SharedAccessAccountServices.Blob,
ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(10),
Protocols = SharedAccessProtocol.HttpsOrHttp
};
// Create new storage credentials using the SAS token.
ExecuteWithExceptionHandling(
() =>
{
returnValue = storageAccount.GetSharedAccessSignature(policy);
});
return returnValue;
This code generates a key properly. But when I try to use this key in Azure Storage Explorer, I get an error saying Inadequate resource type access. At least service-level ('s') access is required.
When I try to use this key from javascript to create a container, I get an error saying Refused to set unsafe header "user-agent" in azure-storage-blob.min.js file (came from azure storage api by microsoft).
I have added the create permission in SharedAccessAccountPolicy, but the key generated is not working for some reason.
Edit:
Javascript code to create blobservice.
var blobService = AzureStorage.Blob.createBlobServiceWithSas(blobUri, $("#SASToken").val());
blobService.createContainerIfNotExists(folderID, function (error, result) {
if (error) {
return false;
} else {
return true;
}
});
Edit:
Below is a screenshot of the azure portal, where I tried to generate manual token. Even that token has ss=b in it instead of sr=b as per the document.
I tried the token I generated manually. And the JS code still says Refused to set unsafe header "user-agent".
Apparently it is an issue with the damn js file provided by Azure.
https://github.com/Azure/azure-storage-node/issues/463
Here is an open ticket about it.
Anyway, for now, I have added a condition to ensure header is not added. If the code still fails, I will create a different question.
I think, within 2 days, I can just take the latest update of the Azure's js and this should fix itself. Still in case, anyone needs a quick fix, here is the code change done by me.
if (e[0] != 'user-agent'){ s.setRequestHeader(e[0],e[1])}}
instead of s.setRequestHeader(e[0],e[1])}
It is only a temporary fix. Best option is still to update to version 2.10.101 or higher for azure storage js file.
I am integrating PayPal payments into my website using the PayPal REST API using the .NET SDK (https://www.nuget.org/packages/PayPal). My website is running as an App Service in Microsoft Azure. I have a testing slot configured to use PayPal Sandbox, and a production slot which uses the Live credentials (configuration settings are in the App Settings, and are slot-specific).
Everything works fine in Sandbox, and I am able to authenticate and get an access token using the live configuration, but when I try to process anything I get a PayPal.IdentityException (I tried Payments as well as Billing Plans and Agreements). If I look at the IdentityException Response (or Details), I get this:
{"error":"invalid_token","error_description":"The token passed in was not found in the system"}
Digging further, it seems that the ApiContext that is created has the AccessToken set, but the Config property is null. If I look at the API request (from PayPalResource.LastRequestDetails.Value) I see that even though it's using the live configuration, it's sending the request to api.sandbox.paypal.com, not the live API. This seems to be a problem with my ApiContext, which I establish as follows:
public static APIContext Authenticate()
{
string clientId = ConfigurationManager.AppSettings["PayPalClientId"];
string clientSecret = ConfigurationManager.AppSettings["PayPalSecret"];
string mode = ConfigurationManager.AppSettings["PayPalMode"];
var config = new Dictionary<string, string>();
config.Add("mode", mode);
config.Add("clientId", clientId);
config.Add("clientSecret", clientSecret);
accessToken = new OAuthTokenCredential(config).GetAccessToken();
return new APIContext(accessToken); // <--- problem here
}
Apart from manually constructing the Config dictionary rather than using the ConfigManager to create it automatically from a custom config section, this follows the .NET SDK documentation very closely. What am I doing wrong?
The problem here is that all that is passed to the APIContext constructor is the accessToken. Without having the settings in the custom config section of Web.config or App.config, APIContext defaults to using the sandbox API.
The solution is to explicitly set the Config property using an Object Initializer:
return new APIContext(accessToken) { Config = config }; // <---- fixed
I am new to Azure Data Lake Analytics and am converting a C# batch job to use service to service authentication before submitting stored procedures to Azure Data Lake Analytics.
public void AuthenticateADLUser()
{
//Connect to ADL
// Service principal / appplication authentication with client secret / key
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
cTokenCreds = ApplicationTokenProvider.LoginSilentAsync(strDomain, strWebApp_clientId, strClientSecret).Result;
SetupClients(cTokenCreds, strSubscriptionID);
}
public static void SetupClients(ServiceClientCredentials tokenCreds, string subscriptionId)
{
_adlaClient = new DataLakeAnalyticsAccountManagementClient(tokenCreds);
_adlaClient.SubscriptionId = subscriptionId;
_adlaJobClient = new DataLakeAnalyticsJobManagementClient(tokenCreds);
_adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(tokenCreds);
}
Even though I have given it the correct ClientId the error comes back with a different ClientID in the error when I execute the following code:
var jobInfo = _adlaJobClient.Job.Create(_adlsAccountName, jobId, parameters);.
The error message is:
The client 'e83bb777-f3af-4526-ae34-f5461a5fde1c' with object id 'e83bb777-f3af-4526-ae34-f5461a5fde1c' does not have authorization to perform action 'Microsoft.Authorization/permissions/read' over scope '/subscriptions/a0fb08ca-a074-489c-bed0-....
Why is the ClientID different than the one I used in the code?
Is this a code issue or a permissions issue? I assume that it is code since the ClientID is not an authorized one that I created.
note: The SubscriptionId is correct.
I assumed you created an Azure Active Directory App and are you the client and domain IDs of this app. If not, you'll need that... If you do have that, then can you check if the App has permissions over your Data Lake Store: https://learn.microsoft.com/en-us/azure/data-lake-store/data-lake-store-authenticate-using-active-directory
Had exactly same symptoms. WebApp was created in AAD in portal originally to access Azure Data Lake Store and same code-snippet worked perfectly. When I decided to re-use same WebApp (clientid/secret) it failed with same error, even though I have given reader/contributor roles on sub/RG/ADLA to the App.
I think the reason is that WebApp underneath has a "service principal" object (thus error msg shows different object id) and ADLA uses it for some reason. Mine didn't have credentials set - empty result:
Get-AzureRmADSpCredential -objectid <object_id_from_error_msg>
Added new password as described here
New-AzureRmADSpCredential -objectid <object_id_from_error_msg> -password $password
Used the pwd as secret in LoginSilentAsync, clientId was left as before - WebApp clientId (not the principal object id shown in the error)
I wasn't able to find this principal info in portal, only PS.
Now a days i am implementing sentiment analysis through google cloud library,my code is
string text = "Feeling Not Well";
var client = LanguageServiceClient.Create();
var response = client.AnalyzeSentiment(new Document()
{
Content = text,
Type = Document.Types.Type.PlainText
});
var sentiment = response.DocumentSentiment;
var Score = sentiment.Score;
var magnitude = sentiment.Magnitude;
but it gives an error on
var client = LanguageServiceClient.Create();.
the error is
The Application Default Credentials are not available.
They are available if running in Google Compute Engine.
Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS
must be defined pointing to a file defining the credentials.
See https://developers.google.com/accounts/docs/application-default-credentials for more information.
please give me solution
You can either use
gcloud auth application-default login
from the command line (assuming you have the Cloud SDK installed), or generate and download a service account JSON file and then set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to that file.
The Create method call will first check the environment variable, and then look for application default credentials from gcloud if the environment variable isn't set.
Basically, the credential options are:
Explicitly create one from a service account file, e.g. GoogleCredential.FromStream(stream) and use that to create a Channel which you can pass into Create, as described in the FAQ
Call create without any arguments (or passing in null) in which case:
If you've set the GOOGLE_APPLICATION_CREDENTIALS environment variable, it is assumed that's where the service account JSON file is
Otherwise, if you've run gcloud auth application-default login those credentials will be used
Otherwise, if you're running on Google Cloud Platform (e.g. Compute Engine or AppEngine Flexible) you will get the default credentials for the project
Otherwise, the call will fail
Additionally, you can use the Document.FromPlainText call to simplify your code:
string text = "Feeling Not Well";
var client = LanguageServiceClient.Create();
var response = client.AnalyzeSentiment(Document.FromPlainText(text));
var sentiment = response.DocumentSentiment;
var Score = sentiment.Score;
var magnitude = sentiment.Magnitude;