A sample Flask application that implements Login With Webex, OpenID Connect, Authorization Code Flow. This Python web application demonstrates how to integrate Webex authentication into a Flask-based web application using the standard OAuth 2.0 Authorization Code flow.
This Flask application provides a complete implementation of the Webex OpenID Connect authentication flow, including:
- OAuth 2.0 Authorization Code Flow: Secure server-side authentication
- JWT Token Parsing: Decoding and validating ID tokens
- User Information Retrieval: Accessing user profile data via the UserInfo endpoint
- Session Management: Secure storage of tokens and user state
- Responsive Web Interface: Clean, modern UI with Webex branding
This project relies on several Python packages:
- Flask: Web framework for Python
- Requests: HTTP library for API calls
- JSON: JSON parsing and manipulation (built-in)
- JWT: JSON Web Token handling
- OS: Operating system interface (built-in)
- webbrowser: Web browser launching (built-in)
Before you can run the project, you'll need to install the necessary dependencies. Here's how to do it:
-
Python 3: Make sure you have Python 3 installed on your machine. You can check this by running
python3 --versionin your command line. If you don't have Python installed, you can download it from here. -
pip3: Install the necessary packages using pip3. pip3 is a package manager for Python. You can install it following these instructions.
Once you have pip3 installed, you can install the packages with the following commands:
pip3 install Flask
pip3 install requests
pip3 install jwtNote: The json and os packages are part of the Python Standard Library, so you don't need to install them separately.
For easier dependency management, you can create a requirements.txt file:
Flask==2.3.3
requests==2.31.0
PyJWT==2.8.0Then install with:
pip3 install -r requirements.txtBefore running the application, you need to configure your Webex Integration:
- Go to Webex Developer Portal: https://developer.webex.com/
- Create a New Integration: Click "Create an Integration"
- Configure Integration Settings:
- Name: Your application name
- Description: Brief description of your app
- Redirect URI:
http://0.0.0.0:10060/oauth - Scopes:
openid,email,profile(minimum required)
Update the configuration variables in login.py:
clientID = "YOUR CLIENT ID HERE" # Replace with your Integration Client ID
secretID = "YOUR CLIENT SECRET HERE" # Replace with your Integration Client Secret
redirectURI = "http://0.0.0.0:10060/oauth" # Update if using different host/portUpdate the OAuth URL in templates/index.html:
<a href='https://webexapis.com/v1/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=http://0.0.0.0:10060/oauth&scope=openid%20email%20profile&state=1234abcd'>Replace YOUR_CLIENT_ID with your actual Client ID.
After installing the dependencies and configuring the application, you can run the script using Python in your command line:
python3 login.pyThe application will start and be available at:
- Local Access: http://127.0.0.1:10060
- Network Access: http://0.0.0.0:10060
login-with-webex-flask/
βββ login.py # Main Flask application
βββ templates/ # HTML templates
β βββ temp.html # Base template
β βββ index.html # Login page
β βββ user.html # User profile page
βββ static/ # Static assets
β βββ css/
β βββ index.css # Stylesheet
β βββ logo.png # Webex logo
βββ LICENSE # MIT License
βββ README.md # This file
- User visits the main page (
/) - User clicks the "GRANT" button
- Application redirects to Webex authorization endpoint
- User logs in with Webex credentials
- User grants permission to the application
- Webex redirects back to
/oauthwith authorization code - Application validates the state parameter
- Application exchanges authorization code for tokens
- ID token and access token are stored in session
- Application parses the ID token to extract claims
- Application calls UserInfo endpoint with access token
- User profile information is displayed
The application uses a hardcoded state parameter (1234abcd) for CSRF protection:
if state == '1234abcd':
# Process authorization code
else:
# Redirect back to loginTokens are securely stored in Flask sessions:
session['id_token'] = id_token
session['access_token'] = access_tokenID tokens are parsed without signature verification for demonstration:
def parse_jwt(token):
return jwt.decode(token, options={"verify_signature": False})| Route | Method | Description |
|---|---|---|
/ |
GET | Main login page |
/oauth |
GET | OAuth callback handler |
| Endpoint | Purpose |
|---|---|
https://webexapis.com/v1/authorize |
Authorization endpoint |
https://webexapis.com/v1/access_token |
Token exchange endpoint |
https://webexapis.com/v1/userinfo |
User information endpoint |
Exchanges authorization code for access and ID tokens:
def get_tokens(code):
url = "https://webexapis.com/v1/access_token"
headers = {'accept':'application/json','content-type':'application/x-www-form-urlencoded'}
payload = ("grant_type=authorization_code&client_id={0}&client_secret={1}&"
"code={2}&redirect_uri={3}").format(clientID, secretID, code, redirectURI)
req = requests.post(url=url, data=payload, headers=headers)
results = json.loads(req.text)
id_token = results["id_token"]
access_token = results["access_token"]
session['id_token'] = id_token
session['access_token'] = access_tokenRetrieves user profile information:
def user_info():
accessToken = session['access_token']
url = "https://webexapis.com/v1/userinfo"
headers = {'accept': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken}
req = requests.get(url=url, headers=headers)
results = json.loads(req.text)
return resultsParses JWT tokens to extract claims:
def parse_jwt(token):
return jwt.decode(token, options={"verify_signature": False})- Clean, centered layout with Webex branding
- Responsive design that works on various screen sizes
- Webex color scheme (blue: #0c99d5)
- Professional typography using Tahoma font
Login Page (index.html):
- Webex logo
- "GRANT INTEGRATION ACCESS" heading
- Large "GRANT" button that initiates OAuth flow
User Profile Page (user.html):
- User claims from ID token
- Complete user information from UserInfo endpoint
-
Start the application:
python3 login.py
-
Open in browser: Navigate to
http://127.0.0.1:10060 -
Test OAuth flow:
- Click "GRANT" button
- Log in with Webex credentials
- Verify user information is displayed
Enable debug mode for development:
app.run("0.0.0.0", port=10060, debug=True)This provides:
- Automatic reloading on code changes
- Detailed error messages
- Interactive debugger
Import Errors
ModuleNotFoundError: No module named 'flask'Solution: Install missing dependencies with pip3 install flask
Configuration Errors
Invalid client_id parameter
Solution: Verify Client ID and Secret are correctly configured
Redirect URI Mismatch
redirect_uri_mismatch
Solution: Ensure redirect URI in code matches Integration settings
Token Exchange Failures
invalid_grant
Solution: Check authorization code hasn't expired (10 minutes max)
The application prints debug information to the console:
print("function : get_tokens()")
print("code:", code)
print("ID Token stored in session : ", session['id_token'])
print("Access Token stored in session : ", session['access_token'])
print('claims : ', claims)For production use, consider:
-
Environment Variables: Store secrets in environment variables
clientID = os.getenv('WEBEX_CLIENT_ID') secretID = os.getenv('WEBEX_CLIENT_SECRET')
-
HTTPS: Use HTTPS in production
# Remove this line in production os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
-
Secret Key: Use a proper secret key
app.secret_key = os.getenv('FLASK_SECRET_KEY', os.urandom(24))
-
JWT Validation: Implement proper JWT signature validation
def parse_jwt(token): # In production, validate signature return jwt.decode(token, key=jwk_key, algorithms=['RS256'])
- Session Security: Uses secure session management
- Token Storage: Tokens stored in server-side sessions
- State Validation: CSRF protection via state parameter
Add additional scopes for extended functionality:
# In OAuth URL
scope = "openid email profile spark:people_read spark:rooms_read"Implement token refresh for long-running sessions:
def refresh_token():
refresh_token = session.get('refresh_token')
# Implement refresh logicCache user information to reduce API calls:
from functools import lru_cache
@lru_cache(maxsize=128)
def get_cached_user_info(access_token):
# Cached user info retrievalpython3 login.pyUsing Gunicorn:
pip3 install gunicorn
gunicorn -w 4 -b 0.0.0.0:10060 login:appUsing Docker:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 10060
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:10060", "login:app"]Create a .env file:
WEBEX_CLIENT_ID=your_client_id_here
WEBEX_CLIENT_SECRET=your_client_secret_here
FLASK_SECRET_KEY=your_secret_key_here
REDIRECT_URI=https://yourdomain.com/oauth
Please make sure to update tests as appropriate.
- Code Style: Follow PEP 8 Python style guidelines
- Documentation: Add docstrings for new functions
- Testing: Test OAuth flow with different user accounts
- Security: Follow security best practices for token handling
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request with detailed description
- Webex Developer Portal
- Login with Webex Documentation
- OAuth 2.0 Authorization Code Flow
- OpenID Connect Specification
- Flask Documentation
- Create an issue in this repository
- Review Webex Developer Documentation
- Contact Webex Developer Support
_
__ _____| |__ _____ __
\ \ /\ / / _ \ '_ \ / _ \ \/ /
\ V V / __/ |_) | __/> < @WebexDevs
\_/\_/ \___|_.__/ \___/_/\_\
Repository: https://github.com/WebexSamples/login-with-webex-flask