Do not input private or sensitive data. View Qlik Privacy & Cookie Policy.
Skip to main content

Announcements
Join us in Toronto Sept 9th for Qlik's AI Reality Tour! Register Now
cancel
Showing results for 
Search instead for 
Did you mean: 
alessandrovernile
Partner - Contributor II
Partner - Contributor II

Unauthorized 401 error calling QRS APIs in .NET ( no default credentials )

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?

Labels (3)
1 Solution

Accepted Solutions
alessandrovernile
Partner - Contributor II
Partner - Contributor II
Author

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;

}

View solution in original post

3 Replies
Øystein_Kolsrud
Employee
Employee

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:

https://github.com/kolsrud/qlik_rest_sdk/blob/master/Qlik.Sense.RestClient/Qlik.Sense.RestClient/Res...

alessandrovernile
Partner - Contributor II
Partner - Contributor II
Author

Seems good, but my project is old and uses .net framework 4.5. I can't import that 

alessandrovernile
Partner - Contributor II
Partner - Contributor II
Author

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;

}