cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
4871
Views
0
Helpful
12
Replies

Duo Admin API - admin/v2/logs/authentication

Carl_Cherry
Level 1
Level 1

Has anyone use the V2 version of the Logs REST request: i.e. /admin/v2/logs/authentication

The documentation around how to format the next_offset for this request is not detailed enough and there are no examples.

12 Replies 12

DuoKristina
Cisco Employee
Cisco Employee

Sorry about that. The paging for the auth logs is a bit different than the paging for other endpoints (described in the Response Paging section).

The first GET on the authentication logs will give you next_offset information with two values, a timestamp and a log event transaction id (txid in the event output).

At the next API call, pass in both the timestamp and ID information as next_offset.

Here are examples using the Duo Python API client:

My first authlogs request; I ask for five records:

kristina:duo_client_python kristina$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST --path /admin/v2/logs/authentication mintime=1516045230000 maxtime=1547563184847 limit=5 --method GET
200 OK
{
    "response": {
        "authlogs": [
            {blah blah five records of info
                }
            }
        ], 
        "metadata": {
            "next_offset": [
                "1547486297000", 
                "5bea5c1e-682c-4f1d-b3f0-75fd31385bd5"
            ], 
            "total_objects": 2233
        }
    }, 
    "stat": "OK"

The next_offset values are 1547486297000 (timestamp) and 5bea5c1e-682c-4f1d-b3f0-75fd31385bd5 (txid). So, I pass BOTH of those into my request for the next five records:

kristina:duo_client_python kristina$ python -m duo_client.client --ikey $IKEY --skey $SKEY --host $HOST --path /admin/v2/logs/authentication mintime=1516045230000 maxtime=1547563184847 limit=5 next_offset=1547486297000 next_offset=5bea5c1e-682c-4f1d-b3f0-75fd31385bd5 --method GET
200 OK
{
    "response": {
        "authlogs": [
            {blah blah blah five more records
                }
            }
        ], 
        "metadata": {
            "next_offset": [
                "1547475232000", 
                "7854c48b-ae46-45dd-a933-0c02699a9db7"
            ], 
            "total_objects": 2233
        }
    }, 
    "stat": "OK"

That response has new next_offset values, which I pass into the next call, rinse, repeat.

Hope that helps!

Duo, not DUO.

Carl_Cherry
Level 1
Level 1

Thanks for the info - the documentation for this request has “allow multiple” set to No. I would suggest reviewing this documentation for accuracy and adding a sample request.

Carl_Cherry
Level 1
Level 1

I wish we were using the Python library - unfortunately I’m locked into using the Ruby client. From what I see the Ruby client does not handle duplicate parameters since it expects the params to be a hash object.

To clarify, you mean the Duo API Ruby client from https://github.com/duosecurity/duo_api_ruby?

Duo, not DUO.

Carl_Cherry
Level 1
Level 1

Yes - this is what we are using in our solution.

Carl_Cherry
Level 1
Level 1

I have modified my local copy and here is what I get now - on the second request - invalid signature. I have modified the logging so you can see the exact URI along with what is created as the “canon”

Here are the logs from the request. Now to be fair, the first request gives me 2 records and says that there are only 2 records - so perhaps I am supposed to stop at this point? The documentation indicates that we should continue calling for more records as long as the metadata has next_offset values - so that is what I am doing.

First Request - success

Jan 16 19:22:55 cyclops-9218 cloudlogshipper[26859]: esduo: https://■■■■/admin/v2/logs/authentication?limit=100&maxtime=1547666575000&mintime=1531690822000&sort=ts%3Aasc
Jan 16 19:22:55 cyclops-9218 cloudlogshipper[26859]: esduo: sign the request
Jan 16 19:22:55 cyclops-9218 cloudlogshipper[26859]: esduo: canon: Wed, 16 Jan 2019 19:22:55 +0000#012GET#012■■■■#012/admin/v2/logs/authentication#012limit=100&maxtime=1547666575000&mintime=1531690822000&sort=ts%3Aasc
Jan 16 19:22:59 cyclops-9218 cloudlogshipper[26859]: esduo: resp.code is 200, resp.body is {“response”: {“authlogs”: [{“access_device”: {“ip”: “72.219.164.28”, “location”: {“city”: “San Clemente”, “country”: “United States”, “state”: “California”}}, “application”: {“key”: “■■■■”, “name”: “portal”}, “auth_device”: {“ip”: null, “location”: {“city”: null, “country”: null, “state”: null}, “name”: “226-338-7323”}, “event_type”: “enrollment”, “factor”: “sms_passcode”, “reason”: null, “result”: “success”, “timestamp”: 1547242195, “txid”: “42c9b072-9016-423d-8759-5fc6abdcf88a”, “user”: {“key”: “■■■■”, “name”: “carl”}}, {“access_device”: {“ip”: “0.0.0.0”, “location”: {“city”: null, “country”: null, “state”: null}}, “application”: {“key”: “■■■■”, “name”: “macOS”}, “auth_device”: {“ip”: “72.219.164.28”, “location”: {“city”: “San Clemente”, “country”: “United States”, “state”: “California”}, “name”: “226-338-7323”}, “event_type”: “authentication”, “factor”: “duo_push”, “reason”: “user_approved”, “result”: “success”, “timestamp”: 1547242569, “txid”: “02dcf416-1f91-4ea7-a34b-8ca019ae5434”, “user”: {“key”: “■■■■”, “name”: “carl”}}], “metadata”: {“next_offset”: [“1547242569000”, “02dcf416-1f91-4ea7-a34b-8ca019ae5434”], “total_objects”: 2}}, “stat”: “OK”}

Second Request - failure

Jan 16 19:22:59 cyclops-9218 cloudlogshipper[26859]: esduo: https://■■■■/admin/v2/logs/authentication?limit=100&maxtime=1547666579000&mintime=1547666579000&next_offset=1547242569000&next_offset=02dcf416-1f91-4ea7-a34b-8ca019ae5434&sort=ts%3Aasc
Jan 16 19:22:59 cyclops-9218 cloudlogshipper[26859]: esduo: canon: Wed, 16 Jan 2019 19:22:59 +0000#012GET#012■■■■#012/admin/v2/logs/authentication#012limit=100&maxtime=1547666579000&mintime=1547666579000&next_offset=1547242569000&next_offset=02dcf416-1f91-4ea7-a34b-8ca019ae5434&sort=ts%3Aasc
Jan 16 19:23:05 cyclops-9218 cloudlogshipper[26859]: esduo: send the request
Jan 16 19:23:05 cyclops-9218 cloudlogshipper[26859]: esduo: resp.code is 401, resp.body is {“code”: 40103, “message”: “Invalid signature in request credentials”, “stat”: “FAIL”}

Carl_Cherry
Level 1
Level 1

Here is the code I changes - it is not very elegant but I am not a Ruby guy. Mostly C++ and Python.

private
def encode_params(params)
	return "" if params.nil?
	if params.is_a?(Hash) then
		params.sort.map do |k,v|
			URI::encode(k.to_s, @@encode_regex) + "=" + URI::encode(v.to_s, @@encode_regex)
		end.join("&")
	else
		# In this case params is an array of hashes - this is needed since
		# some of the requests are able to have repeated parameters.
		# NOTE - if sending in an array of hashes they MUST be already sorted.
                    result = ""
		params.each do |param|
			param = Hash(param)
			param.each do |k,v|
				result += URI::encode(k.to_s, @@encode_regex) + "=" + URI::encode(v.to_s, @@encode_regex)
			end
			result += "&"
		end
		return result.chomp("&")
	end
end

Thanks! I’ll get someone who knows Ruby better than me (which is really anyone who knows Ruby at all) to take a look.

Duo, not DUO.

biggsb
Level 1
Level 1

FYI the Perl module has the same issue (can’t handle multiple of the same named params in canonicalize_params()).

kang1
Level 1
Level 1

FYI the Python module… has this problem too if you call it from duo_client.Admin instead of duo_client.client (i.e. if you write a program)

In particular the call https://github.com/duosecurity/duo_client_python/blob/c69c3■■■■80fadb23eb49ec9/duo_client/admin.py#L322 def get_authentication_log(self, api_version=1, **kwargs): takes keyword args and you cannot repeat the same keyword twice (SyntaxError: keyword argument repeated) and not give it a list as this will fail canonicalization here:

  File "/home/test/project/env/site-packages/duo_client/client.py", line 49, in <genexpr>
    for val in sorted(six.moves.urllib.parse.quote(val, "~") for val in vals):
  File "/usr/lib64/python2.7/urllib.py", line 1296, in quote
    if not s.rstrip(safe):
AttributeError: 'int' object has no attribute 'rstrip'

If you set the timestamp as string, then this can work though, by calling it as such:

get_authentication_log(api_version=2, sort="ts:asc", mintime=31337, next_offset=[[str(31337), txid]], ...) (it also works if you grab it from the metadata reply “as string”

NOTE: I don’t get all results if I don’t use sort="ts:asc" for example. I’m guessing Duo sends back results unordered and they might be outside the mintime:maxtime range, but that’s really confusing if that’s the case. In fact, that’s unusable and if that theory is true (I hope it’s not then sort should be made required, or have a default.

DuoKristina
Cisco Employee
Cisco Employee

We’re publishing updates to more of our API client demos now (perl, ruby, and c#) to address this. Thanks for the feedback!

Duo, not DUO.

lvincent7
Level 1
Level 1

I would like to add another question related to this topic. In this documentation: Accounts API | Duo Security

it did mention to utilized Admin API to access Accounts API methods. If so, is it possible to retrieve Accounts authentication logs using the Admin API credentials? Based on the authlogs documentation: Duo Admin API | Duo Security I don’t see a query parameter to filter by Account ID. I could be wrong in this one but need some enlightenment.

Thanks!

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community:

Quick Links