We have a 3rd party web service that we consume from our web application. I've created a windows service that call this web service for all our customer, each of them with their own credentials.
The web service is using basic authentication.
When cycling through the credentials, it always uses the first authenticated credential.
Example code below:
private void testConnection(string username, string password)
{
_url = "https://somewebservice.com.au/transport/SomeService";
using (var someWebService = new someWebService {Url = _url})
{
var netCredential = new NetworkCredential(username, password);
var credential = new CredentialCache
{
{new Uri(someWebService.Url), "Basic", new NetworkCredential(username, password)}
};
someWebService.Credentials = credential;
someWebService.PreAuthenticate = true;
someWebService.Timeout = 1200000;
try
{
var result = someWebService.testConnection();
textBoxMsg.Invoke(new UpdateTextBoxDelegate(UpdateTextBox), result);
}
catch (SoapException soapex)
{
textBoxMsg.Invoke(new UpdateTextBoxDelegate(UpdateTextBox), "SOAP Error: " + soapex.Message + "\r\n");
}
catch (WebException webex)
{
textBoxMsg.Invoke(new UpdateTextBoxDelegate(UpdateTextBox), "Web Error: " + webex.Message + "\r\n");
}
catch (Exception ex)
{
textBoxMsg.Invoke(new UpdateTextBoxDelegate(UpdateTextBox), "Error: " + ex.Message + "\r\n");
}
finally
{
credential.Remove(new Uri(someWebService.Url), "Basic");
}
}
}
private void buttonTest2_Click(object sender, EventArgs e)
{
ThreadStart start = () => testConnection("user1", "123");
new Thread(start).Start();
}
private void buttonTest3_Click(object sender, EventArgs e)
{
ThreadStart start = () => testConnection("user2", "1234");
new Thread(start).Start();
}
if "user1" is authenticated first, second call to testConnection will always use "user1" credentials.
I've tried setting HttpWebRequest KeepAlive to false so connection is not re-used. TCP monitoring show the connection getting killed after setting it to false. This did not solve the issue.
If I close down the application and restart, and run testConnection for "user2", it gives the correct result.
Seems like the credentials are cached in the process. The only way to get rid of it is to exit the process.
This present some challenges when I have to cycle through about 1000 credentials.
Is there a way to clear the credential cache in process? If that is what's happening. See this link How to stop credential caching on Windows.Web.Http.HttpClient?
Options that I've explored and protoyped
Self hosted WCF accessing the web service. Another process that start and stop the WCF service for every credential
WCF service hosted in Windows Services - Another process that start and stop the windows service for every credential
Wait more than 10 mins for server side credential to expire. Not viable too slow.
My problem with the aboves are they have to be sequential, so to cycle through 1000 of them will take a while. Each credential will be around 20-30 secs.
Try using CredentialCache.Remove() method after your last catch block.
https://msdn.microsoft.com/en-us/library/4zaz5c95(v=vs.110).aspx
This will remove it from cache.
Maybe this is a bit old question, but you PreAuthenticate should be false for HttpClient not to cache the credentials.
Related
I need help with EWS service. I need to read inbox/sent mails of an account Office 365, the access to this account is "on behalf of" and I usually login with my domain credentials. This is the firt time I need to do this, so searching on Google I found this solution:
static void Main(string[] args)
{
ExchangeService _service;
try
{
Console.WriteLine("Registering Exchange connection");
_service = new ExchangeService
{
Credentials = new WebCredentials("user", "pwd", "domain")
};
AutodiscoverRedirectionUrlValidationCallback validurl = new AutodiscoverRedirectionUrlValidationCallback((a) => { return true; });
_service.AutodiscoverUrl("email#mail.com", validurl);
}
catch (Exception e)
{
Console.WriteLine("new ExchangeService failed with error '" + e.Message + "'. Press enter to exit:");
return;
}
// This is the office365 webservice URL
_service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
When I run this code, the program goes up to the line "_service.AutodiscoverUrl("email#mail.com", validurl);", so it remains as "suspended", no error no timeout.
I don't know what the problem is, can anyone help me?
Thanks everyone
Nick
I am using Nick Vane's Magento REST API and the Oauth authentication works on one client and fails on another.
I have tried resetting the consumer information, client key and secret but on the second site still fails.
var client = new MagentoApi();
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
try
{
if (AdminURLPart.Length > 0)
{
client.SetCustomAdminUrlPart(AdminURLPart);
}
client.Initialize(URL, ConsumerKey, ConsumerSecret);
client.AuthenticateAdmin(MagentoUser, MagentoPassword);
}
catch (Exception e1)
{
BCCUtils.Logger("Authentication Error: " + e1.Message, true, CurrentModule);
return -1;
}
There was a problem with oauth at 'https://www.xxxxz.com/oauth/initiate' for consumer key 'xxxxxxx'. Message: 'oauth_problem=parameter_absent&oauth_parameters_absent=oauth_consumer_key'
It may be helpful to the community that we resolved this problem by removing "www." from the URL being used within the application. Once we did that, the MagentoAPI authentication function within the Nick Vane project worked correctly. This was the case only for the one client where we were having difficulty authenticating.
I'm trying to authenticate my web app with Google Plus API. In the Google Developer console under Credentials for my Cliend Id for Web Application.
I add the following redirect uri: http://localhost:50883/oauth/add_oauth_token
When I run my application I get the following Error:
400. That’s an error.
Error: redirect_uri_mismatch
Application: SID3
You can email the developer of this application at: carlosmoralesdiego#gmail.com
The redirect URI in the request: http://localhost:55404/authorize/ did not match a registered redirect URI.
Learn more
Request Details
from_login=1
response_type=code
scope=https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
access_type=offline
redirect_uri=http://localhost:55404/authorize/
as=-45fed094242eac62
pli=1
client_id=389029010035-knoo3a2445r77pirc06h8fhgdc5q0dsv.apps.googleusercontent.com
authuser=0
hl=es
So for any reason google changes my redirect uri to the port 55404, why?. Thanks so much and regards
This is the code:
protected void LoginToGooglePlus(object sender, ImageClickEventArgs e)
{
PlusService service = null;
UserCredential credential = null;
string[] scopes = new string[] {PlusService.Scope.PlusLogin, PlusService.Scope.UserinfoEmail,
PlusService.Scope.UserinfoProfile};
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
try
{
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credentials;
using (var stream = new FileStream("C:\\Users\\Usuario\\Documents\\Visual Studio 2010\\Projects\\WebApplication1\\WebApplication1\\client_secret.json", FileMode.Open, FileAccess.Read))
{
credentials= GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, scopes, Environment.UserName, CancellationToken.None, new FileDataStore("WebApplication1")).Result;
}
}
catch (Exception ex)
{
//If the user hits cancel you wont get access.
if (ex.InnerException.Message.IndexOf("access_denied") != -1)
{
Console.WriteLine("User declined access");
Console.ReadLine();
return;
}
else
{
Console.WriteLine("Unknown Authentication Error:" + ex.Message);
Console.ReadLine();
return;
}
}
// Now we create a Google service. All of our requests will be run though this.
service = new PlusService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "SID3",
});
}
The json file which I use it's this one:
{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret":"jpkVDaaMjlLCHGh67DJ9Zv19","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"389029010035-knoo3a2445r77pirc06h8fhgdc5q0dsv#developer.gserviceaccount.com","redirect_uris":["http://localhost:50880/Paneles.aspx"],"client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/389029010035-knoo3a2445r77pirc06h8fhgdc5q0dsv#developer.gserviceaccount.com","client_id":"389029010035-knoo3a2445r77pirc06h8fhgdc5q0dsv.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}
I just solved the same problem simply by changing DataStore to null.
credentials= GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
scopes,
Environment.UserName,
CancellationToken.None,
null // <-- here
).Result;
I don't know why but it stops the random port shenanigans.
I should also add, that in my case, it just solved one problem since I needed to publish my app online, where this solution was just wrong. If anyone needs the online version (asp.net 4.5 MVC 5) you can use the code here.
API redirect URL and your application Redirect URL Should be same....
For example (if your application URL Like this localhost:55404/authorize/ and need to add same URL in API Redirect URL localhost:55404/authorize/ )
I've spent some time over the last few days trying to implement a feature for my web application. The feature should add new events to a users google calendar while they are offline. I read the Google OAuth2 documentation for web server applications and seem to understand the basics of it. I created a link to authorize the application for offline access:
<a target='_blank' href='https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Ftasks&response_type=code&client_id=<MY CLIENT ID>&access_type=offline&redirect_uri=http%3A%2F%2Flocalhost:49949%2Foauth2callback.aspx'>Grant Tasks Permission</a>
If the user accepts then I capture the refresh token at the redirect uri like this:
private static OAuth2Authenticator<WebServerClient> _authenticator;
protected void Page_Load(object sender, EventArgs e)
{
if (HttpContext.Current.Request["code"] != null)
{
_authenticator = CreateAuthenticator();
_authenticator.LoadAccessToken();
}
Response.Write("Refresh Token: " + _authenticator.State.RefreshToken);
}
private OAuth2Authenticator<WebServerClient> CreateAuthenticator()
{
var provider = new WebServerClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "<MY CLIENT ID>";
provider.ClientSecret = "<MY CLIENT SECRET>";
return new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization);
}
private IAuthorizationState GetAuthorization(WebServerClient client)
{
return client.ProcessUserAuthorization(new HttpRequestInfo(HttpContext.Current.Request));
}
For testing purposes I have been copying the refresh token to a text file for further use.
My problem is using this refresh token for offine access. I have been using this code to refresh the token:
protected void btnGetTasks_Click(object sender, EventArgs e)
{
if (_service == null)
{
_authenticator = CreateAuthenticator();
_service = new TasksService(new BaseClientService.Initializer() { Authenticator = _authenticator });
}
var cl = _service.Tasklists.List().Fetch();
}
private OAuth2Authenticator<WebServerClient> CreateAuthenticator()
{
// Register the authenticator.
var provider = new WebServerClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "<MY CLIENT ID>";
provider.ClientSecret = "<MY CLIENT SECRET>";
var authenticator = new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization);
return authenticator;
}
private IAuthorizationState GetAuthorization(WebServerClient client)
{
string scope = "https://www.googleapis.com/auth/tasks";
IAuthorizationState state = new AuthorizationState(new[] { scope });
state.RefreshToken = "<REFRESH TOKEN FROM FIRST STEP>";
var result = client.RefreshToken(state);
return client.ProcessUserAuthorization();
}
Everything seems fine at this point. When I step through the code I can see the result from client.RefreshToken(state) is true. The issue is when I call this line of code:
_service.Tasklists.List().Fetch();
It returns a (401) unauthorized error from google. I'm looking into the cause but I am not sure how to proceed and I am running short on time with this feature. Any advice would be greatly appreciated. Thanks!
Seems just the act of putting code on here always helps me figure it out a little sooner :)
It now appears this line is unnecessary:
return client.ProcessUserAuthorization();
removing that from the GetAuthorization method and just returning the state passed to RefreshToken has resolved the unauthorized error. I'll leave the question in case it stumps anyone else.
I'm having a problem with calling a web service request in C#.
The service and request are working fine in Soap UI with the option 'Authenticate Preemptively' enabled (File, Preferences, HTTP Settings). Without this setting enabled the service returns a 'Java.Lang.NullPointerException'.
The problem I'm having is that I do not know how to enable this setting in a C# context.
I have a .NET 3.5 class library which holds a so called service reference to the specific service. This is a simple code snippet;
try
{
CatalogService.CatalogChangeClient service = new CatalogService.CatalogChangeClient();
service.ClientCredentials.UserName.UserName = "fancydress";
service.ClientCredentials.UserName.Password = "47fda9cb4b51a9e";
service.ClientCredentials.SupportInteractive = true;
ProductUpdate[] products = new ProductUpdate[1];
products[0] = new ProductUpdate();
products[0].ProductCode = "00001";
products[0].ProductDescription = "TestProduct";
string result = service.UpdateProducts(products);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
Update after first reply.
The CatalogService.CatalogChangeClient class seems to implement the WCF abstract class
System.ServiceModel.ClientBase<TChannel>
End Update
Could anyone help me set this property?
You could try and override the GetWebRequest method from your generated client stub. I have used this once and that solved my problem.
Look at the following URL:
http://www.eggheadcafe.com/community/wcf/18/10056093/consuming-webservices-and-http-basic-authentication.aspx
Scroll a bit down.
Here's the code from the link:
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request;
request = (HttpWebRequest)base.GetWebRequest(uri);
if (PreAuthenticate)
{
NetworkCredential networkCredentials =
Credentials.GetCredential(uri, "Basic");
if (networkCredentials != null)
{
byte[] credentialBuffer = new UTF8Encoding().GetBytes(
networkCredentials.UserName + ":" +
networkCredentials.Password);
request.Headers["Authorization"] =
"Basic " + Convert.ToBase64String(credentialBuffer);
}
else
{
throw new ApplicationException("No network credentials");
}
}
return request;
}