HttpClient fails to authenticate via NTLM on the second request when using the Sharepoint REST API on Windows Phone 8.1 - sharepoint

Sorry for the long title, but it seems to be the best summary based on what I know so far.
We’re currently working on a Universal App that needs to access some documents on a Sharepoint server via the REST API using NTLM Authentication, which proves to be more difficult than it should be. While we were able to find workarounds for all problems (see below), I don’t really understand what is happening and why they are even necessary.
Somehow the HttpClient class seems to behave differently on the phone and on the PC. Here’s what I figured out so far.
I started with this code:
var credentials = new NetworkCredential(userName, password);
var handler = new HttpClientHandler()
{
Credentials = credentials
};
var client = new HttpClient(handler);
var response = await client.GetAsync(url);
This works fine in the Windows app, but it fails in the Windows Phone app. The server just returns a 401 Unauthorized status code.
Some research revealed that you need to provide a domain to the NetworkCredential class.
var credentials = new NetworkCredential(userName, password, domain);
This works on both platforms. But why is the domain not required on Windows?
The next problem appears when you try to do multiple requests:
var response1 = await client.GetAsync(url);
var response2 = await client.GetAsync(url);
Again, this works just fine in the Windows app. Both requests return successfully:
And again, it fails on the phone. The first request returns without problems:
Strangely any consecutive requests to the same resource fail, again with status code 401.
This problem has been encountered before, but there doesn’t seem to be a solution yet.
An answer in the second thread suggests that there’s something wrong with the NTLM handshake. But why only the second time?
Also, it seems to be a problem of the HttpClient class, because the following code works without problems on both platforms:
var request3 = WebRequest.CreateHttp(url);
request3.Credentials = credentials;
var response3 = await request3.GetResponseAsync();
var request4 = WebRequest.CreateHttp(url);
request4.Credentials = credentials;
var response4 = await request4.GetResponseAsync();
So the problem only appears:
on Windows Phone. The same code in a Windows App works.
when connecting to Sharepoint. Accessing another site with NTLM authentication works on both platforms.
when using HttpClient. Using WebRequest, it works.
So while I'm glad that I at least found some way to make it work, I’d really like to know what’s so special about this combination and what could be done to make it work?

Hi Daniel at the same problem when I do my sync, because windows phone had a lot of problems with cache, finallt I could solve with add headers.
Also I think so it's good idea that you use the timeout because it's a loooong response you can wait a lot of time... And the other good way to work it's use "using", it's similar that use ".Dispose()". Now I show you the code:
var request3 = WebRequest.CreateHttp(url);
request3.Credentials = credentials;
request.ContinueTimeout = 4000; //4 seconds
//For solve cache problems
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
using(httpWebResponse response3 = (httpWebResponse) await request3.GetResponseAsync()){
if (response3.StatusCode == HttpStatusCode.OK)
{
//Your code...
}
}
var request4 = WebRequest.CreateHttp(url);
request4.Credentials = credentials;
request.ContinueTimeout = 4000; //4 seconds
//For solve cache problems
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
using(httpWebResponse response4 = (httpWebResponse) await request4.GetResponseAsync()){
if (response4.StatusCode == HttpStatusCode.OK)
{
//Your code...
}
}
I wait that my code can help you. Thanks and good luck!

Related

Dotnet Core 2.0.3 Migration | Encoding unable to translate bytes [8B]

Im not sure if this should just go direct to the github but I thought id check here first if anyone has encountered this issue before.
I recently have upgraded one of my apps to use dot net 2.0.3 From 1.1.4.
Everything works fine locally but when I deploy to my app service in azure I get the following exception.
System.Text.DecoderFallbackException: Unable to translate bytes [8B] at index 1 from specified code page to Unicode.
The code that calls it is a httpclient that talks between the apps.
public async Task<T1> Get<T1>(string url, Dictionary<string, string> urlParameters = null) where T1 : DefaultResponse, new()
{
var authToken = _contextAccessor.HttpContext.Request.Cookies["authToken"];
using (var client = new HttpClient().AcceptJson().Acceptgzip().AddAuthToken(authToken))
{
var apiResponse = await client.GetAsync(CreateRequest(url, urlParameters));
T1 output;
if (apiResponse.IsSuccessStatusCode)
{
output = await apiResponse.Content.ReadAsAsync<T1>();
//output.Succeeded = true;
}
else
{
output = new T1();
var errorData = GlobalNonSuccessResponseHandler.Handle(apiResponse);
output.Succeeded = false;
output.Messages.Add(errorData);
}
return output;
}
}
public static HttpClient AcceptJson(this HttpClient client)
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
public static HttpClient Acceptgzip(this HttpClient client)
{
// Commenting this out fixes the issue.
//client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("deflate"));
return client;
}
public static HttpClient AddAuthToken(this HttpClient client, string authToken)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
return client;
}
Im a bit stumped as to whats going on.
So I have 2 apps which we call client and server from now on.
Client uses the above code to talk to the server.
Locally this is fine on azure not so, this all worked fine before upgrading.
So I setup the client locally to talk to the server on azure I was able to replicate the issue.
I had a look at the response in fiddler and it is able to correctly decode it.
If anyone has any idea where I should look and has seen it before any info would be great :D.
UPDATE 1
So after some more digging I decided to remove gzip and then everything started working.
client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
Can anyone explain this?
8B can be a second byte of multi-byte UTF8 character. The DecoderFallbackException tells that you’re interpreting the data as some other encoding. Probably Latin-1 which doesn’t have 8B character.
In fiddler, you should look at the content-type HTTP header in the response. If it says application/json or application/json; charset=utf-8, it’s probably a bug in .NET, because even without charset=utf-8 RFC 4627 says the default encoding is already UTF-8.
If it says something else, I would try changing the server so it sends the correct content-type header in the response.

Webclient 401 Unauthorized Issue on above 5 minutes of processing time

I have HttpHandler (abc.ashx), which I make a call to process (Bulk Upload) an excel file. This processing takes a certain amount of time, which is proportionate to the number of rows in the excel.
On top of this I have an Event Handler in Sharepoint which uses a WebClient to make a post service call to this handler using the code below;
NOTE: I generate an identity token using elevated privilege of Sharepoint, and use that token to impersonate to make the service call. net net I make the post service call using the identity of the System Account.
WindowsIdentity identity = null;
// Get an identity token using delegate activity of Elevated Privelleges of Sharepoint
SPHelper.ElevatedActivity(properties.Web.Site.Url, properties.Web.ServerRelativeUrl, web =>
{
identity = WindowsIdentity.GetCurrent();
});
//Impersonates the identity of System Account user, as received in token in the line above
using (identity.Impersonate())
{
// create a web client object which only increases the timeout of the web client call
var webClient = (new CustomWebClient());
webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
webClient.UseDefaultCredentials = true;
//url of the httphandler as deployed in sharepoint, and the parameters that needs to be passed
string handlerUrl =
string.Format(
properties.Web.Url.Trim('/') + "/_layouts/Handlers/abc.ashx?Table=BulkUpload&Region={0}&UserId={1}&BatchId={2}&FileUrl={3}",
region, userValue.User.LoginName, batchId, fileUrl);
}
// execute the web service call using the webclient object
webClient.DownloadString(handlerUrl));
Essentially this code is written in the Event Handler for ItemAdded and ItemUpdated event.
The issue is in the webClient which seems to give an error "system.net.webexception: the remote server returned an error: (401) unauthorized", after precisely 5 mins of processing. Hence if the excel sheet to be processed has rows less than certain number (1700), the processing happens within 5 minutes, and everything runs fine without any error. However if it has more than that, then the processing takes more than 5 minutes, and fails with the error specificed above.
The strange behaviour is it seems like a timeout issue but the error message indicates authorization issue, which does not make sense, as if there was an authorization issue, it shouldnt have worked even when the processing time was less that 5 minutes.
We have tried to find any configuration that times out in 5 minutes, however we could not find any.
Any help or suggestion on this matter is appreciated.
UPDATE: I have now tried to make this work using HttpWebRequest, and have tried a bunch of setting which might cause this timeout/issue. However still getting the same issue, where after 5 minutes of processing I am getting "system.net.webexception: the remote server returned an error: (401) unauthorized". Below is the code I have tried
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(handlerUrl);
httpWebRequest.Credentials = CredentialCache.DefaultCredentials;
httpWebRequest.UseDefaultCredentials = true;
httpWebRequest.Timeout = 600000;
httpWebRequest.ReadWriteTimeout = 600000;
httpWebRequest.ServicePoint.ConnectionLeaseTimeout = 600000;
httpWebRequest.ServicePoint.MaxIdleTime = 600000;
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.ContentLength = 0;
httpWebRequest.Method = WebRequestMethods.Http.Get;
BulkUploadProcessorResponse response = null;
var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (Stream stream = httpWebResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(stream))
{
response =
JsonHelper.JsonDeserialize<BulkUploadProcessorResponse>(sr.ReadToEnd());
}
}

How to use a proxy with the instagram-private-api for Node.js

Using a proxy with instagram-private-api.
Hi all, I spent a decent amount of time trying to figure this out and there is probably a really simple answer but I was seriously confused. When creating a session needed for the Instagram nodeJS API (private) you need a proxyUrl. I was wondering how to do / configure this? Do you need to create your own proxy server that you host?
Here is my code so far.
var Upload = require('instagram-private-api').V1;
var Client = require('instagram-private-api').V1;
var device = new Client.Device('test');
var storage = new Client.CookieFileStorage(__dirname +
'/cookies/test.json');
var photo = require('instagram-private-api').V1;
var username = 'testusername'
var password = 'testpassword'
var proxyUrl = '???'
Client.Session.create(device, storage, username, password, proxyUrl)
var Upload = require('./node_modules/instagram-private-api/client/v1/Upload.js')
var session = new Client.Session(device, storage, 'test', 'test')
Upload.photo(session, 'aaaa.jpg')
.then(function(upload) {
console.log(upload.params.uploadId);
return Media.configurePhoto(session, upload.params.uploadId, 'henlo world');
})
.then(function(medium) {
console.log(medium.params)
})
I know my code is probably seriously flawed as well, criticism is appreciated! Here's the link to the GitHub of the Node.JS wrapper mentioned. Here.
e
Hi there Benjamin,
I'm really looking forward to helping you.
As already stated, this library won't be upkept and maintained and could become irrelevant in updates to the official API.
I believe one has to set up a proxy url on their phone.

Loopback current context is null sometime

In my loopback application i am getting the current logged-in user using currentcontext as follows..
exports.getCurrentUserId =function(){
var ctx = loopback.getCurrentContext();
var accessToken = ctx && ctx.get('accessToken');
var userId = accessToken && accessToken.userId;
return userId;
}
When i am calling this method getCurrentUserId, its working fine sometime, sometimes its giving null. Its unpredictable . Please share your ideas. Thanks in advance.
versions:
loopback-connector-mongo:1.15.1
Re. getCurrentContext(), there is a plan to deprecate this method as it has been unreliable ( see discussion here).
However, there is a suggested workaround to inject Remote Context via Options.

Using AuthenticationContext & ActiveDirectoryClient with an application proxy?

Using the Azure Active Directory Graph Client API, how would I configure the underlying HttpClient to use an HttpClientHander, where I can define an authenticated application proxy?
var proxy = new WebProxy(...);
proxy.Credentials = ...;
var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true};
var auth = new AuthenticationContext(...);
var client = new ActiveDirectoryClient(...);
Or, can I not use the Graph Client behind a proxy?
Thanks
I was exploring the same problem. It took some digging, but I found a solution. Now, I realize you asked specifically how to apply an HttpClientHandler. I don't know if this can be done; however, you can apply a proxy. Here's how.
The ActiveDirectoryClient class provides a DataServiceContextWrapper property called Context, which is, not surprisingly, a wrapper to a DataServiceContext.
This is good. It reduces the problem to figuring out how to apply a proxy to the DataServiceContext class. I used some old code I had sitting around, and things pretty much exploded. This is because I used the deprecated SendingRequest event to intercept a request and apply a proxy before it goes out the door. This client is not compatible with the deprecated event.
It took a little more digging to figure out how to do it with the SendingRequest2 event; it only required a little type casting.
Do this:
var client = new ActiveDirectoryClient(...);
client.Context.SendingRequest2 += OnSendingRequest2;
// ...
static void OnSendingRequest2(object sender, SendingRequest2EventArgse)
{
var request = ((HttpWebRequestMessage)e.RequestMessage).HttpWebRequest;
request.Proxy = new WebProxy("http://myproxy:port");
}
Don't Do This: (It is deprecated and will produce an exception.)
var client = new ActiveDirectoryClient(...);
client.Context.SendingRequest += OnSendingRequest;
// ...
static void OnSendingRequest(object sender, SendingRequestEventArgs e)
{
e.Request.Proxy = new WebProxy("http://myproxy:port");
}
a bit late but i ran into the same issue.
using the code below in app.config saved my day !
<system.net>
<defaultProxy useDefaultCredentials="true" />
</system.net>

Resources