IOS/web Auth Client ID Handling for Google Sign In
To preface, I'm not asking for a direct fix here, I'm just curious if what I'm doing is the appropriate auth flow for setting dynamic client ID based on device platform.
I am 2 applications that use the same Django Allauth backend. One of them is for web, and the other is in Flutter (iOS). Both applications would call an endpoint that routes to GoogleDirectLogin(APIView)
note that the implementation I currently have a method get_client_id that dynamically use the appropriate client ID based on device type (X-Client-Type)
class GoogleDirectLogin(APIView):
permission_classes = [AllowAny]
def post(self, request):
# Get token from request
token = request.data.get('id_token') or request.data.get('access_token')
if not token:
return Response(
{'error': 'Missing token in request'},
status=status.HTTP_400_BAD_REQUEST
)
# importing middleware is crucial for checking multiple client ID based on JSON Header value
from auth_backend.middleware import get_client_id # Import from middleware
client_id = get_client_id(request)
print(f"using client ID: {client_id}")
try:
# Verify Google token
identity_data = id_token.verify_oauth2_token(
token,
google_requests.Request(),
client_id,
clock_skew_in_seconds=10
)
# Validate issuer
if identity_data.get('iss') not in ['accounts.google.com', 'https://accounts.google.com']:
return Response(
{'error': 'Invalid token issuer'},
status=status.HTTP_400_BAD_REQUEST
)
# # Exchange token with your internal API
response = requests.post(
settings.INTERNAL_API_CALL_GOOGLE,
json={'access_token': token}
)
response.raise_for_status()
auth_data = response.json()
return Response({
'access': auth_data['access'],
'refresh': auth_data['refresh'],
'user_id': auth_data['user']['user_id'],
'username': auth_data['user']['username'],
'is_approved': auth_data['user']['is_approved'],
'is_orcid_valid': auth_data['user']['is_orcid_valid']
})
except ValueError as e:
return Response(
{'error': f'Token verification failed: {str(e)}'},
status=status.HTTP_401_UNAUTHORIZED
)
except jwt.PyJWTError as e:
return Response(
{'error': f'JWT processing error: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
except requests.exceptions.RequestException as e:
return Response(
{'error': f'Internal API error: {str(e)}'},
status=status.HTTP_503_SERVICE_UNAVAILABLE
)
def get_client_id(request):
"""Determine client ID from session or header"""
# Try to get from session first (for callbacks)
client_type = request.session.get('oauth_client_type')
# Then check header (for initial requests)
if not client_type and 'X-Client-Type' in request.headers:
client_type = request.headers['X-Client-Type'].lower()
# Store in session for callback phase
request.session['oauth_client_type'] = client_type
print(f"Stored client_type: {client_type} in session")
# Map to client IDs
if client_type == 'ios':
print("USING IOS")
return config("GOOGLE_OAUTH_CLIENT_ID_IOS")
print("USING WEB")
return config("GOOGLE_OAUTH_CLIENT_ID_WEB")
and I have a custom middleware to handle this dynamic client ID selection:
class DynamicGoogleClientMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Only process OAuth-related requests
if self.is_oauth_request(request.path):
client_id = get_client_id(request)
# Update settings dynamically
if hasattr(settings, 'SOCIALACCOUNT_PROVIDERS'):
settings.SOCIALACCOUNT_PROVIDERS['google']['APP']['client_id'] = client_id
settings.SOCIALACCOUNT_PROVIDERS['google_jwt']['APP']['client_id'] = client_id
response = self.get_response(request)
# Cleanup after OAuth callback
if self.is_callback_request(request.path) and 'oauth_client_type' in request.session:
print(f"Clearing oauth_client_type from session after callback")
del request.session['oauth_client_type']
return response
in my settings.py, I have modified my SOCIALACCOUNT_PROVIDERS so that it initially sets to a dummy client ID value, as I am certain it would get replaced by get_client_id's returned client ID.
SOCIALACCOUNT_PROVIDERS = {
"google": {
"APP": {
"client_id": "dummy-client-id", # to be filled dynamically by middleware, based on X-Client-Type header
"secret": GOOGLE_OAUTH_SECRET_KEY, # must keep this
"key": "",
},
"SCOPE": [
"profile",
"email",
],
"AUTH_PARAMS": {
"access_type": "online",
},
"VERIFIED_EMAIL": True,
},
# For tying a JWT to the Google social login process.
"google_jwt": {
"APP": {
"client-id": "dummy-client-id",
"secret": GOOGLE_OAUTH_SECRET_KEY,
"key": "",
},
"SCOPE": [
"profile",
"email",
],
"AUTH_PARAMS": {
"access_type": "online",
},
"VERIFIED_EMAIL": True,
},
}
This flow works in the development environment, but once I deploy it to production, I hit
Google authentication failed - Login failed: 503 - {"error":"Internal API error: 500 Server Error: Internal Server Error for url: https://bpm-portal-testbed.cnsi.ucsb.edu/api/auth/google/"} flutter: Error logging in: Exception: Google authentication failed - Failed to get user.
Again, I'm not asking for a direct fix, but rather a nudge as to how I should approach a scenario like this.