简体   繁体   English

Python + Selenium - 微软 2FA

[英]Python + Selenium - Microsoft 2FA

I'm trying to log in to Azure DevOps using Python and Selenium.我正在尝试使用 Python 和 Selenium 登录 Azure DevOps。 I can enter the username and password just fine, but I have the Microsoft Authenticator App on my phone set to give me a prompt to "approve" or "deny" when trying to log in.我可以很好地输入用户名和密码,但我手机上的 Microsoft Authenticator 应用程序设置为在尝试登录时提示我“批准”或“拒绝”。

I've tried having Selenium wait until the "No" button element on the stay signed in page is present (see screenshot below),我试过让 Selenium 等到保持登录页面上的“否”按钮元素出现(见下面的截图), 在此处输入图片说明 but it doesn't seem to like it.但它似乎并不喜欢它。 I took a look at this post which is regarding Google's Authenticator.我看了一下这篇关于谷歌身份验证器的帖子 The solution uses Google Authenticator's secret, along with pyotp.该解决方案使用 Google Authenticator 的秘密以及 pyotp。 I can't seem to find a way to get a secret from the MS Authenticator, at least for myself.我似乎无法找到从 MS Authenticator 获取秘密的方法,至少对我自己而言。 I checked out this guide, but I do not have access to Azure AD.我查看了指南,但我无权访问 Azure AD。

Here's the code I've tried to wait for the screen after 2FA:这是我尝试在 2FA 后等待屏幕的代码:

def signin(user, passwd):
    # find elements before passing information
    username = browser.find_element_by_name('loginfmt')
    username.send_keys(user)
    username.send_keys(Keys.ENTER)

    time.sleep(1)

    password = browser.find_element_by_name('passwd')
    password.send_keys(passwd)
    password.send_keys(Keys.ENTER)

    try:
        WebDriverWait(browser, 60).until(
            EC.element_to_be_clickable((By.ID, "idBtn_Back"))
        ).click()
    finally:
        sys.exit('Timeout for 2FA approval reached. Try again.')

Unfortunately, as I briefly mentioned above, even after stay signed in page appears, Python isn't able to detect the "No" button.不幸的是,正如我上面简要提到的,即使在出现保持登录页面后,Python 也无法检测到“否”按钮。 I don't really care which button is used because the script is using a separate instance of the browser where I'm not signed in.我真的不在乎使用了哪个按钮,因为脚本使用的是我未登录的浏览器的单独实例。

I've considered setting a long time.sleep() command, but that seems like a really stupid way to do it.我考虑过设置 long time.sleep()命令,但这似乎是一种非常愚蠢的方法。 Does anyone have any suggestions?有没有人有什么建议?

Python isn't able to detect the "No" button. Python 无法检测到“否”按钮。

Trust me whenever I find something weird happening in Python, something that absolutely doesn't make sense, where the program simply doesn't do what it is told, I have always found a try-except statement there.相信我,每当我发现 Python 中发生了一些奇怪的事情,一些绝对没有意义的事情,程序根本不按照它的要求执行时,我总是在那里找到一个try-except语句。 And that my friend is your mistake.我的朋友是你的错误。

Also finally will always execute .finally将始终执行 Doesn't matter if try works or fails.不管try有效还是失败。

WebDriverWait(browser, 60).until(
            EC.element_to_be_clickable((By.ID, "idBtn_Back"))
        ).click()

is simply wrong!简直是错误的! Try running it on its own:尝试单独运行它:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-b43596b524d7> in <module>
      9 
     10 WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "i0116")))
---> 11 driver.find_element_by_id("i0116").send_keys("u18ec013@svnitsuratg.onmicrosoft.com").click()
     12 
     13 WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "idSIButton9")))

AttributeError: 'NoneType' object has no attribute 'click'

Because you simply can't call .click() on a WebDriverWait 's output (which is None ).因为您根本无法在WebDriverWait的输出(即None )上调用.click() ) 。

The following code works fine:以下代码工作正常:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome(executable_path='path/to/executable')

driver.get("https://login.microsoftonline.com")

WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "i0116")))
driver.find_element_by_id("i0116").send_keys("your_emailid")

WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "idSIButton9")))
driver.find_element_by_id("idSIButton9").click()

driver.find_element_by_id("i0118").send_keys("your_password")
WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "idSIButton9")))
driver.find_element_by_id("idSIButton9").click()

WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.ID, "idSIButton9")))
driver.find_element_by_id("idSIButton9").click()

In your case, you simply need to change it to:在您的情况下,您只需将其更改为:

def signin(user, passwd):
    # find elements before passing information
    username = browser.find_element_by_name('loginfmt')
    username.send_keys(user)
    username.send_keys(Keys.ENTER)

    time.sleep(1)

    password = browser.find_element_by_name('passwd')
    password.send_keys(passwd)
    password.send_keys(Keys.ENTER)

    try:
        WebDriverWait(browser, 60).until(EC.element_to_be_clickable((By.ID, "idBtn_Back")))
        browser.find_element_by_id("idSIButton9").click()

    except:
        sys.exit('Timeout for 2FA approval reached. Try again.')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM