A comprehensive Flask application demonstrating the OAuth 2.0 Device Authorization Grant flow with Webex APIs. This sample showcases secure authentication for devices without keyboards or browsers, including QR code generation, token polling, and user profile retrieval.
This sample shows how to implement the OAuth 2.0 Device Authorization Grant flow with Webex APIs:
- Device Authorization Grant - OAuth flow for input-constrained devices
- QR Code Generation - Visual authentication for mobile devices
- Token Polling - Secure background token retrieval
- Automatic Token Refresh - Handle expired tokens seamlessly
- User Profile Retrieval - Fetch authenticated user information
- Real-time Updates - jQuery-powered status polling
- Secure Session Management - Safe credential storage
- Python 3.6 or higher
- Flask web framework
- Requests library for HTTP operations
- QRCode library with PIL support
- Webex Developer Account - Sign up for free
- Webex Integration with device authorization capabilities
Install the required Python modules using pip:
pip install flask
pip install requests
pip install qrcode[pil]
Module Details:
- Flask: Web framework for Python applications
- Requests: Simplified HTTP library for API calls
- QRCode[PIL]: QR code generation with image support (includes Pillow)
-
Navigate to Webex Developer Portal
-
Log in with your Webex account credentials
-
Select "Start building apps" on the landing page
-
Choose "Create an Integration" in the Integrations card
-
Configure your integration:
Field Value Mobile SDK No Integration Name Your app name (alphanumeric) Icon Choose from default icons Description e.g., "OAuth testing integration" Redirect URIs Use the helper service URLs below Scopes spark:all
Required Redirect URIs:
https://oauth-helper-a.wbx2.com/helperservice/v1/actions/device/callback https://oauth-helper-k.wbx2.com/helperservice/v1/actions/device/callback https://oauth-helper-r.wbx2.com/helperservice/v1/actions/device/callback
-
Save your integration and securely store the Client ID and Client Secret
-
Update environment variables in the
.env
file:CLIENT_ID=your_client_id_here CLIENT_SECRET=your_client_secret_here
-
Start the application:
python oauth.py
-
Access the application at the generated URL:
Running on http://10.26.164.77:10060
-
Authenticate by scanning the QR code or entering the URL and code manually
-
View profile information after successful authentication
webex-device-oauth-sample/
βββ oauth.py # Main Flask application
βββ .env # Environment variables (not tracked)
βββ static/css/ # CSS styling files
βββ templates/ # HTML templates
β βββ index.html # Landing page
β βββ sign-in.html # Authentication page with QR code
β βββ granted.html # Post-authentication page
β βββ whoami.html # User profile display
β βββ temp.html # Base template
βββ package.json # Development tools configuration
βββ README.md # This file
Step | Description | Implementation |
---|---|---|
1. Device Authorization | Request device and user codes | /sign-in endpoint |
2. User Authentication | User authorizes via QR code or URL | External Webex authorization |
3. Token Polling | Background polling for access token | poll_for_access_token() |
4. Token Retrieval | Receive access and refresh tokens | Background thread processing |
5. API Access | Use tokens for authenticated API calls | /whoami endpoint |
def sign_in():
scopes = "meeting:recordings_read spark:all spark:kms"
params = {'client_id': clientID, 'scope': scopes}
device_auth_url = "https://webexapis.com/v1/device/authorize"
device_auth_request = requests.post(url=device_auth_url, data=params)
def qr_cde_generation(url):
img = qrcode.make(url)
img.save('./static/qr_code.png')
def poll_for_access_token(device_code, poll_interval, secure_prefix):
token_url = "https://webexapis.com/v1/device/token"
while True:
time.sleep(poll_interval)
token_request = requests.post(url=token_url, data=body, headers=headers)
if token_request.status_code == 200:
# Store tokens and mark as ready
break
- User Clicks Sign-In: Initiates the device authorization flow
- Device Authorization: App requests device and user codes from Webex
- QR Code Display: Visual representation of the verification URL
- User Authentication: User scans QR code or visits URL to authorize
- Token Polling: App polls Webex for authorization completion
- Token Storage: Access and refresh tokens stored securely
- Profile Access: User can view their Webex profile information
- Secure Session Management: Random alphanumeric session prefixes
- Base64 Credential Encoding: Client credentials properly encoded
- Automatic Token Refresh: Handles expired tokens transparently
- Thread-Safe Polling: Background token retrieval without blocking UI
POST https://webexapis.com/v1/device/authorize
Content-Type: application/x-www-form-urlencoded
client_id=YOUR_CLIENT_ID&scope=spark:all
POST https://webexapis.com/v1/device/token
Authorization: Basic {base64_encoded_credentials}
client_id=YOUR_CLIENT_ID&device_code=DEVICE_CODE&grant_type=urn:ietf:params:oauth:grant-type:device_code
GET https://webexapis.com/v1/people/me?callingData=true
Authorization: Bearer ACCESS_TOKEN
The application includes comprehensive error handling:
def poll_for_access_token(device_code, poll_interval, secure_prefix):
while True:
token_request = requests.post(url=token_url, data=body, headers=headers)
if token_request.status_code == 200:
# Success - store tokens
break
else:
# Handle errors like 'slow_down', 'expired_token'
print("Response Code:", token_request.status_code,
token_request.json()['errors'][0]['description'])
- Flask Templates with Jinja2 templating
- jQuery for asynchronous token status checking
- CSS Styling for professional appearance
- QR Code Display for mobile-friendly authentication
The sign-in page uses jQuery to poll for token readiness:
function checkAccessToken() {
$.ajax({
url: '/access_token_ready/{{ secure_prefix }}',
type: 'GET',
success: function (response) {
if (response.token_ready) {
window.location.href = '/granted/{{ secure_prefix }}';
} else {
setTimeout(checkAccessToken, 2000); // Poll every 2 seconds
}
}
});
}
- Landing Page: Simple interface with "Sign-In" button
- Authentication Page: QR code and manual entry options
- Waiting State: Automatic polling with user feedback
- Success Page: Confirmation and profile access
- Profile Display: Formatted JSON user information
Variable | Description | Example |
---|---|---|
CLIENT_ID |
Webex Integration Client ID | C1234567890abcdef... |
CLIENT_SECRET |
Webex Integration Client Secret | secret123... |
# Flask configuration
app.secret_key = os.urandom(16) # Secure session key
app.run(debug=True, host='0.0.0.0', port=10060) # Development settings
# OAuth scopes requested
scopes = "meeting:recordings_read spark:all spark:kms"
-
Environment Variables: Never hardcode credentials
clientID = os.getenv('CLIENT_ID') clientSecret = os.getenv('CLIENT_SECRET')
-
HTTPS: Use HTTPS in production
-
Session Security: Implement proper session management
-
Token Storage: Use secure storage for production tokens
# Production configuration
if __name__ == "__main__":
port = int(os.environ.get('PORT', 10060))
app.run(host='0.0.0.0', port=port, debug=False)
import logging
# Configure logging for production
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def poll_for_access_token(device_code, poll_interval, secure_prefix):
logger.info(f"Starting token polling for session {secure_prefix}")
# ... polling logic
-
Virtual Environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt
-
Debug Mode:
app.run(debug=True, host='0.0.0.0', port=10060)
Example: Adding token expiration checking:
import jwt
from datetime import datetime
def is_token_expired(access_token):
try:
payload = jwt.decode(access_token, options={"verify_signature": False})
exp_timestamp = payload.get('exp')
return datetime.now().timestamp() > exp_timestamp
except Exception:
return True
- Start the application
- Navigate to the landing page
- Click "Sign-In" to initiate flow
- Scan QR code or enter URL manually
- Authorize in Webex
- View profile information
- Webex Developer Portal
- OAuth 2.0 Device Authorization Grant
- Webex API Documentation
- Flask Documentation
- QRCode Library Documentation
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly with Webex integrations
- Submit a pull request
This project is licensed under the Cisco Sample Code License.
- β Permitted: Copy, modify, and redistribute for use with Cisco products
- β Prohibited: Use independent of Cisco products or to compete with Cisco
- βΉοΈ Warranty: Provided "as is" without warranty
- βΉοΈ Support: Not supported by Cisco TAC
See the LICENSE file for full license terms.
- Create an issue in this repository
- Visit Webex Developer Support
- Join the Webex Developer Community
Ready to implement device authentication with Webex! ππ±