Troubleshooting ¶
Common Errors¶
redirect_uri_mismatch (Error 400)¶
flowchart TD
A["Error 400: redirect_uri_mismatch"] --> B{Check these}
B --> C["URI in Google Console matches exactly?"]
B --> D["Scheme matches? (http vs https)"]
B --> E["Host matches? (127.0.0.1 vs localhost)"]
B --> F["Path matches? (trailing slash?)"]
C --> G["✅ Fix in Google Cloud Console"]
D --> G
E --> G
F --> G
The redirect URI in the request does not match
Cause: The redirect URI your app generates doesn't match what's in Google Console.
Fix:
- Check your Django server URL (is it
127.0.0.1orlocalhost?) - Go to Google Cloud Console → Credentials → Your OAuth Client
- Set Authorized redirect URIs to exactly:
http://127.0.0.1:8000/gauth/login-callback - No trailing slash!
ModuleNotFoundError: No module named 'django_gauth'¶
App not found
Cause: Package not installed in your active Python environment.
Fix:
Or check you're using the correct virtual environment:
System Check Error: django_gauth.E003¶
GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET missing
Cause: Required credentials not in settings.
Fix: Add to settings.py:
System Check Error: django_gauth.E002¶
SessionMiddleware not in MIDDLEWARE
Cause: Django Gauth needs sessions to store OAuth state.
Fix: Ensure your MIDDLEWARE includes:
400 Bad Request — "Invalid or missing OAuth state"¶
Callback rejected before token exchange
Cause: The state returned by Google didn't match the value stored in the session.
This usually means the session expired, cookies were cleared, the callback link was
replayed/bookmarked, or a possible CSRF attempt.
Fix: Start the flow again from /gauth/login/. Ensure cookies/sessions are enabled
and that the same browser/session completes the round-trip. This is expected, safe
behaviour — django-gauth returns a clear 400 instead of a raw oauthlib stack trace.
oauthlib.oauth2.rfc6749.errors.InsecureTransportError¶
OAuth2 must use HTTPS
Cause: Running on HTTP without the insecure transport flag.
Fix (development only):
Fix (production): Use HTTPS!
Credentials expire immediately¶
Token expired
Cause: The exp claim in the ID token has passed.
Possible reasons:
- Server clock is wrong
- Session hasn't been refreshed
Fix: Ensure your server's clock is synchronized (use NTP).
PKCE / token-exchange failures on django-gauth < 0.2.1¶
Pin google-auth-oauthlib if you use django-gauth < 0.2.1
Cause: google-auth-oauthlib >= 1.3.0 changed its PKCE (Proof Key for
Code Exchange) handling. Releases of django-gauth older than 0.2.1 do
not pin this dependency, so a fresh install can pull >= 1.3.0 and break the
OAuth callback — the authorization request and token exchange disagree, causing
token-exchange failures.
Fix (staying on an old version): pin the dependency explicitly in your Django project's requirements:
Recommended fix: upgrade to django-gauth >= 0.2.1, which already pins
google-auth-oauthlib<1.3.0,>=1.0.0 for you — no manual pin needed.
Debugging Tips¶
Enable Debug Endpoint¶
When DEBUG=True, access session data at:
This returns a JSON response with sanitized session data (no raw secrets).
Check Django System Checks¶
This runs all Django Gauth validators and reports issues.
Inspect Session Data¶
# In Django shell or a view:
def my_debug_view(request):
print(request.session.keys())
print(request.session.get("credentials"))
print(request.session.get("id_info"))
Common Debugging Flowchart¶
flowchart TD
A[Something's not working] --> B{Can you reach /gauth/?}
B -->|No| C[Check INSTALLED_APPS & urls.py]
B -->|Yes| D{Does login redirect to Google?}
D -->|No| E[Check GOOGLE_CLIENT_ID in settings]
D -->|Yes| F{Does Google show an error?}
F -->|redirect_uri_mismatch| G[Fix redirect URI in Console]
F -->|No, consent works| H{Does callback succeed?}
H -->|Error in callback| I[Check GOOGLE_CLIENT_SECRET]
H -->|Yes| J[✅ Working!]
style J fill:#4CAF50,color:white
Known Limitations¶
gauth_required / GauthRequiredMixin do not support Django REST Framework¶
Django native views only
Cause: The view-protection helpers
(gauth_required and GauthRequiredMixin) are built
against Django's native view contract — django.http.HttpRequest,
request.session, and request.user as populated by AuthenticationMiddleware.
Django REST Framework views
(APIView, ViewSet, @api_view) use a different contract: request.user is
populated by DRF's authentication_classes (not Django's middleware), access
control is expected to run through permission_classes, and unauthorized
responses are raised as DRF exceptions (rendered 401/403) rather than returned
as a 302 redirect. Applying these helpers to DRF endpoints may therefore behave
incorrectly.
Workaround: Protect DRF endpoints with DRF's own authentication and permission classes for now.
Status: DRF-native adapters (a BaseAuthentication + BasePermission pair
reusing django-gauth's session-lifecycle core) are planned. Track or upvote the
feature request on GitHub.
Still Stuck?¶
- Check the GitHub Issues
- Open a new issue with:
- Your Django version
- Your Python version
- The full error traceback
- Your settings (with secrets removed!)