DEV Community

cycy
cycy

Posted on

How to Set Up Google OAuth2 Authentication in a Django REST API

Integrating Google OAuth2 authentication in a Django REST API can be tricky, especially with changing library APIs and the need to support both web and mobile clients. Here’s a practical, robust approach that avoids common pitfalls and works reliably with modern Django and Google APIs.


1. Why Not Use Allauth Adapters Directly?

While django-allauth is a popular library for social authentication, its internal APIs (like get_app and direct use of adapters) can change or become unstable. Relying on these can lead to hard-to-debug errors, especially after library upgrades.

Solution:

Use the official Google OAuth2 HTTP endpoints directly for the callback, and only use allauth for registration flows if you need them.


2. The OAuth2 Flow Overview

  1. User clicks “Sign in with Google” on your frontend.
  2. Frontend redirects user to Google’s OAuth2 consent screen.
  3. Google redirects back to your backend callback with a code parameter.
  4. Backend exchanges the code for tokens using Google’s token endpoint.
  5. Backend fetches user info from Google using the access token.
  6. Backend creates or retrieves the user in your database.
  7. Backend generates JWT tokens for your frontend.
  8. Backend redirects the user to the frontend dashboard with tokens.

3. Backend Implementation Steps

a. Environment Variables

Set these in your .env file:

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=http://localhost:8002/api/users/auth/google/callback/
FRONTEND_URL=http://localhost:3001
Enter fullscreen mode Exit fullscreen mode

b. Django Settings

Add or update in settings.py:

SOCIALACCOUNT_PROVIDERS = {
    "google": {
        "APP": {
            "client_id": os.environ.get("GOOGLE_CLIENT_ID"),
            "secret": os.environ.get("GOOGLE_CLIENT_SECRET"),
            "key": "",
        },
        "SCOPE": [
            "profile",
            "email",
        ],
        "AUTH_PARAMS": {
            "access_type": "online",
        },
    }
}
GOOGLE_CALLBACK_URL = os.environ.get("GOOGLE_CALLBACK_URL")
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET")
FRONTEND_URL = os.environ.get("FRONTEND_URL")
Enter fullscreen mode Exit fullscreen mode

c. OAuth2 Callback View

Instead of using allauth’s adapter methods, use direct HTTP requests:

# views.py or google_auth_views.py

import requests
from django.conf import settings
from django.core.exceptions import ValidationError
from django.shortcuts import redirect
from rest_framework.views import APIView

class GoogleOAuthCallbackView(APIView):
    def get(self, request, *args, **kwargs):
        code = request.GET.get("code")
        if not code:
            return redirect(f"{settings.FRONTEND_URL}/login?error=oauth_no_code")

        try:
            # Exchange code for tokens
            token_url = "https://oauth2.googleapis.com/token"
            token_data = {
                'client_id': settings.GOOGLE_CLIENT_ID,
                'client_secret': settings.GOOGLE_CLIENT_SECRET,
                'code': code,
                'grant_type': 'authorization_code',
                'redirect_uri': settings.GOOGLE_CALLBACK_URL,
            }
            token_response = requests.post(token_url, data=token_data)
            token_response.raise_for_status()
            token_info = token_response.json()

            # Get user info
            user_info_url = f"https://www.googleapis.com/oauth2/v2/userinfo?access_token={token_info['access_token']}"
            user_response = requests.get(user_info_url)
            user_response.raise_for_status()
            user_data = user_response.json()

            email = user_data.get('email')
            if not email:
                raise ValidationError("Email not provided by Google")

            # Create or get user (pseudo-code, adapt to your User model)
            user, created = User.objects.get_or_create(
                email=email,
                defaults={
                    'first_name': user_data.get('given_name', ''),
                    'last_name': user_data.get('family_name', ''),
                }
            )

            # Create user profile if needed (pseudo-code)
            create_user_profile(user, user_type="sender")  # or "rider"

            # Generate JWT tokens (using SimpleJWT)
            from rest_framework_simplejwt.tokens import RefreshToken
            refresh = RefreshToken.for_user(user)
            tokens = {
                "access": str(refresh.access_token),
                "refresh": str(refresh)
            }

            # Redirect to frontend dashboard with tokens as query params
            dashboard_url = f"{settings.FRONTEND_URL}/dashboard"
            redirect_url = f"{dashboard_url}?access_token={tokens['access']}&refresh_token={tokens['refresh']}"
            return redirect(redirect_url)

        except Exception as e:
            return redirect(f"{settings.FRONTEND_URL}/login?error=auth_failed")
Enter fullscreen mode Exit fullscreen mode

4. Frontend Handling

  • After redirect, the frontend should read the tokens from the URL and store them (e.g., in localStorage or cookies).
  • Use these tokens for authenticated API requests.

5. Common Pitfalls & Lessons Learned

  • Don’t rely on allauth’s internal adapter methods for the callback—they may change or break.
  • Always use the official Google OAuth2 endpoints for exchanging codes and fetching user info.
  • Keep your callback URL and Google Cloud Console settings in sync (including port numbers).
  • Handle errors gracefully and always redirect to a safe frontend route with an error message if something fails.
  • Deprecation warnings from dj-rest-auth/allauth are safe to ignore if you use the new config style.

6. Conclusion

This approach is robust, simple, and easy to maintain.

If you ever upgrade Django, allauth, or dj-rest-auth, you won’t be caught off guard by breaking API changes in their internals.

Just use the official OAuth2 endpoints and keep your user creation/profile logic in your own code.


Bookmark this guide for future projects!

It will save you hours of debugging and frustration.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.