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

Announcements
Join us in Bucharest on Sept 18th 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;

}