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
Related
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");
This follows on from a previous post which started as a general issue and is now more specific.
In short, I've been following guidance (such as this from Microsoft, this from Scott Hanselman, and this from Barry Dorrans) to allow me to share the authentication cookie issued by a legacy ASP.NET web app with a new dotnet core app running on the same domain.
I'm confident that I'm using the recommended Microsoft.Owin.Security.Interop library correctly. On that side (the old ASP.NET app), the CookieAuthenticationOptions are configured with AuthenticationType and CookieName both set to the same value - SiteIdentity. This same value is also used in the interop data protector setup:
var appName = "SiteIdentity";
var encryptionSettings = new AuthenticatedEncryptorConfiguration
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
};
var interopProvider = DataProtectionProvider.Create(
new DirectoryInfo(keyRingSharePath),
builder =>
{
builder.SetApplicationName(appName);
builder.SetDefaultKeyLifetime(TimeSpan.FromDays(365 * 20));
builder.UseCryptographicAlgorithms(encryptionSettings);
if (!generateNewKey)
{
builder.DisableAutomaticKeyGeneration();
}
});
ShimmedDataProtector = new DataProtectorShim(
interopProvider.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
appName,
"v2"));
I log in using this app, confirm I have a cookie named SiteIdentity then switch to a new dotnet core app running on the same domain.
There, without adding authentication middleware I can confirm that I can unprotect and deserialize the cookie. I do this by setting up data protection in Startup to match the other app:
var appName = "SiteIdentity";
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(keyRingSharePath))
.SetDefaultKeyLifetime(TimeSpan.FromDays(365 * 20))
.DisableAutomaticKeyGeneration()
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
})
.SetApplicationName(appName);
Then in my controller I can use a data protector to manually unprotect the cookie:
var appName = "SiteIdentity";
var protector = _dataProtectionProvider.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
appName,
"v2");
var cookieValue = Request.Cookies[appName];
var format = new TicketDataFormat(protector);
var ticket = format.Unprotect(cookieValue);
I can confirm that ticket.Principal does indeed reference a claims principal representing the account which I signed in with on the other app.
However, I've found it impossible to wire up the cookie authentication middleware to properly protect my endpoints using this cookie. This is what I've added to Startup, after the data protection code above:
var protectionProvider = services.BuildServiceProvider().GetService<IDataProtectionProvider>();
var dataProtector = protectionProvider.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
appName,
"v2");
services
.AddAuthentication(appName)
.AddCookie(appName, options =>
{
options.TicketDataFormat = new TicketDataFormat(dataProtector);
options.Cookie.Name = appName;
});
By my understanding this is telling the middleware that I have an authentication scheme named "SiteIdentity" (the advice is that authentication scheme must match the ASP.NET authentication type) which expects a cookie also called "SiteIdentity" which will contain protected data that the supplied data protector can interpret.
But when I add the attribute [Authorize(AuthenticationSchemes = "SiteIdentity")] to my controller I'm kicked away to a login page.
I can't understand what I'm doing wrong. As I've shown, I can confirm that it is indeed possible to use this data protector and ticket format to interpret the authentication cookie, so I guess I must have something wrong in this middleware wiring, but I'm not sure what.
Please ignore. It turns out that my code is actually correct. I had been working on this solution for long enough that the session represented by the cookie value I was using to test had expireed. Will leave this question here in case the code benefits anyone trying to achieve the same.
I create an Azure KeyVault using Pulumi:
var currentConfig = Output.Create(GetClientConfig.InvokeAsync());
var keyvault = new KeyVault(vaultname, new KeyVaultArgs
{
Name = vaultname,
Location = _resourceGroup.Location,
ResourceGroupName = _resourceGroup.Name,
TenantId = currentConfig.Apply(q => q.TenantId),
SkuName = "standard",
AccessPolicies =
{
new Pulumi.Azure.KeyVault.Inputs.KeyVaultAccessPolicyArgs
{
TenantId=currentConfig.Apply(q=>q.TenantId),
ObjectId=currentConfig.Apply(q=>q.ObjectId),
KeyPermissions={"get", "create", "list"},
SecretPermissions={"set","get","delete","purge","recover", "list"}
}, new Pulumi.Azure.KeyVault.Inputs.KeyVaultAccessPolicyArgs
}
});
As you can see I did not only create the KeyVault but also added the current ObjectId as an Access Policy.
Directly after that I try to add an entry to the KeyVault:
new Secret("secret",new SecretArgs
{
Name = "secret",
Value = "value",
KeyVaultId = keyVault.Id
});
This works fine locally when working with a user login (az login) But when using a service principle (DevOps) instead the Vault-Creation still works but adding secrets fails because of permission issues:
azure:keyvault:Secret connectionstrings-blobstorageaccountkey
creating error: checking for presence of existing Secret
"connectionstrings-blobstorageaccountkey" (Key Vault
"https://(vaultname).vault.azure.net/"):
keyvault.BaseClient#GetSecret: Failure responding to request:
StatusCode=403 -- Original Error: autorest/azure: Service returned an
error. Status=403 Code="Forbidden" Message="The user, group or
application
'appid=;oid=(objectId);iss=https://sts.windows.net/***/'
does not have secrets get permission on key vault
';location=westeurope'.
I am using the "classic" (non-nextgen)-variant at Pulumi.Azure
The cause of this issue was that I an pulumi up locally with my personal azure account. When running pulumi up as a service connection afterwards access wasn't possible because of different credentials.
When using a different stack (and different resources) for the service everything works fine.
So if testing the pulumi configuration you should always use a different stack when testing locally if permissions are required (which they almost ever are).
I will leave this question here because I suspect a few more people will fall into the same pit.
I am using Google.Cloud.Vision.V1, Version=2.0.0.0 and the following below code from Google Vision API specify JSON file
using Google.Apis.Auth.OAuth2;
using Google.Cloud.Vision.V1;
using Grpc.Auth;
using Grpc.Core;
var credential = GoogleCredential.FromFile("VisionProject.json");
var channel = new Grpc.Core.Channel(ImageAnnotatorClient.DefaultEndpoint.ToString(), credential.ToChannelCredentials());
var client = ImageAnnotatorClient.Create(channel);
But its shows me this error No overload for method 'ImageAnnotatorClient.Create' takes 1 arguments.
I have found similar code in documentation https://googleapis.github.io/google-cloud-dotnet/docs/Google.Cloud.Vision.V1P2Beta1/api/Google.Cloud.Vision.V1P2Beta1.ImageAnnotatorClient.html
But for some reason, it's not working( unable to see the overload)
It seems that you are using newer version of API. Docs state that now authentication is set up(when needed) via environment variable:
Otherwise, the simplest way of authenticating your API calls is to download a service account JSON file then set the GOOGLE_APPLICATION_CREDENTIALS environment variable to refer to it. The credentials will automatically be used to authenticate. See the Getting Started With Authentication guide for more details.
So you can do something like this:
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", "PathTo_VisionProject.json");
var client = ImageAnnotatorClient.Create();
Or set this environment variable some other way.
While setting the environment variable is certainly a simple way of specifying which service account file to use, it's not the only one. You can use the builder to specify the path very easily:
var client = new ImageAnnotatorClientBuilder
{
CredentialsPath = "VisionProject.json"
}.Build();
Another solution if you do not have access to the JSON file directly and you want to stay close to what you previously did with the GoogleCredential and Channel creation is something like:
var credential = GoogleCredential.FromFile("VisionProject.json");
// or if you have access to the content only
// var credential = GoogleCredential.FromJson(json);
var client = await new ImageAnnotatorClientBuilder
{
Endpoint = ImageAnnotatorClient.DefaultEndpoint,
ChannelCredentials = credential.ToChannelCredentials()
}.BuildAsync();
I am trying to use box api in an asp.net web application.
Based on the search there are two options to access box account;
By downloading the Box.V2 package using below link containing the required dlls and use that in our application
By using Box SDK containing code and reference that inside our application. Using this approach we can debug the Box.V2 code by adding the project to our solution.
Correct me if I am wrong.
So, I am trying to implement the second approach. Can someone help me move forward by specifying the steps to be taken, minimum .net framework requirement, etc.
Good question, GitHub samples does not mention about the Web (Asp.Net).
It's possible and it looks pretty easy to do once you figure out the the way,
I have seen some answers for Windows apps trying to manually build the authorization URLs etc, but there is an easier way to do it.
Here's how to do it with OAuth,
Install nuget
PM> Install-Package Box.V2
Get the Authcode (this is what's been missing in most examples)
public async Task<ActionResult> Connect()
{
var clientId = "xxxxx";
var clientSecret = "xxxxxx";
var redirectUri = new Uri("http://localhost:xxxx/Home/AuthCallBackAsync");//Your call back URL
var config = new BoxConfig(clientId, clientSecret, redirectUri);
return Redirect(config.AuthCodeUri.ToString());
}
Interesting thing is that the "config" object generates the AuthCodeUri.
This will redirect the user to Consent screen and ask the user to sign in. Once the user "Grants Access" you will get the "Authcode" for your call back URL which can be used to generate accesstoken.
Handle the Auth Callback response
public async Task<ActionResult> AuthCallbackAsync()
{
NameValueCollection parms = Request.QueryString;
var authCode = parms["code"]
//Get "config" - you can store this in session or in a cache.
var config = new BoxConfig(clientId, clientSecret, redirectUri);
var client = new BoxClient(config);
await client.Auth.AuthenticateAsync(authCode);
//Now you will get the accesstoken and refresh token
var accessToken = client.Auth.Session.AccessToken;
var refreshToken = client.Auth.Session.RefreshToken;
//Ready to consume the API
var user = await client.UsersManager.GetCurrentUserInformationAsync();
-------More Api Calls---
}