22Example of Duo Auth API uaer authentication with synchronous request/response
33"""
44
5+ from argparse import ArgumentParser , Namespace
56import duo_client
6- import sys
77import getpass
88
9- from pprint import pprint
109
10+ def _get_arg (args : Namespace , name : str , prompt : str , secure = False ):
11+ """Read arg from CLI flags or stdin, using getpass when sensitive information should not be echoed to tty"""
12+ value = getattr (args , name )
13+ if value is not None :
14+ return value
1115
12- argv_iter = iter (sys .argv [1 :])
13-
14-
15- def _get_next_arg (prompt , secure = False ):
16- """Read information from STDIN, using getpass when sensitive information should not be echoed to tty"""
17- try :
18- return next (argv_iter )
19- except StopIteration :
20- if secure is True :
21- return getpass .getpass (prompt )
22- else :
23- return input (prompt )
16+ if secure is True :
17+ return getpass .getpass (prompt )
18+ else :
19+ return input (prompt )
2420
2521
26- def prompt_for_credentials () -> dict :
22+ def prompt_for_credentials (args : Namespace ) -> dict :
2723 """Collect required API credentials from command line prompts
2824
2925 :return: dictionary containing Duo Auth API ikey, skey and hostname strings
3026 """
3127
32- ikey = _get_next_arg ( 'Duo Auth API integration key ("DI..."): ' )
33- skey = _get_next_arg ( 'Duo Auth API integration secret key: ' , secure = True )
34- host = _get_next_arg ( 'Duo Auth API hostname ("api-....duosecurity.com"): ' )
35- username = _get_next_arg ( 'Duo Username: ' )
28+ ikey = _get_arg ( args , "ikey" , 'Duo Auth API integration key ("DI..."): ' )
29+ skey = _get_arg ( args , "skey" , 'Duo Auth API integration secret key: ' , secure = True )
30+ host = _get_arg ( args , "api_host" , 'Duo Auth API hostname ("api-....duosecurity.com"): ' )
31+ username = _get_arg ( args , "username" , 'Duo Username: ' )
3632
3733 return {"USERNAME" : username , "IKEY" : ikey , "SKEY" : skey , "APIHOST" : host }
3834
3935
4036def main ():
4137 """Main program entry point"""
4238
43- inputs = prompt_for_credentials ()
39+ parser = ArgumentParser ()
40+ parser .add_argument ("--ipaddr" , type = str )
41+ parser .add_argument ("--ikey" , type = str )
42+ parser .add_argument ("--skey" , type = str )
43+ parser .add_argument ("--api-host" , type = str )
44+ parser .add_argument ("--username" , type = str )
45+ parser .add_argument ("--verbose" , action = "store_true" , default = False )
46+ args = parser .parse_args ()
47+
48+ inputs = prompt_for_credentials (args )
4449
4550 auth_client = duo_client .Auth (
4651 ikey = inputs ['IKEY' ],
@@ -64,15 +69,20 @@ def main():
6469
6570 # Execute pre-authentication for given user
6671 print (f"\n Executing pre-authentication for { inputs ['USERNAME' ]} ..." )
67- pre_auth = auth_client .preauth (username = inputs ['USERNAME' ])
72+ pre_auth = auth_client .preauth (username = inputs ['USERNAME' ], ipaddr = args .ipaddr )
73+ if args .verbose :
74+ print (f"{ pre_auth = } " )
6875
6976 if pre_auth ['result' ] == "auth" :
7077 try :
7178 # User exists and has an MFA device enrolled
7279 print (f"Executing authentication action for { inputs ['USERNAME' ]} ..." )
7380 # "auto" is selected for the factor in this example, however the pre_auth['devices'] dictionary
7481 # element contains a list of factors available for the provided user, if an alternate method is desired
75- auth = auth_client .auth (factor = "auto" , username = inputs ['USERNAME' ], device = "auto" )
82+ auth = auth_client .auth (factor = "auto" , username = inputs ['USERNAME' ], device = "auto" , ipaddr = args .ipaddr )
83+ if args .verbose :
84+ print (f"{ auth = } " )
85+
7686 print (f"\n { auth ['status_msg' ]} " )
7787 except Exception as e_str :
7888 print (e_str )
0 commit comments