Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
Hi, I have a .NET web app from which I want to call the Qlik Sense QRS APIs: let's take the call to /qrs/about
as an example. The calls are managed through this Client and the GetWebRequest
method inside it:
public class CookieAwareWebClient : WebClient, ICookieAwareWebClient {
private TimeSpan _timeout;
private CookieContainer _cookieContainer;
private QSIdentityCredentials _networkCredentials;
public CookieAwareWebClient(TimeSpan timeout, ICredentials networkCredential) {
_cookieContainer = new CookieContainer();
_timeout = timeout;
UseDefaultCredentials = networkCredential == null;
if (!UseDefaultCredentials)
Credentials = networkCredential;
//_networkCredentials = new QSIdentityCredentials() { Username = ((NetworkCredential)networkCredential).UserName, Password = ((NetworkCredential)networkCredential).Password };
Encoding = Encoding.UTF8;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
if (request == null)
throw new NullReferenceException("request");
request.CookieContainer = _cookieContainer;
request.Timeout = (int)_timeout.TotalMilliseconds;
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
SetCredentials(ref request, new QSIdentityCredentials() { Username = ((NetworkCredential)Credentials).UserName, Password = ((NetworkCredential)Credentials).Password });
return request;
}
The authentication part is handled through the SetCredentials
method:
public static void SetCredentials(ref HttpWebRequest request, QSIdentityCredentials credentials) {
if (credentials == null) {
request.UseDefaultCredentials = true;
return;
}
request.UseDefaultCredentials = false;
if (!credentials.Username.Contains("\\")) {
var hdCredentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials.Username + ":" + credentials.Password));
request.Headers.Add("Authorization", "Basic " + hdCredentials);
request.Credentials = new NetworkCredential(credentials.Username, credentials.Password);
return;
}
var hdCredentials2 = Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials.Username.Split('\\')[1] + ":" + credentials.Password));
request.Headers.Add("Authorization", "Basic " + hdCredentials2);
var domain = credentials.Username.Split('\\').First();
var username = credentials.Username.Split('\\').Last();
request.Credentials = new NetworkCredential(username, credentials.Password, domain);
}
When I do not pass credentials to the method and thus use useDefaultCredentials = true
, the calls succeed because the IIS user is used correctly. However, when I try to use another user so setting useDefaultCredentials = false (which has the rootAdmin
role in Qlik Sense), the calls always return a 401 unauthorized error. What am I doing wrong?
I resolved in this way:
public static void SetCredentials(ref HttpWebRequest request, NetworkCredential credentials) {
if (credentials == null) {
request.UseDefaultCredentials = true;
return;
}
request.UseDefaultCredentials = false;
var credentialCache = new CredentialCache();
if (credentials.UserName.Contains("\\"))
{
credentials.Domain = credentials.UserName.Split('\\').First();
credentials.UserName = credentials.UserName.Split('\\').Last();
}
credentialCache.Add(new Uri($"{request.Address.Scheme}://{request.Host}"), "ntlm", credentials);
request.Credentials = credentialCache;
}
Getting that flow to work can be rather tricky. It's easy to get something wrong when trying to set up all the headers and credentials and everything. I have this library for doing this type of connections that might be of interest to you:
https://www.nuget.org/packages/QlikSenseRestClient/
With that library you can set up your connection like this:
var restClient = new RestClient(url);
restClient.AsNtlmUserViaProxy(new NetworkCredential(username, password));
var rsp = restClient.Get<JToken>("/qrs/about");
If you want to look at how the credential part is set up in that library, then this part is probably the most relevant:
Seems good, but my project is old and uses .net framework 4.5. I can't import that
I resolved in this way:
public static void SetCredentials(ref HttpWebRequest request, NetworkCredential credentials) {
if (credentials == null) {
request.UseDefaultCredentials = true;
return;
}
request.UseDefaultCredentials = false;
var credentialCache = new CredentialCache();
if (credentials.UserName.Contains("\\"))
{
credentials.Domain = credentials.UserName.Split('\\').First();
credentials.UserName = credentials.UserName.Split('\\').Last();
}
credentialCache.Add(new Uri($"{request.Address.Scheme}://{request.Host}"), "ntlm", credentials);
request.Credentials = credentialCache;
}