cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
2906
Views
4
Helpful
8
Replies

I can not replicate the basic auth header that the example shows

VictorioBerra
Level 1
Level 1

I copied and pasted the Authorization python verbatim, and I plugged in the sample parameters identically to the examples and I still can not get an exact basic auth header match that the examples show. This is very frustrating game of trial and error. Bottom of this page: Duo Admin API | Duo Security

This is Python 2.7.12

import base64, email, hmac, hashlib, urllib

def sign(method, host, path, params, skey, ikey):
    """
    Return HTTP Basic Authentication ("Authorization" and "Date") headers.
    method, host, path: strings from request
    params: dict of request parameters
    skey: secret key
    ikey: integration key
    """

    # create canonical string
    now = "Tue, 21 Aug 2012 17:29:18 -0000"
    canon = [now, method.upper(), host.lower(), path]
    args = []
    for key in sorted(params.keys()):
        val = params[key]
        if isinstance(val, unicode):
            val = val.encode("utf-8")
        args.append(
            '%s=%s' % (urllib.quote(key, '~'), urllib.quote(val, '~')))
    canon.append('&'.join(args))
    canon = '\n'.join(canon)

    # sign canonical string
    sig = hmac.new(skey, canon, hashlib.sha1)
    auth = '%s:%s' % (ikey, sig.hexdigest())

    # return headers
    print ("RElXSjhYNkFFWU9SNU9NQzZUUTE6MmQ5N2Q2MTY2MzE5NzgxYjVhM2EwN2FmMzlkMzY2ZjQ5MTIzNGVkYw==")
    print (base64.b64encode(auth))

params = {
  "realname": "First Last",
  "username": "root"
}

sign("POST", "■■■■■■■■■■■■■■■■■■■■■■■■■■■■", "/admin/v1/users", params, "Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep", "■■■■■■■■■■■■■■■■■■■■")

My output:

RElXSjhYNkFFWU9SNU9NQzZUUTE6MmQ5N2Q2MTY2MzE5NzgxYjVhM2EwN2FmMzlkMzY2ZjQ5MTIzNGVkYw==
RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==

You can see, they don’t match.

The only code I changed was to hardcode the date as shown in the examples, and added print statements.

8 Replies 8

jpazniokas1
Level 1
Level 1

You’re probably long gone… but did you ever get around this? I’m experiencing the same issue, in my own hand-rolled C# code. Your output looks familiar: I may have produced that with one of my myriad tweaks and fiddles.

I abandoned the Python solution. I am a little annoyed that Duo doesn’t reply to these forum posts and help the community.

I rolled a simple solution that works in Postman. https://github.com/VictorioBerra/duo-v1-postman-signer

I know that might not help you much but you might try to read my code (its very small) or try a different language.

nickmkk
Level 1
Level 1

Was anyone able to get a C# example working? I’m having the same issue where I can’t reproduce the hash from the example.

I have the following implementation and I get the same result as in @VictorioBerra’s comment.

public string GetAuthHeaderValue(CustomerDuoSettings customerSettings, HttpWebRequest request)
{
    var filters = request.RequestUri.Query.TrimStart('?').Split('&');
    var sortedFilters = filters.OrderBy(x => x.Split('=').FirstOrDefault()).ToList();

    var canonicalString = new List<string>
    {
        request.Headers.Get("Date"), //date
        request.Method.ToUpper(), //method
        customerSettings.DuoHostname.ToLower(), //host
        request.RequestUri.LocalPath, //path
        string.Join("&", sortedFilters), //params, lexicographically sorted by key
    };

    var sig = HmacSign(customerSettings.DuoSecretKey, string.Join("\n", canonicalString));

    var credentials = $"{customerSettings.DuoIntegrationKey}:{sig}";
    var basicAuthCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));
    return $"Basic {basicAuthCredentials}";
}

public static string HmacSign(string key, string input)
{
    using (var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(key)))
    {
        byte[] bytes = Encoding.ASCII.GetBytes(input);
        hmac.ComputeHash(bytes);
        return BitConverter.ToString(hmac.Hash).Replace("-", "").ToLower();
    }
}

This is my unit test for reference

[Fact]
public void GetAuthHeaderValue_ReturnsTheExpectedAuthToken()
{
    var service = new DuoClient();
    var settings = new CustomerDuoSettings
    {
        DuoEnabled = true,
        DuoHostname = "■■■■■■■■■■■■■■■■■■■■■■■■■■■■",
        DuoIntegrationKey = "■■■■■■■■■■■■■■■■■■■■",
        DuoSecretKey = "Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep"
    };
    var request = (HttpWebRequest)WebRequest.Create("https://■■■■■■■■■■■■■■■■■■■■■■■■■■■■/admin/v1/users?realname=First%20Last&username=root");
    request.Method = WebRequestMethods.Http.Post;

    // this is a hacky way to set the date header to a specific value without .net controlling the string format
    var priMethod = request.Headers.GetType().GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);
    priMethod.Invoke(request.Headers, new[] { "Date", "Tue, 21 Aug 2012 17:29:18 -0000" });

    var authHeader = service.GetAuthHeaderValue(settings, request);
    Assert.Equal("Basic RElXSjhYNkFFWU9SNU9NQzZUUTE6MmQ5N2Q2MTY2MzE5NzgxYjVhM2EwN2FmMzlkMzY2ZjQ5MTIzNGVkYw==", authHeader);
}

The expected value as shown here Duo Admin API | Duo Security is
RElXSjhYNkFFWU9SNU9NQzZUUTE6MmQ5N2Q2MTY2MzE5NzgxYjVhM2EwN2FmMzlkMzY2ZjQ5MTIzNGVkYw==

However, i’m getting back
RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==

Yeah, our example hash is not correct. @nickmkk if you go ahead and try to hit the API with your auth header does it work?

Duo, not DUO.

Amy2
Level 5
Level 5

Hi everyone, I apologize that I missed your initial posts and failed to reply sooner. I’m unable to help with this myself, as I’m not familiar with using our API, but I’ll surface it with our team and see if someone can assist. You might also have luck contacting the Duo Support team for assistance with this.

nickmkk
Level 1
Level 1

No unfortunately it just returns a 401 unauthorized result code. I just looked at the fiddler response though and It may just be a bad integration key.
{"code": 40102, "message": "Invalid integration key in request credentials", "stat": "FAIL"}

My integration and secret keys look correct though, are the “web sdk” application keys not valid for calling the admin api?

Update: I just looked through the documentation and found the part where I have to configure an “Admin API” application in duo and use the keys from that. Sorry for the confusion.

Yep, only the Admin API integration can use the Admin API endpoints. Looks like you caught that. DId it work once you switched the integration type?

Duo, not DUO.

nickmkk
Level 1
Level 1

I haven’t tested it since I was using a trial account which doesn’t have that option. We allow our customers to configure DUO MFA for logging in to our site so we’re not typically using our own keys. The plan was to integrate DUO into an MFA security audit report that we provide to our customers so they can see which users need to configure MFA. However, the separate app requirement means we’d need to require additional setup configuration for DUO so we may abandon this feature. Thanks a bunch for your time though.

Quick Links