Nike-Inc / gimme-aws-creds

A CLI that utilizes Okta IdP via SAML to acquire temporary AWS credentials
Apache License 2.0
919 stars 262 forks source link

Duo push and passcode support in Okta is too simplistic #427

Open LucidityCrash opened 11 months ago

LucidityCrash commented 11 months ago

The if statement in PR #217 is too simplistic, it also assumes that the user doesn't have any other MFA methods configured ... Google Authenticator for example

Expected Behavior

push and passcode options for DUO via Okta should be available if more than 1 MFA is configured.

Current Behavior

if another MFA token eg Google Authenticator is added to the Okta account along side Duo then only web: Duo is available.

Possible Solution

iterate over the list of factors and only add push & passcode to the list if provider == DUO and factortype push & passcode are not present.

Steps to Reproduce (for bugs)

Add Google Authenticator and Duo as MFA methods on an Okta account run gimmie-aws-creds after authentication Pick a factor shows [0] token:software:totp( GOOGLE ) : xxx@xxxxxxxxx [1] web: Duo

Attaching a debugger you can see that the factors variable is :

[
{'id': 'xxxxxxxxxxxxxxxxxxxx', 'factorType': 'token:software:totp', 'provider': 'GOOGLE', 'vendorName': 'GOOGLE', 'profile': {...}, '_links': {...}}, 
{'id': 'xxxxxxxxxxxxxxxxxxxx', 'factorType': 'web', 'provider': 'DUO', 'vendorName': 'DUO', 'profile': {...}, '_links': {...}}
]

so the len of factors is 2 so the code block to add push and passcode is never executed.

Your Environment

LucidityCrash commented 11 months ago

a 5 minute hack give a not exactly pretty patch replace lines 802 to 808 in okta_classic.py with :

        only_web = True
        i = 0
        j = 0
        returned_factors = len(factors)
        while i < returned_factors:
            if factors[i].get('provider') == 'DUO' and factors[i].get('factorType') != 'web':
                only_web = False
                break
            i += 1  

        while j < returned_factors:
            if factors[j].get('provider') == 'DUO' and factors[j].get('factorType') == 'web' and only_web:
                push = copy.deepcopy(factors[j])
                push['factorType'] = "push"
                factors.append(push)
                passcode = copy.deepcopy(factors[j])
                passcode['factorType'] = "passcode"
                factors.append(passcode)
            j += 1

But it seems to do whats needed ... the first while loop is to manage what the len(factors) = 1 check used to do and make sure that if Okta start returning push and passcode as valid Duo methods we don't add them a 2nd time (limitation here is it assumes that if they do change this they will start returning all 3 and not just add one of push or passcode), 2nd loop adds push and passcode if only duo web is present.