# Django Googler
Simple Google OAuth authentication for Django. Returns JWT tokens for your API.
## What It Does
1. User clicks "Sign in with Google"
2. Google handles authentication
3. Your Django app gets JWT tokens + user info
4. Use JWT tokens for authenticated API requests
## Installation
```bash
uv add django-googler
```
or
```bash
pip install django-googler
```
## Quick Setup
### 1. Add to `settings.py`
```python
INSTALLED_APPS = [
# ...
"rest_framework",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist", # Optional: for logout
"django_googler",
]
# Get these from Google Cloud Console
GOOGLE_OAUTH_CLIENT_ID = "your-client-id"
GOOGLE_OAUTH_CLIENT_SECRET = "your-client-secret"
```
### 2. Add URLs
```python
# urls.py
from django.urls import path, include
urlpatterns = [
# Django Rest Framework OAuth API views
path("api/auth/", include("django_googler.urls.drf")),
# Standard Django OAuth views (non django rest framework)
path("auth/", include("django_googler.urls.default")),
]
```
### 3. Run Migrations
```bash
python manage.py migrate
```
Done! You now have these endpoints:
- `GET /api/auth/google/login/` - Get Google OAuth URL
- `POST /api/auth/google/callback/` - Exchange code for JWT tokens
- `GET /api/auth/me/` - Get current user (requires JWT)
- `POST /api/auth/logout/` - Logout (requires JWT)
You can also use the Django views directly:
- `GET /auth/google/login/` - To automatically start Google OAuth which will automatically redirect you to `/auth/google/callback/`
- `GET /auth/google/callback/` - To handle Google's callback
- To `logout` you'll use a standard Django logout view or logout method to end the User's session
## Usage
### API Examples:
```bash
curl http://localhost:8000/api/auth/google/login/
```
Yields:
```json
{
"authorization_url": "https://accounts.google.com/...",
"state": "..."
}
```
Opening `authorization_url` in a new tab will start the Google OAuth flow and automatically redirect you to `/api/auth/google/callback/`. If you set `DJANGO_GOOGLER_ALLOW_GET_ON_DRF_CALLBACK = True` in `settings.py`, your automatic redirect to `/api/auth/googler/callback/` will automatically provider your auth tokens in the response such as:
```json
{
"access": "eyJ0eXAiOiJKV1Q...", // JWT access token (short-lived)
"refresh": "eyJ0eXAiOiJKV1Q...", // JWT refresh token (long-lived)
"user": {
"id": 1,
"email": "user@example.com",
"username": "user",
"first_name": "John",
"last_name": "Doe"
}
}
```
From here you can do an authenticated request to `/api/auth/me/` to get the user's information:
```bash
curl http://localhost:8000/api/auth/me/ \
-H "Authorization: Bearer <access_token>"
```
To refresh the access token, you can use the `/api/auth/google/refresh/` endpoint:
```bash
curl -X POST http://localhost:8000/api/auth/google/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh": "<refresh_token>"}'
```
This is a standard JWT refresh endpoint provided by [rest_framework_simplejwt](https://django-rest-framework-simplejwt.readthedocs.io/) with `TokenRefreshView`.
### Frontend Flow
**1. Get Google OAuth URL**
```javascript
const redirect_uri = 'http://localhost:3000/auth/callback';
const apiBaseUrl = 'http://localhost:8000';
const loginApiEndpoint = `${apiBaseUrl}/api/auth/google/login/`;
const requestUrl = `${loginApiEndpoint}?redirect_uri=${redirect_uri}`;
const response = await fetch(requestUrl);
const data = await response.json();
// data = { "authorization_url": "https://accounts.google.com/...", "state": "..." }
// Redirect user to Google
window.location.href = data.authorization_url;
```
**2. Handle Google's Callback**
After Google redirects back to your frontend with a `code` and `state`:
```javascript
// Get the callback data from the current URL
const currentUrl = new URL(window.location.href);
const googleCallbackData = Object.fromEntries(currentUrl.searchParams);
// Send the callback data to the backend
const apiBaseUrl = 'http://localhost:8000';
const callbackApiEndpoint = `${apiBaseUrl}/api/auth/google/callback/`;
const response = await fetch(callbackApiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(googleCallbackData)
});
const responseData = await response.json();
/* responseData = {
"access": "eyJ0eXAiOiJKV1Q...", // JWT access token (short-lived)
"refresh": "eyJ0eXAiOiJKV1Q...", // JWT refresh token (long-lived)
"user": {
"id": 1,
"email": "user@example.com",
"username": "user",
"first_name": "John",
"last_name": "Doe"
}
} */
// Save tokens
localStorage.setItem('access_token', responseData.access);
localStorage.setItem('refresh_token', responseData.refresh);
```
**3. Make Authenticated Requests**
```javascript
const apiBaseUrl = 'http://localhost:8000';
const meApiEndpoint = `${apiBaseUrl}/api/auth/me/`;
const response = await fetch(meApiEndpoint, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
const responseData = await response.json();
/* responseData = {
"id": 1,
"email": "user@example.com",
"username": "user",
"first_name": "John",
"last_name": "Doe"
} */
```
**4. Logout**
```javascript
const apiBaseUrl = 'http://localhost:8000';
const logoutApiEndpoint = `${apiBaseUrl}/api/auth/logout/`;
const response = await fetch(logoutApiEndpoint, {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
refresh: localStorage.getItem('refresh_token')
})
});
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
```
## Configuration
### Required Settings
```python
GOOGLE_OAUTH_CLIENT_ID = "your-client-id"
GOOGLE_OAUTH_CLIENT_SECRET = "your-client-secret"
```
### Optional Settings
```python
# Return Google tokens in callback response (for calling Google APIs from frontend)
# Default: False
GOOGLE_OAUTH_RETURN_TOKENS = False
# Revoke Google access on logout
# Default: False
GOOGLE_OAUTH_REVOKE_ON_LOGOUT = False
# Save Google OAuth tokens to database (for backend Google API calls)
# Default: True
GOOGLE_OAUTH_SAVE_TOKENS_TO_DB = True
# Request additional Google API scopes
# Default: ["openid", "email", "profile"]
GOOGLE_OAUTH_SCOPES = [
"openid",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
# "https://www.googleapis.com/auth/calendar", # Add if needed
]
```
## Google Cloud Setup
For Development:
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create a project
3. Go to **Google Auth Platform**
4. Navigate to **Clients** > **+Create Client**
For Development use:
- **Application type**: Web application
- **Name**: Django Googler Dev
- **Authorized redirect URIs**:
- `http://localhost:8000/api/auth/google/callback/`
- `http://localhost:8000/auth/google/callback/` (frontend handler for Django-based frontend)
- `http://localhost:3000/auth/google/callback/` (frontend handler for React, Next.js or Vue)
- Any others you might need during development
For Production use:
- **Application type**: Web application
- **Name**: Django Googler Prod
- **Redirect URIs**:
- `https://yourdomain.com/api/auth/google/callback/`
- `https://yourdomain.com/auth/google/callback/` (frontend handler for Django or Next.js/React/Vue frontend)
- Any others you might need (adding more is fine especially if you have multiple frontend frameworks)
After you configure it, click **Create** and for _each environment_ (prod/dev) grab:
- **Client ID** (such as `django-googler-dev.apps.googleusercontent.com` and `django-googler-prod.apps.googleusercontent.com`)
- **Client Secret**
For dev, update your `.env` file with:
```bash
GOOGLE_OAUTH_CLIENT_ID=django-googler-dev.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=your-dev-client-secret
```
For prod, update your runtime secrets with:
```bash
GOOGLE_OAUTH_CLIENT_ID=django-googler-prod.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=your-prod-client-secret
```
## Making Google API Calls
If your backend needs to call Google APIs on behalf of users:
```python
from django_googler.services import GoogleOAuthService
def my_view(request):
# Get valid access token (auto-refreshes if expired)
access_token, expiry = GoogleOAuthService.get_valid_token(request.user)
if access_token:
import requests
headers = {"Authorization": f"Bearer {access_token}"}
response = requests.get(
"https://www.googleapis.com/calendar/v3/calendars/primary/events",
headers=headers,
)
return response.json()
```
## Using Django Views (Instead of API)
If you prefer browser redirects over API calls:
```python
# urls.py
urlpatterns = [
path("auth/", include("django_googler.urls.default")),
]
```
Then in your template:
```html
<a href="{% url 'django_googler:google-login' %}?next=/dashboard/">
Sign in with Google
</a>
```
Users will be redirected to Google and back, then logged into Django's session.
## Architecture
- **Views** - Handle OAuth flow and return JWT tokens
- **Services** - Business logic for OAuth, users, and tokens
- **Models** - Store Google OAuth tokens in database
## License
MIT License
## Support
[GitHub Issues](https://github.com/jmitchel3/django-googler/issues)
Raw data
{
"_id": null,
"home_page": null,
"name": "django-googler",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "auth, django, djangorestframework, google, oauth, oauth2, oauthlib",
"author": null,
"author_email": "Justin Mitchel <justin@codingforentrepreneurs.com>",
"download_url": "https://files.pythonhosted.org/packages/7f/17/828ad2aa3c6236a28c5e5f0dced2abaf70f8e6ebba613b72428c113da21e/django_googler-0.0.16.tar.gz",
"platform": null,
"description": "# Django Googler\n\nSimple Google OAuth authentication for Django. Returns JWT tokens for your API.\n\n## What It Does\n\n1. User clicks \"Sign in with Google\"\n2. Google handles authentication\n3. Your Django app gets JWT tokens + user info\n4. Use JWT tokens for authenticated API requests\n\n## Installation\n\n```bash\nuv add django-googler\n```\n\nor\n\n```bash\npip install django-googler\n```\n\n## Quick Setup\n\n### 1. Add to `settings.py`\n\n```python\nINSTALLED_APPS = [\n # ...\n \"rest_framework\",\n \"rest_framework_simplejwt\",\n \"rest_framework_simplejwt.token_blacklist\", # Optional: for logout\n \"django_googler\",\n]\n\n# Get these from Google Cloud Console\nGOOGLE_OAUTH_CLIENT_ID = \"your-client-id\"\nGOOGLE_OAUTH_CLIENT_SECRET = \"your-client-secret\"\n```\n\n### 2. Add URLs\n\n```python\n# urls.py\nfrom django.urls import path, include\n\nurlpatterns = [\n # Django Rest Framework OAuth API views\n path(\"api/auth/\", include(\"django_googler.urls.drf\")),\n # Standard Django OAuth views (non django rest framework)\n path(\"auth/\", include(\"django_googler.urls.default\")),\n]\n```\n\n### 3. Run Migrations\n\n```bash\npython manage.py migrate\n```\n\nDone! You now have these endpoints:\n- `GET /api/auth/google/login/` - Get Google OAuth URL\n- `POST /api/auth/google/callback/` - Exchange code for JWT tokens\n- `GET /api/auth/me/` - Get current user (requires JWT)\n- `POST /api/auth/logout/` - Logout (requires JWT)\n\nYou can also use the Django views directly:\n- `GET /auth/google/login/` - To automatically start Google OAuth which will automatically redirect you to `/auth/google/callback/`\n- `GET /auth/google/callback/` - To handle Google's callback\n- To `logout` you'll use a standard Django logout view or logout method to end the User's session\n\n## Usage\n\n### API Examples:\n\n```bash\ncurl http://localhost:8000/api/auth/google/login/\n```\nYields:\n\n```json\n{\n \"authorization_url\": \"https://accounts.google.com/...\",\n \"state\": \"...\"\n}\n```\nOpening `authorization_url` in a new tab will start the Google OAuth flow and automatically redirect you to `/api/auth/google/callback/`. If you set `DJANGO_GOOGLER_ALLOW_GET_ON_DRF_CALLBACK = True` in `settings.py`, your automatic redirect to `/api/auth/googler/callback/` will automatically provider your auth tokens in the response such as:\n\n```json\n{\n \"access\": \"eyJ0eXAiOiJKV1Q...\", // JWT access token (short-lived)\n \"refresh\": \"eyJ0eXAiOiJKV1Q...\", // JWT refresh token (long-lived)\n \"user\": {\n \"id\": 1,\n \"email\": \"user@example.com\",\n \"username\": \"user\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\"\n }\n}\n```\n\nFrom here you can do an authenticated request to `/api/auth/me/` to get the user's information:\n```bash\ncurl http://localhost:8000/api/auth/me/ \\\n -H \"Authorization: Bearer <access_token>\"\n```\n\nTo refresh the access token, you can use the `/api/auth/google/refresh/` endpoint:\n```bash\ncurl -X POST http://localhost:8000/api/auth/google/refresh/ \\\n -H \"Content-Type: application/json\" \\\n -d '{\"refresh\": \"<refresh_token>\"}'\n```\nThis is a standard JWT refresh endpoint provided by [rest_framework_simplejwt](https://django-rest-framework-simplejwt.readthedocs.io/) with `TokenRefreshView`.\n\n### Frontend Flow\n\n**1. Get Google OAuth URL**\n\n```javascript\nconst redirect_uri = 'http://localhost:3000/auth/callback';\nconst apiBaseUrl = 'http://localhost:8000';\nconst loginApiEndpoint = `${apiBaseUrl}/api/auth/google/login/`;\nconst requestUrl = `${loginApiEndpoint}?redirect_uri=${redirect_uri}`;\nconst response = await fetch(requestUrl);\nconst data = await response.json();\n// data = { \"authorization_url\": \"https://accounts.google.com/...\", \"state\": \"...\" }\n\n// Redirect user to Google\nwindow.location.href = data.authorization_url;\n```\n\n**2. Handle Google's Callback**\n\nAfter Google redirects back to your frontend with a `code` and `state`:\n\n```javascript\n// Get the callback data from the current URL\nconst currentUrl = new URL(window.location.href);\nconst googleCallbackData = Object.fromEntries(currentUrl.searchParams);\n\n// Send the callback data to the backend\nconst apiBaseUrl = 'http://localhost:8000';\nconst callbackApiEndpoint = `${apiBaseUrl}/api/auth/google/callback/`;\nconst response = await fetch(callbackApiEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(googleCallbackData)\n});\n\nconst responseData = await response.json();\n/* responseData = {\n \"access\": \"eyJ0eXAiOiJKV1Q...\", // JWT access token (short-lived)\n \"refresh\": \"eyJ0eXAiOiJKV1Q...\", // JWT refresh token (long-lived)\n \"user\": {\n \"id\": 1,\n \"email\": \"user@example.com\",\n \"username\": \"user\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\"\n }\n} */\n\n// Save tokens\nlocalStorage.setItem('access_token', responseData.access);\nlocalStorage.setItem('refresh_token', responseData.refresh);\n```\n\n**3. Make Authenticated Requests**\n\n```javascript\nconst apiBaseUrl = 'http://localhost:8000';\nconst meApiEndpoint = `${apiBaseUrl}/api/auth/me/`;\nconst response = await fetch(meApiEndpoint, {\n headers: {\n 'Authorization': `Bearer ${localStorage.getItem('access_token')}`\n }\n});\n\nconst responseData = await response.json();\n/* responseData = {\n \"id\": 1,\n \"email\": \"user@example.com\",\n \"username\": \"user\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\"\n} */\n```\n\n**4. Logout**\n\n```javascript\nconst apiBaseUrl = 'http://localhost:8000';\nconst logoutApiEndpoint = `${apiBaseUrl}/api/auth/logout/`;\nconst response = await fetch(logoutApiEndpoint, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${localStorage.getItem('access_token')}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n refresh: localStorage.getItem('refresh_token')\n })\n});\n\nlocalStorage.removeItem('access_token');\nlocalStorage.removeItem('refresh_token');\n```\n\n## Configuration\n\n### Required Settings\n\n```python\nGOOGLE_OAUTH_CLIENT_ID = \"your-client-id\"\nGOOGLE_OAUTH_CLIENT_SECRET = \"your-client-secret\"\n```\n\n### Optional Settings\n\n```python\n# Return Google tokens in callback response (for calling Google APIs from frontend)\n# Default: False\nGOOGLE_OAUTH_RETURN_TOKENS = False\n\n# Revoke Google access on logout\n# Default: False\nGOOGLE_OAUTH_REVOKE_ON_LOGOUT = False\n\n# Save Google OAuth tokens to database (for backend Google API calls)\n# Default: True\nGOOGLE_OAUTH_SAVE_TOKENS_TO_DB = True\n\n# Request additional Google API scopes\n# Default: [\"openid\", \"email\", \"profile\"]\nGOOGLE_OAUTH_SCOPES = [\n \"openid\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/userinfo.profile\",\n # \"https://www.googleapis.com/auth/calendar\", # Add if needed\n]\n```\n\n## Google Cloud Setup\n\nFor Development:\n1. Go to [Google Cloud Console](https://console.cloud.google.com/)\n2. Create a project\n3. Go to **Google Auth Platform**\n4. Navigate to **Clients** > **+Create Client**\n\nFor Development use:\n\n- **Application type**: Web application\n- **Name**: Django Googler Dev\n- **Authorized redirect URIs**:\n - `http://localhost:8000/api/auth/google/callback/`\n - `http://localhost:8000/auth/google/callback/` (frontend handler for Django-based frontend)\n - `http://localhost:3000/auth/google/callback/` (frontend handler for React, Next.js or Vue)\n - Any others you might need during development\n\nFor Production use:\n\n- **Application type**: Web application\n- **Name**: Django Googler Prod\n- **Redirect URIs**:\n - `https://yourdomain.com/api/auth/google/callback/`\n - `https://yourdomain.com/auth/google/callback/` (frontend handler for Django or Next.js/React/Vue frontend)\n - Any others you might need (adding more is fine especially if you have multiple frontend frameworks)\n\nAfter you configure it, click **Create** and for _each environment_ (prod/dev) grab:\n\n- **Client ID** (such as `django-googler-dev.apps.googleusercontent.com` and `django-googler-prod.apps.googleusercontent.com`)\n- **Client Secret**\n\nFor dev, update your `.env` file with:\n\n```bash\nGOOGLE_OAUTH_CLIENT_ID=django-googler-dev.apps.googleusercontent.com\nGOOGLE_OAUTH_CLIENT_SECRET=your-dev-client-secret\n```\n\nFor prod, update your runtime secrets with:\n\n```bash\nGOOGLE_OAUTH_CLIENT_ID=django-googler-prod.apps.googleusercontent.com\nGOOGLE_OAUTH_CLIENT_SECRET=your-prod-client-secret\n```\n\n## Making Google API Calls\n\nIf your backend needs to call Google APIs on behalf of users:\n\n```python\nfrom django_googler.services import GoogleOAuthService\n\n\ndef my_view(request):\n # Get valid access token (auto-refreshes if expired)\n access_token, expiry = GoogleOAuthService.get_valid_token(request.user)\n\n if access_token:\n import requests\n\n headers = {\"Authorization\": f\"Bearer {access_token}\"}\n response = requests.get(\n \"https://www.googleapis.com/calendar/v3/calendars/primary/events\",\n headers=headers,\n )\n return response.json()\n```\n\n## Using Django Views (Instead of API)\n\nIf you prefer browser redirects over API calls:\n\n```python\n# urls.py\nurlpatterns = [\n path(\"auth/\", include(\"django_googler.urls.default\")),\n]\n```\n\nThen in your template:\n```html\n<a href=\"{% url 'django_googler:google-login' %}?next=/dashboard/\">\n Sign in with Google\n</a>\n```\n\nUsers will be redirected to Google and back, then logged into Django's session.\n\n## Architecture\n\n- **Views** - Handle OAuth flow and return JWT tokens\n- **Services** - Business logic for OAuth, users, and tokens\n- **Models** - Store Google OAuth tokens in database\n\n## License\n\nMIT License\n\n## Support\n\n[GitHub Issues](https://github.com/jmitchel3/django-googler/issues)\n",
"bugtrack_url": null,
"license": null,
"summary": "Django Googler is a simple way to integrate Google Auth Platform with your Django project.",
"version": "0.0.16",
"project_urls": {
"Changelog": "https://github.com/jmitchel3/django-googler",
"Documentation": "https://github.com/jmitchel3/django-googler",
"Funding": "https://github.com/jmitchel3/django-googler",
"Repository": "https://github.com/jmitchel3/django-googler"
},
"split_keywords": [
"auth",
" django",
" djangorestframework",
" google",
" oauth",
" oauth2",
" oauthlib"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "c6089c080d161a2394412e943a2878f2c1eb55fae82b7f67414233bee0397f45",
"md5": "fda19d8dea7436a2bf5b476eb5d79a50",
"sha256": "479f4f2357bef6f9c00fec02cd515ae27bd5ce849ba0607b8daa86527ee66500"
},
"downloads": -1,
"filename": "django_googler-0.0.16-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fda19d8dea7436a2bf5b476eb5d79a50",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 34118,
"upload_time": "2025-10-20T06:13:46",
"upload_time_iso_8601": "2025-10-20T06:13:46.184808Z",
"url": "https://files.pythonhosted.org/packages/c6/08/9c080d161a2394412e943a2878f2c1eb55fae82b7f67414233bee0397f45/django_googler-0.0.16-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7f17828ad2aa3c6236a28c5e5f0dced2abaf70f8e6ebba613b72428c113da21e",
"md5": "9c77c5be4ed76ae2162e6cb983c371fa",
"sha256": "d88f0c609f325199d7075a36d69ada96e75137f9d37843f28b85eee464644e89"
},
"downloads": -1,
"filename": "django_googler-0.0.16.tar.gz",
"has_sig": false,
"md5_digest": "9c77c5be4ed76ae2162e6cb983c371fa",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 63120,
"upload_time": "2025-10-20T06:13:47",
"upload_time_iso_8601": "2025-10-20T06:13:47.887308Z",
"url": "https://files.pythonhosted.org/packages/7f/17/828ad2aa3c6236a28c5e5f0dced2abaf70f8e6ebba613b72428c113da21e/django_googler-0.0.16.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-20 06:13:47",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jmitchel3",
"github_project": "django-googler",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-googler"
}