如何用Python实现Cisco Webex基于ADFS的SSO客户端认证?
Hey there, I get exactly what you're asking for—you want to simulate a browser-like SSO client authentication flow for Webex (using your existing ADFS setup), not set up a Service Provider (SP) for SSO. Most tutorials online focus on the SP side, so it's totally understandable why you're stuck. Let me break down the feasible approaches and share some example code:
You have two main paths here, depending on how much control/lightweight you need your solution to be:
- Browser Automation (Simpler, but heavier):Use tools like Playwright or Selenium to replicate exactly what a Chrome user does—clicking, typing, and following redirects. This avoids manually handling all the messy HTTP details like dynamic tokens or cookies.
- Manual HTTP Request Replication (Lightweight, but requires packet capture):Use the
requestslibrary to mimic the HTTP flow you capture from Chrome's DevTools. You'll need to record all redirects, form parameters, and cookies to replicate the login sequence accurately.
Playwright is a modern browser automation tool that's more intuitive than Selenium, and it supports headless mode if you don't need a visible browser.
Step 1: Install Dependencies
pip install playwright playwright install chrome
Step 2: Example Code
from playwright.sync_api import sync_playwright import json def webex_sso_login(webex_portal_url, adfs_username, adfs_password): with sync_playwright() as p: # Launch Chrome (set headless=True for invisible mode) browser = p.chromium.launch(headless=False) page = browser.new_page() # Navigate to Webex portal—this will auto-redirect to ADFS login page page.goto(webex_portal_url) # Wait for ADFS login form to load, then input credentials page.wait_for_selector('#userNameInput', timeout=30000) page.fill('#userNameInput', adfs_username) page.fill('#passwordInput', adfs_password) page.click('#submitButton') # Wait for redirect back to Webex and confirm authentication page.wait_for_url('https://*.webex.com/*', timeout=60000) # Extract Webex access token (from LocalStorage—adjust based on your environment) access_token = page.evaluate("localStorage.getItem('access_token')") print(f"Successfully retrieved Webex Access Token: {access_token}") # Save cookies for future API calls (optional) cookies = page.context.cookies() with open('webex_auth_cookies.json', 'w') as f: json.dump(cookies, f) browser.close() return access_token # Run the login flow if __name__ == "__main__": WEBEX_URL = "https://your-company-webex-portal.com" ADFS_USER = "your-adfs-username@company.com" ADFS_PASS = "your-adfs-password" webex_sso_login(WEBEX_URL, ADFS_USER, ADFS_PASS)
requests This approach is lighter but requires you to capture the exact login sequence using Chrome DevTools (Network tab). You'll need to note redirect URLs, form parameters, and dynamic tokens like CSRF or __VIEWSTATE.
Example Code (Adjust Based on Your ADFS/Webex Setup)
import requests from bs4 import BeautifulSoup def webex_sso_login_manual(webex_portal_url, adfs_username, adfs_password): session = requests.Session() # Step 1: Access Webex, get redirect to ADFS initial_resp = session.get(webex_portal_url) soup = BeautifulSoup(initial_resp.text, 'html.parser') adfs_form = soup.find('form', {'action': lambda x: x and 'adfs' in x.lower()}) if not adfs_form: raise Exception("Couldn't find ADFS redirect form") adfs_login_url = adfs_form['action'] # Extract hidden form fields (SAMLRequest, RelayState, etc.) form_data = { input_tag['name']: input_tag.get('value', '') for input_tag in adfs_form.find_all('input', {'name': True}) } # Step 2: Submit to ADFS, load login page adfs_resp = session.post(adfs_login_url, data=form_data) soup = BeautifulSoup(adfs_resp.text, 'html.parser') login_form = soup.find('form', id='loginForm') if not login_form: raise Exception("Couldn't find ADFS login form") login_submit_url = login_form['action'] # Prepare login data with dynamic tokens login_data = { 'UserName': adfs_username, 'Password': adfs_password, 'AuthMethod': 'FormsAuthentication', '__VIEWSTATE': soup.find('input', {'name': '__VIEWSTATE'})['value'], '__EVENTVALIDATION': soup.find('input', {'name': '__EVENTVALIDATION'})['value'] } # Step 3: Submit credentials to ADFS mfa_resp = session.post(login_submit_url, data=login_data) soup = BeautifulSoup(mfa_resp.text, 'html.parser') # Step 4: Submit SAML response back to Webex (auto-generated form) saml_form = soup.find('form', {'action': lambda x: x and 'webex' in x.lower()}) if not saml_form: raise Exception("Couldn't find Webex SAML callback form") saml_data = { input_tag['name']: input_tag.get('value', '') for input_tag in saml_form.find_all('input', {'name': True}) } final_resp = session.post(saml_form['action'], data=saml_data) # Extract access token from cookies (adjust based on Webex's auth mechanism) access_token = session.cookies.get('access_token') print(f"Successfully retrieved Webex Access Token: {access_token}") return access_token # Run the manual flow (update parameters based on your capture) if __name__ == "__main__": WEBEX_URL = "https://your-company-webex-portal.com" ADFS_USER = "your-adfs-username@company.com" ADFS_PASS = "your-adfs-password" webex_sso_login_manual(WEBEX_URL, ADFS_USER, ADFS_PASS)
- Dynamic Tokens: ADFS uses dynamic fields like
__VIEWSTATEand__EVENTVALIDATION—always extract these from the current page, never hardcode them. - MFA Handling: If your ADFS requires multi-factor authentication, Playwright can wait for user input (e.g., SMS code), while the manual approach will need additional steps to capture the MFA flow.
- API Usage: Once you have the
access_token, you can call Webex APIs by adding it to the Authorization header:def get_webex_meetings(access_token): headers = {'Authorization': f'Bearer {access_token}'} resp = requests.get('https://webexapis.com/v1/meetings', headers=headers) return resp.json()
内容的提问来源于stack exchange,提问作者Difan Zhao

