Skip to main content

Django Googler is a simple way to integrate Google Auth Platform with your Django project.

Project description

Django Googler

Django Googler is a simple way to integrate Google OAuth Platform with your Django project. It provides both Django Rest Framework API views and regular Django views for handling Google OAuth authentication flows.

Features

  • 🔐 Google OAuth 2.0 Integration - Complete OAuth flow implementation
  • 🎯 Dual View Support - Both DRF API views and regular Django views
  • 🔧 Service Layer Architecture - Clean separation of business logic
  • ⚙️ Highly Configurable - Override settings via Django settings
  • 🛡️ CSRF Protection - Built-in state verification
  • 👥 Automatic User Management - Create or update users from Google info
  • 💾 Database Token Storage - Persistent OAuth token storage with auto-refresh
  • 🔄 Token Management - Automatic token refresh when expired
  • 🎛️ Django Admin Integration - Manage OAuth tokens via Django admin
  • 📦 Zero Configuration - Works out of the box with sensible defaults
  • 🚦 Rate Limiting - Built-in throttling to prevent abuse
  • Settings Validation - Django system checks for configuration
  • 📝 DRF Serializers - Proper request/response validation

Installation

pip install django-googler

Dependencies

Quick Start

1. Add to Installed Apps

# settings.py
INSTALLED_APPS = [
    # ...
    "rest_framework",
    "rest_framework.authtoken",  # Required for API token authentication
    "django_googler",
]

2. Configure Google OAuth Settings

Get your credentials from Google Cloud Console:

# settings.py
import os

# Required: Get these from Google Cloud Console
GOOGLE_OAUTH_CLIENT_ID = os.environ.get("GOOGLE_OAUTH_CLIENT_ID")
GOOGLE_OAUTH_CLIENT_SECRET = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET")

# Optional: Override default redirect URIs
GOOGLE_OAUTH_REDIRECT_URIS = [
    "http://localhost:8000/auth/google/callback/",
    "http://localhost:8000/api/auth/google/callback/",
]

# Optional: Override default scopes
GOOGLE_OAUTH_SCOPES = [
    "openid",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/userinfo.profile",
]

# Optional: Save OAuth tokens to database (recommended for persistent storage)
# Default: True
GOOGLE_OAUTH_SAVE_TOKENS_TO_DB = True

# Optional: Store OAuth tokens in session (legacy, use database storage instead)
# Default: False
GOOGLE_OAUTH_STORE_TOKENS = False

# Optional: Return Google tokens in API callback response (for frontend Google API calls)
# Default: False (only returns DRF token)
GOOGLE_OAUTH_RETURN_TOKENS = False

# Optional: Revoke Google OAuth tokens on logout
# Default: False
GOOGLE_OAUTH_REVOKE_ON_LOGOUT = False

# Optional: Login URL for error redirects
# Default: "/login/"
LOGIN_URL = "/admin/login/"

3. Run Migrations

python manage.py migrate

This creates the necessary database tables for:

  • DRF authentication tokens (authtoken)
  • Google OAuth token storage (django_googler)

4. Add URL Patterns

Choose between Regular Django Views or Django Rest Framework API Views (or use both!):

Regular Django Views

# urls.py
from django.urls import path, include

urlpatterns = [
    path("auth/", include("django_googler.urls.default")),
]

Or explicitly:

# urls.py
from django.urls import path
from django_googler import GoogleOAuthLoginView, GoogleOAuthCallbackView

urlpatterns = [
    path("auth/google/login/", GoogleOAuthLoginView.as_view(), name="google-login"),
    path(
        "auth/google/callback/",
        GoogleOAuthCallbackView.as_view(),
        name="google-callback",
    ),
]

Django Rest Framework API Views

# urls.py
from django.urls import path, include

urlpatterns = [
    path("api/auth/", include("django_googler.urls.drf")),
]

Or explicitly:

# urls.py
from django.urls import path
from django_googler import (
    GoogleOAuthLoginAPIView,
    GoogleOAuthCallbackAPIView,
    CurrentUserAPIView,
    GoogleOAuthLogoutAPIView,
)

urlpatterns = [
    path(
        "api/auth/google/login/",
        GoogleOAuthLoginAPIView.as_view(),
        name="google-login-api",
    ),
    path(
        "api/auth/google/callback/",
        GoogleOAuthCallbackAPIView.as_view(),
        name="google-callback-api",
    ),
    path("api/auth/me/", CurrentUserAPIView.as_view(), name="current-user"),
    path("api/auth/logout/", GoogleOAuthLogoutAPIView.as_view(), name="logout"),
]

Usage

Regular Django Views (Browser Redirects)

  1. Redirect users to the login view:

    <a href="{% url 'django_googler:google-login' %}?next=/dashboard/">Sign in with Google</a>
    
  2. Users are redirected to Google for authentication

  3. After authentication, users are redirected back and automatically logged in

The regular Django views will:

  • Create a new user if they don't exist
  • Update existing user information
  • Log the user into Django's authentication system
  • Redirect to the next parameter or / by default

Django Rest Framework API Views (JSON Responses)

  1. GET the login endpoint to get the authorization URL:

    curl http://localhost:8000/api/auth/google/login/
    

    Response:

    {
        "authorization_url": "https://accounts.google.com/o/oauth2/auth?...",
        "state": "random-state-string"
    }
    
  2. Redirect users to the authorization_url

  3. After Google redirects back with the authorization code, POST it to the callback:

    curl -X POST http://localhost:8000/api/auth/google/callback/ \
      -H "Content-Type: application/json" \
      -d '{
        "code": "4/0AY0e-g...",
        "state": "random-state-string",
        "redirect_uri": "http://localhost:3000/auth/callback"
      }'
    

    Response (user is created/logged in and API token returned):

    {
        "token": "drf_api_token_abc123...",
        "user": {
            "id": 1,
            "email": "user@example.com",
            "username": "user",
            "first_name": "John",
            "last_name": "Doe"
        }
    }
    

    Note: Google tokens are not included by default. If your frontend needs to call Google APIs directly (Calendar, Drive, Gmail, etc.), set GOOGLE_OAUTH_RETURN_TOKENS = True in settings to include them in the response.

  4. Use the returned token for authenticated requests:

    curl http://localhost:8000/api/protected/ \
      -H "Authorization: Token drf_api_token_abc123..."
    

Additional DRF Endpoints

Get Current User

curl http://localhost:8000/api/auth/me/ \
  -H "Authorization: Token your-token-here"

Response:

{
    "id": 1,
    "email": "user@example.com",
    "username": "user",
    "first_name": "John",
    "last_name": "Doe"
}

Logout

curl -X POST http://localhost:8000/api/auth/logout/ \
  -H "Authorization: Token your-token-here"

Response:

{
    "message": "Logged out successfully"
}

This will:

  • Delete the user's DRF authentication token
  • Clear all OAuth session data (access tokens, refresh tokens, etc.)

Advanced Usage

Using the Service Layer

You can use the service layer directly in your own views or logic:

from django_googler import GoogleOAuthService, UserService, OAuthFlowService

# Verify and process OAuth credentials
credentials_data = GoogleOAuthService.process_credentials(credentials)

# Create or update user from OAuth info
user, _ = UserService.get_or_create_user(
    email="user@example.com",
    name="John Doe",
    google_id="1234567890",
    picture="https://...",
)

# Manage OAuth flow state
OAuthFlowService.store_state(request, state)
is_valid = OAuthFlowService.verify_state(request, state)
OAuthFlowService.clear_state(request)

Custom Scopes

Request additional Google API scopes:

<!-- In your template -->
<a href="{% url 'django_googler:google-login' %}?scopes=openid,email,profile,https://www.googleapis.com/auth/calendar">
    Sign in with Google Calendar Access
</a>

Or for API views:

curl "http://localhost:8000/api/auth/google/login/?scopes=openid,email,profile"

Database Token Storage (Recommended)

New in v0.0.4: OAuth tokens are now automatically saved to the database by default. This allows your backend to make Google API calls on behalf of users and automatically refresh expired tokens.

Accessing Stored Tokens

from django_googler.services import GoogleOAuthService


# Get user's token
def my_view(request):
    # Option 1: Get valid token (auto-refreshes if expired)
    access_token, expiry = GoogleOAuthService.get_valid_token(request.user)

    if access_token:
        # Use token to call Google APIs
        import requests

        headers = {"Authorization": f"Bearer {access_token}"}
        response = requests.get(
            "https://www.googleapis.com/calendar/v3/calendars/primary/events",
            headers=headers,
        )
        events = response.json()

    # Option 2: Get token object directly
    token = GoogleOAuthService.get_user_token(request.user)
    if token:
        if token.is_expired():
            # Manually refresh if needed
            token = GoogleOAuthService.refresh_user_token(token)

        # Check scopes
        if token.has_scope("https://www.googleapis.com/auth/calendar"):
            # User has calendar access
            pass

Token Model

The GoogleOAuthToken model stores:

  • Access token (for API calls)
  • Refresh token (for getting new access tokens)
  • Token expiry datetime
  • OAuth scopes granted
  • Google ID (sub claim)
  • ID token (JWT)
from django_googler.models import GoogleOAuthToken

# Get user's token
token = GoogleOAuthToken.objects.get(user=request.user)

# Check status
if token.is_valid:
    print("Token is valid and not expired")

if token.can_refresh:
    print("Token can be refreshed")

# Get scopes
scopes = token.get_scopes_list()
# ['openid', 'email', 'profile']

Django Admin

View and manage OAuth tokens in Django admin at /admin/django_googler/googleoauthtoken/:

  • See token status (Valid/Expired/Invalid)
  • View granted scopes
  • Search by user email or Google ID
  • Filter by date or expiry
  • Delete tokens when needed

Token Management

from django_googler.services import GoogleOAuthService

# Save/update a user's token (automatically called during OAuth flow)
token = GoogleOAuthService.save_user_token(
    user=request.user,
    credentials=credentials,
    google_id="1234567890",
    scopes=["email", "profile"],
)

# Refresh an expired token
token = GoogleOAuthService.refresh_user_token(request.user)

# Revoke and delete a token
GoogleOAuthService.revoke_user_token(request.user)

Configuration

# settings.py

# Save tokens to database (default: True)
GOOGLE_OAUTH_SAVE_TOKENS_TO_DB = True

# Revoke tokens when user logs out (default: False)
GOOGLE_OAUTH_REVOKE_ON_LOGOUT = True

Legacy Session Token Storage

If you prefer session-based storage instead of database storage:

# settings.py
GOOGLE_OAUTH_SAVE_TOKENS_TO_DB = False  # Disable database storage
GOOGLE_OAUTH_STORE_TOKENS = True  # Enable session storage

Then access tokens in your views:

def my_view(request):
    access_token = request.session.get("google_access_token")
    refresh_token = request.session.get("google_refresh_token")
    token_expiry = request.session.get("google_token_expiry")

    # Use tokens to make Google API calls
    # ...

Note: Session storage is less reliable and doesn't support automatic token refresh.

Extending User Creation

If you need to store additional user information (like google_id or picture), you can:

  1. Create a custom user model:

    from django.contrib.auth.models import AbstractUser
    
    
    class User(AbstractUser):
        google_id = models.CharField(max_length=255, blank=True)
        picture = models.URLField(blank=True)
    
  2. Or create a user profile model:

    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        google_id = models.CharField(max_length=255, blank=True)
        picture = models.URLField(blank=True)
    
  3. Override the UserService (recommended):

    from django_googler.services import UserService as BaseUserService
    
    
    class CustomUserService(BaseUserService):
        @staticmethod
        def _create_new_user(
            email,
            name=None,
            given_name=None,
            family_name=None,
            google_id=None,
            picture=None,
        ):
            user = super()._create_new_user(
                email, name, given_name, family_name, google_id, picture
            )
            # Store additional fields
            if google_id:
                user.google_id = google_id
            if picture:
                user.picture = picture
            user.save()
            return user
    

Architecture

Django Googler follows a clean service-layer architecture:

  • views.py - View layer (DRF and Django views)
  • services.py - Business logic layer
    • GoogleOAuthService - OAuth token processing
    • UserService - User creation and management
    • OAuthFlowService - OAuth state management
  • platform_client.py - Google OAuth client wrapper
  • defaults.py - Configuration and settings

Configuration Reference

All settings are optional and have sensible defaults:

Setting Default Description
GOOGLE_OAUTH_CLIENT_ID "" Google OAuth Client ID (required)
GOOGLE_OAUTH_CLIENT_SECRET "" Google OAuth Client Secret (required)
GOOGLE_OAUTH_REDIRECT_URIS ["http://localhost:8000/api/googler/callback"] Authorized redirect URIs
GOOGLE_OAUTH_SCOPES ["openid", "email", "profile"] OAuth scopes to request
GOOGLE_OAUTH_SAVE_TOKENS_TO_DB True Save tokens to database (recommended)
GOOGLE_OAUTH_STORE_TOKENS False Store tokens in session (legacy)
GOOGLE_OAUTH_RETURN_TOKENS False Return Google tokens in API response
GOOGLE_OAUTH_REVOKE_ON_LOGOUT False Revoke tokens when user logs out
LOGIN_URL "/login/" Redirect URL on OAuth errors

Rate Limiting

Django Googler includes built-in rate limiting for OAuth endpoints:

  • Login endpoint: 10 requests per hour
  • Callback endpoint: 20 requests per hour

You can customize these rates in your Django REST Framework settings:

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES": {
        "anon": "100/hour",
        "user": "1000/hour",
    }
}

Error Handling

The views handle various error scenarios:

Error Code Description
oauth_init_failed Failed to initiate OAuth flow
missing_code Authorization code not provided by Google
invalid_state CSRF state verification failed
no_email Google didn't provide an email address
oauth_callback_failed General callback processing error

Access errors via query parameters:

def login_view(request):
    error = request.GET.get("error")
    if error == "invalid_state":
        messages.error(request, "Security check failed. Please try again.")

Google Cloud Console Setup

  1. Go to Google Cloud Console
  2. Create a new project or select existing one
  3. Enable the Google+ API and People API
  4. Go to CredentialsCreate CredentialsOAuth 2.0 Client ID
  5. Configure Authorized redirect URIs:
    • For local development: http://localhost:8000/auth/google/callback/
    • For production: https://yourdomain.com/auth/google/callback/
  6. Copy the Client ID and Client Secret to your Django settings

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please use the GitHub issue tracker.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_googler-0.0.7.tar.gz (59.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_googler-0.0.7-py3-none-any.whl (29.1 kB view details)

Uploaded Python 3

File details

Details for the file django_googler-0.0.7.tar.gz.

File metadata

  • Download URL: django_googler-0.0.7.tar.gz
  • Upload date:
  • Size: 59.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_googler-0.0.7.tar.gz
Algorithm Hash digest
SHA256 6c0f7efdbb863cad48f18247fca3c5e6242bacd8bf8a5d606673a320fd9d357e
MD5 39b7f2f49daacfc8680e78abdbc8edb7
BLAKE2b-256 54d8695b67de5c25e561e4a461957c614552ea8190c6772b90ab2dab8d82c6ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_googler-0.0.7.tar.gz:

Publisher: main.yaml on jmitchel3/django-googler

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_googler-0.0.7-py3-none-any.whl.

File metadata

  • Download URL: django_googler-0.0.7-py3-none-any.whl
  • Upload date:
  • Size: 29.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_googler-0.0.7-py3-none-any.whl
Algorithm Hash digest
SHA256 e15f1c1404a34ec191e879df71d5ee7c5b476533c6847602a35456e0e113fecd
MD5 4699c739192bdc5be92270b8499951a4
BLAKE2b-256 228b3bd8bb2639a37523c8036442013b1ee5fbfb5ffe492e1293bf556166705b

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_googler-0.0.7-py3-none-any.whl:

Publisher: main.yaml on jmitchel3/django-googler

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page