React doesn’t receive API tokens after Google OAuth redirect via social_django/drf-social-oauth2
I'm implementing Google OAuth2 for my Django REST API with a React frontend. The basic flow is set up correctly. I have routes for:
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/", include((v1_patterns, "v1"))),
path("api/v1/auth/", include("social_django.urls", namespace="social")),
path("api/v1/auth/token/", include("drf_social_oauth2.urls", namespace="oauth2")),
]
Also I have:
INSTALLED_APPS = [
...
"drf_yasg",
"social_django",
"oauth2_provider",
"drf_social_oauth2",
...
]
SOCIAL_AUTH_URL_NAMESPACE = "social"
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"social_core.backends.google.GoogleOAuth2",
"drf_social_oauth2.backends.DjangoOAuth2",
)
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = config("GOOGLE_OAUTH_CLIENT_ID")
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = config("GOOGLE_OAUTH_CLIENT_SECRET")
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ["email", "profile"]
SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_DATA = [
"email",
"first_name",
"last_name",
"picture",
]
LOGIN_REDIRECT_URL = "http://127.0.0.1:5173"
# REST Framework general settings
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",
"drf_social_oauth2.authentication.SocialAuthentication",
)
}
For login, I sent a GET request from browser to http://localhost:8000/api/v1/auth/login/google-oauth2/
After sending the request, in the console I get:
api_container | [24/Jun/2025 13:04:19] "GET /api/v1/auth/login/google-oauth2/ HTTP/1.1" 302 0
api_container | [24/Jun/2025 13:04:20] "GET /api/v1/auth/complete/google-oauth2/?state=KYTUm5yi1NheUmc095zaRqIc3mZjsOLp&code=4%2F0AUJR-x4dcWMTghJHjPc1QbiPrCFo5lo2u9l1cYJ47F61fB0kIkQe4I0DFAt33UZOPBBI8g&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid&authuser=0&prompt=none HTTP/1.1" 302 0
After a successful login, I get redirected to my React app (127.0.0.1:5173) — but the problem is, no tokens (access or refresh) are passed to the client. I don’t know how to retrieve or send them so that React can authenticate future API calls.
I can confirm that after login, the database records email, picture, first_name, last_name, and an access token (from Google), but React receives nothing.
The data, i have in database on extra_data field:
{
"hd": "domain",
"email": "my_email",
"expires": 3599,
"picture": "link",
"auth_time": 1750762274,
"last_name": "Doe",
"first_name": "John",
"token_type": "Bearer",
"access_token": "token"
}
What I need help with:
- How can I extract and send backend-generated access/refresh tokens to React after a successful login?
- Should I use convert-token endpoint? If yes — what request should I make and where?
- Or should I modify the redirect flow to include tokens in the URL fragment or similar?
I'd like to learn how to integrate the full flow — from Google login → React POST to /convert-token/
→ store tokens → use tokens in authenticated API calls.
What I’ve tried so far:
- Configured
social_django
anddrf-social-oauth2
- Verified redirect cycles and database fields
- Examined docs, but no working React integration example
Well. After much searching and trying, i came up with this option.
Solution: JWT with Google OAuth via Custom Pipeline & Cookie-Based Refresh.
In settings.py
, set up the SOCIAL_AUTH_PIPELINE
:
SOCIAL_AUTH_PIPELINE = (
"social_core.pipeline.social_auth.social_details",
"social_core.pipeline.social_auth.social_uid",
"social_core.pipeline.social_auth.auth_allowed",
"social_core.pipeline.social_auth.social_user",
"social_core.pipeline.social_auth.associate_by_email",
"social_core.pipeline.user.create_user",
"social_core.pipeline.social_auth.associate_user",
"social_core.pipeline.social_auth.load_extra_data",
"social_core.pipeline.user.user_details",
"users.pipeline.get_token_google_oauth", # <— Your custom step
)
Custom pipeline function: get_token_google_oauth
# users/pipeline.py
from django.shortcuts import redirect
from rest_framework_simplejwt.tokens import RefreshToken
def get_token_google_oauth(strategy, details, user=None, *args, **kwargs):
"""
After Google auth, generate JWT and send to client via cookies and redirect.
"""
if not user:
return redirect("http://127.0.0.1:5173/auth/error")
refresh = RefreshToken.for_user(user)
access = str(refresh.access_token)
refresh_t = str(refresh)
response = redirect("http://127.0.0.1:5173/")
response.set_cookie(
"refresh_token",
refresh_t,
httponly=True,
secure=False,
samesite="Lax",
path="/api/v1/auth/token/refresh/",
)
# Optionally expose the access token
response.set_cookie(
"access_token",
access,
httponly=False,
secure=False,
samesite="Lax",
)
return response
This pipeline step:
1. Creates JWT tokens (access
+ refresh
).
2. Stores refresh_token
as an HttpOnly cookie scoped to refresh URL.
3. Exposes access_token
to front-end via a non-HttpOnly cookie.
4. Redirects the user to your React app
Refresh endpoint: TokenRefreshFromCookieView
# urls.py
from django.urls import path, include
from rest_framework_simplejwt.views import TokenRefreshView
from .views import TokenRefreshFromCookieView
urlpatterns = [
path("api/v1/auth/", include("social_django.urls", namespace="social")),
path(
"api/v1/auth/token/refresh/",
TokenRefreshFromCookieView.as_view(),
name="token_refresh",
),
]
Custom view to refresh tokens via cookies:
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.serializers import TokenRefreshSerializer
class TokenRefreshFromCookieView(APIView):
"""
Refresh access token using a refresh token from cookie or request body.
"""
def post(self, request, *args, **kwargs):
refresh = request.data.get("refresh") or request.COOKIES.get("refresh_token")
if not refresh:
raise AuthenticationFailed("No refresh token provided.")
serializer = TokenRefreshSerializer(data={"refresh": refresh})
serializer.is_valid(raise_exception=True)
access = serializer.validated_data["access"]
new_refresh = serializer.validated_data.get("refresh", refresh)
resp = Response({"access": access}, status=status.HTTP_200_OK)
resp.set_cookie(
"refresh_token",
new_refresh,
httponly=True,
secure=False,
samesite="Lax",
path="/api/v1/auth/token/refresh/",
)
return resp
Flow Summary:
User logs in via
/auth/login/google-oauth2/
.The pipeline creates JWT, sets cookies, and redirects to React.
Front-end reads
access_token
from cookie and uses it for API requests.After expiration, front-end triggers:
POST /api/v1/auth/token/refresh/
browser automatically sends the HttpOnly5. View returns a new
access
token (and optionally a newrefresh
) in cookies and body.
If you see any problems or have any suggestions, I'd be happy to hear them.
The reason your React application is not receiving any tokens following Google login is that Django manages the OAuth flow and authenticates the user, but it does not automatically transmit the access or refresh tokens from your backend to the frontend. Once Google redirects back to Django, it simply logs the user in and redirects to your LOGIN_REDIRECT_URL (which is your React application), but does not include any tokens. To resolve this issue, you must introduce an additional step: develop a custom Django view that intercepts the redirect, retrieves the Google access token from the logged-in user’s social authentication data, and subsequently redirects to your React application with that token included in the URL (for example, ?google_token=...). Within your React application, extract that token from the URL and promptly send a POST request to /api/v1/auth/token/convert-token/, including your client_id, client_secret, backend=google-oauth2, and the token you received. This endpoint will provide you with your own API's access and refresh tokens, which you can then store and utilize for all subsequent authenticated API requests. Essentially, Django has completed its task; now React simply needs to invoke /convert-token/ to finalize the process.