Metadata-Version: 2.4
Name: flaxon-oauth-google
Version: 0.1.1
Summary: Google OAuth 2.0 authentication plugin for Flaxon framework
Author-email: Aldane Hutchinson <aldanehutchinson5@gmail.com>
License: MIT
Project-URL: Homepage, https://flaxon-website.vercel.app/
Project-URL: Repository, https://github.com/aldanedev-create/flaxon-oauth-google.git
Project-URL: Issues, https://github.com/aldanedev-create/flaxon-oauth-google/issues.git
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flaxon>=0.1.7
Requires-Dist: httpx>=0.27.0
Requires-Dist: pyjwt>=2.8.0
Requires-Dist: cryptography>=42.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
Provides-Extra: standard
Requires-Dist: flaxon[standard]; extra == "standard"
Dynamic: license-file

# Flaxon OAuth Google

Google OAuth 2.0 authentication plugin for Flaxon framework.

## Features

- 🔐 **OAuth 2.0 Authorization Code Flow** — Full OAuth 2.0 implementation
- 👤 **User Info Retrieval** — Fetch user profile from Google's API
- 🔄 **Session Management** — Optional session creation after authentication
- 🛡️ **CSRF Protection** — State parameter validation
- ⚙️ **Configurable Scopes** — Request only the permissions you need
- 🧩 **User Mapping** — Map Google user data to your app's user model
- 🎯 **Error Handling** — Graceful OAuth error handling
- 🔁 **Refresh Tokens** — Automatic token refresh support

## Installation

```bash
pip install flaxon-oauth-google


Quick Start
python
from flaxon import Flaxon
from flaxon_oauth_google import GoogleOAuthPlugin

app = Flaxon("my-app")

# Basic usage with environment variables
app.plugins.load_plugin(GoogleOAuthPlugin(
    client_id="your-client-id.apps.googleusercontent.com",
    client_secret="your-client-secret",
    redirect_uri="https://yourapp.com/auth/google/callback",
))

@app.get("/")
async def home(request):
    return """
    <a href="/auth/google/login">Sign in with Google</a>
    """
Configuration
Environment Variables
bash
# Required
export GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=your-client-secret

# Optional
export GOOGLE_REDIRECT_URI=https://yourapp.com/auth/google/callback
export GOOGLE_SCOPES=openid,email,profile
export GOOGLE_SUCCESS_REDIRECT=/dashboard
export GOOGLE_FAILURE_REDIRECT=/login?error=oauth_failed
With Flaxon Config
python
app = Flaxon("my-app", config={
    "GOOGLE_CLIENT_ID": "your-client-id",
    "GOOGLE_CLIENT_SECRET": "your-client-secret",
    "GOOGLE_REDIRECT_URI": "https://yourapp.com/auth/google/callback",
})

plugin = GoogleOAuthPlugin.from_config(app.config)
app.plugins.load_plugin(plugin)
Advanced Usage
Custom Session Creation
python
def create_session_from_user(google_user):
    """Create a session from Google user data."""
    user = app_user_from_google(google_user)
    session_token = generate_session_token(user)
    return session_token

plugin = GoogleOAuthPlugin(
    client_id="...",
    client_secret="...",
    redirect_uri="...",
    session_maker=create_session_from_user,
    success_redirect="/dashboard",
)
app.plugins.load_plugin(plugin)
Custom User Mapping
python
def map_google_user(google_user):
    """Map Google user to app user model."""
    return {
        "external_id": google_user.id,
        "email": google_user.email,
        "email_verified": google_user.verified_email,
        "full_name": google_user.name,
        "first_name": google_user.given_name,
        "last_name": google_user.family_name,
        "avatar_url": google_user.picture,
        "locale": google_user.locale,
    }

plugin = GoogleOAuthPlugin(
    client_id="...",
    client_secret="...",
    redirect_uri="...",
    user_mapper=map_google_user,
)
Custom Scopes
python
# Request only what you need
plugin = GoogleOAuthPlugin(
    client_id="...",
    client_secret="...",
    redirect_uri="...",
    scopes=["openid", "email"],  # Just email, no profile
)

# Or request additional scopes
plugin = GoogleOAuthPlugin(
    client_id="...",
    client_secret="...",
    redirect_uri="...",
    scopes=[
        "openid",
        "email",
        "profile",
        "https://www.googleapis.com/auth/drive.readonly"
    ],
)
Protected Routes
python
from flaxon_oauth_google import login_required

@app.get("/profile")
@login_required
async def profile(request):
    user = request.session.get("user")
    return {
        "name": user.get("name"),
        "email": user.get("email"),
        "picture": user.get("picture"),
    }

@app.get("/settings")
@login_required
async def settings(request):
    return {"settings": "..."}
OAuth Endpoints
Route	Method	Description
/auth/google/login	GET	Redirect to Google consent screen
/auth/google/callback	GET	OAuth callback endpoint
/auth/google/logout	GET	Clear session (optional)
/auth/google/user	GET	Get current user info
Security Best Practices
✅ Store client secret in environment variables

✅ Use HTTPS in production

✅ Validate redirect URI matches registered URI

✅ Use state parameter for CSRF protection

✅ Verify ID token signature

✅ Keep scopes minimal (least privilege)

✅ Regenerate session ID on login

✅ Use secure cookies (Secure, HttpOnly, SameSite)

Example Application
Complete Setup
python
from flaxon import Flaxon
from flaxon_oauth_google import GoogleOAuthPlugin
import os

app = Flaxon("my-app")

# Load plugin
plugin = GoogleOAuthPlugin(
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    redirect_uri=f"{os.environ['APP_URL']}/auth/google/callback",
    scopes=["openid", "email", "profile"],
    success_redirect="/dashboard",
    failure_redirect="/login?error=oauth_failed",
)

def create_session(google_user):
    """Create a session for the user."""
    # Your session creation logic
    session_id = f"session_{google_user.id}_{int(time.time())}"
    return session_id

plugin.session_maker = create_session
app.plugins.load_plugin(plugin)

@app.get("/")
async def home(request):
    return """
    <html>
        <body>
            <h1>Welcome to My App</h1>
            <a href="/auth/google/login">
                <button>Sign in with Google</button>
            </a>
        </body>
    </html>
    """

@app.get("/dashboard")
async def dashboard(request):
    user = request.session.get("user")
    if not user:
        return {"error": "Not authenticated"}, 401
    return {
        "welcome": f"Hello, {user.get('name')}!",
        "email": user.get("email"),
        "picture": user.get("picture"),
    }

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
Testing
bash
# Run tests
pytest

# Run with coverage
pytest --cov=flaxon_oauth_google

# Run specific test
pytest tests/test_provider.py -v
Requirements
Python 3.11+

Flaxon 0.1.0+

httpx 0.27.0+

pyjwt 2.8.0+

cryptography 42.0.0+

Project Structure
text
flaxon-oauth-google/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── flaxon_oauth_google/
│       ├── __init__.py       # Public API exports
│       ├── plugin.py         # GoogleOAuthPlugin class
│       ├── provider.py       # GoogleOAuthProvider
│       ├── client.py         # Google API client
│       ├── user.py           # User info mapping
│       └── routes.py         # OAuth route handlers
└── tests/
    ├── test_plugin.py
    ├── test_provider.py
    └── test_integration.py
Roadmap
Version	Features
0.1.0	Basic Google OAuth flow
0.2.0	G Suite domain restriction
0.3.0	Service account support
0.4.0	Social login buttons (HTML helpers)
0.5.0	Refresh token rotation
Contributing
Fork the repository

Create a feature branch

Add tests for new features

Ensure all tests pass

Submit a pull request

License
MIT License - See LICENSE file for details.

Support

📚 Documentation

🐛 Issue Tracker

💬 Discussions
