Add comprehensive documentation for Quizzical Beats
- Created a detailed database schema document outlining tables, relationships, and key fields. - Added OAuth integration documentation covering Spotify and Dropbox authentication processes. - Introduced a FAQ section addressing common user inquiries about the application. - Developed a user-friendly index page for easy navigation of the documentation. - Specified documentation dependencies in requirements.txt for building the documentation site. - Expanded user guide with sections on account management, creating rounds, exporting rounds, getting started, importing songs, and user interface navigation. - Updated mkdocs.yml for improved site structure and navigation.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# OAuth Integration
|
||||
|
||||
This document details how Quizzical Beats integrates with OAuth providers, including Spotify and Dropbox.
|
||||
|
||||
## Overview
|
||||
|
||||
Quizzical Beats uses OAuth 2.0 to authenticate with third-party services. The current OAuth implementation provides:
|
||||
|
||||
- API access to third-party services (Spotify API, Dropbox files)
|
||||
- Token storage and refresh mechanisms
|
||||
- Fallback strategies when tokens expire
|
||||
|
||||
## OAuth Provider Configuration
|
||||
|
||||
### Spotify OAuth
|
||||
|
||||
Spotify OAuth is used for API access:
|
||||
|
||||
```python
|
||||
SPOTIFY_CLIENT_ID = os.environ.get('SPOTIFY_CLIENT_ID')
|
||||
SPOTIFY_CLIENT_SECRET = os.environ.get('SPOTIFY_CLIENT_SECRET')
|
||||
SPOTIFY_REDIRECT_URI = os.environ.get('SPOTIFY_REDIRECT_URI', 'http://localhost:5000/auth/spotify/callback')
|
||||
```
|
||||
|
||||
### Dropbox OAuth
|
||||
|
||||
Dropbox OAuth enables file export functionality:
|
||||
|
||||
```python
|
||||
DROPBOX_APP_KEY = os.environ.get('DROPBOX_APP_KEY')
|
||||
DROPBOX_APP_SECRET = os.environ.get('DROPBOX_APP_SECRET')
|
||||
DROPBOX_REDIRECT_URI = os.environ.get('DROPBOX_REDIRECT_URI', 'http://localhost:5000/users/dropbox/callback')
|
||||
```
|
||||
|
||||
## Dropbox Integration Implementation
|
||||
|
||||
The Dropbox OAuth integration is implemented directly in the User model:
|
||||
|
||||
```python
|
||||
class User(db.Model):
|
||||
# Other user fields...
|
||||
|
||||
# Dropbox OAuth fields
|
||||
dropbox_id = db.Column(db.String(100), nullable=True)
|
||||
dropbox_token = db.Column(db.Text(), nullable=True)
|
||||
dropbox_refresh_token = db.Column(db.Text(), nullable=True)
|
||||
dropbox_token_expiry = db.Column(db.DateTime(), nullable=True)
|
||||
dropbox_export_path = db.Column(db.String(255), nullable=True)
|
||||
```
|
||||
|
||||
### Dropbox Authentication Flow
|
||||
|
||||
1. User initiates Dropbox connection from their profile page
|
||||
2. Application redirects to Dropbox's authorization page
|
||||
3. User grants permission to the application
|
||||
4. Dropbox redirects back to our callback URL with an authorization code
|
||||
5. Application exchanges the code for access and refresh tokens
|
||||
6. Tokens and basic user info are stored in the user's record
|
||||
|
||||
Example of the callback handler:
|
||||
|
||||
```python
|
||||
@users_bp.route('/dropbox/callback')
|
||||
@login_required
|
||||
def dropbox_callback():
|
||||
# Handle errors
|
||||
if 'error' in request.args:
|
||||
flash(f'Dropbox authorization failed: {error}', 'error')
|
||||
return redirect(url_for('users.profile'))
|
||||
|
||||
# Exchange authorization code for tokens
|
||||
code = request.args.get('code')
|
||||
token_info = exchange_code_for_token(code)
|
||||
|
||||
# Store tokens in the user model
|
||||
current_user.dropbox_token = token_info.get('access_token')
|
||||
current_user.dropbox_refresh_token = token_info.get('refresh_token')
|
||||
|
||||
# Store expiration time
|
||||
expires_in = token_info.get('expires_in', 14400) # Default to 4 hours
|
||||
current_user.dropbox_token_expiry = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
# Get and store account info
|
||||
account_info = get_dropbox_account_info(current_user.dropbox_token)
|
||||
if account_info:
|
||||
current_user.dropbox_id = account_info.get('account_id')
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('users.profile'))
|
||||
```
|
||||
|
||||
## Token Management
|
||||
|
||||
### Token Refresh
|
||||
|
||||
Tokens are refreshed when they expire. The Dropbox implementation uses:
|
||||
|
||||
```python
|
||||
def get_current_user_dropbox_token():
|
||||
"""Get a valid Dropbox access token for the current user, refreshing if needed"""
|
||||
if not current_user or not current_user.is_authenticated:
|
||||
return None
|
||||
|
||||
# Check if token exists and is valid
|
||||
if (current_user.dropbox_token and
|
||||
current_user.dropbox_token_expiry and
|
||||
current_user.dropbox_token_expiry > datetime.now() + timedelta(minutes=5)):
|
||||
return current_user.dropbox_token
|
||||
|
||||
# Token is missing or about to expire - try to refresh
|
||||
if current_user.dropbox_refresh_token:
|
||||
# Refresh the token
|
||||
token_info = refresh_dropbox_token(current_user.dropbox_refresh_token)
|
||||
|
||||
if token_info and 'access_token' in token_info:
|
||||
# Update token in database
|
||||
current_user.dropbox_token = token_info['access_token']
|
||||
expires_in = token_info.get('expires_in', 14400)
|
||||
current_user.dropbox_token_expiry = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return current_user.dropbox_token
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### Token Revocation
|
||||
|
||||
Users can disconnect their Dropbox accounts:
|
||||
|
||||
```python
|
||||
@users_bp.route('/dropbox/disconnect', methods=['POST'])
|
||||
@login_required
|
||||
def dropbox_disconnect():
|
||||
"""Disconnect user's Dropbox account"""
|
||||
# Revoke token if present
|
||||
if current_user.dropbox_token:
|
||||
try:
|
||||
revoke_token(current_user.dropbox_token)
|
||||
except Exception as e:
|
||||
# Log the error but continue
|
||||
pass
|
||||
|
||||
# Clear Dropbox credentials
|
||||
current_user.dropbox_token = None
|
||||
current_user.dropbox_refresh_token = None
|
||||
current_user.dropbox_token_expiry = None
|
||||
current_user.dropbox_id = None
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('users.profile'))
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
When working with OAuth:
|
||||
|
||||
- Always use HTTPS in production
|
||||
- Store tokens securely
|
||||
- Implement proper token refresh
|
||||
- Handle token revocation when users disconnect accounts
|
||||
- Request minimal scope access
|
||||
- Validate all OAuth-related inputs
|
||||
- Use the official provider documentation for the most up-to-date OAuth implementation details
|
||||
Reference in New Issue
Block a user