Skip to content

Instantly share code, notes, and snippets.

@alvarovelezgalvez
Created October 15, 2020 09:15
Show Gist options
  • Save alvarovelezgalvez/f97231c3f92320f47c6b0b04ea9e05c3 to your computer and use it in GitHub Desktop.
Save alvarovelezgalvez/f97231c3f92320f47c6b0b04ea9e05c3 to your computer and use it in GitHub Desktop.
Google One Tap Sample View
class GoogleOneTapLoginView(ProcessFormView):
"""
Class to manage Google One Tap Login View. It's not compatible yet with Django Social Auth
"""
def post(self, request, *args, **kwargs):
import jwt
try:
# Decode JWT token
credential = request.POST['credential']
decoded_credentials = jwt.decode(credential, verify=False)
now = datetime.now()
# Check if all data are correct
if 'exp' in decoded_credentials \
and 'iat' in decoded_credentials \
and 'nbf' in decoded_credentials \
and 'email_verified' in decoded_credentials \
and 'iss' in decoded_credentials \
and 'azp' in decoded_credentials \
and 'aud' in decoded_credentials \
and 'email' in decoded_credentials \
and 'given_name' in decoded_credentials \
and 'family_name' in decoded_credentials \
and datetime.fromtimestamp(decoded_credentials['exp']) > now \
and datetime.fromtimestamp(decoded_credentials['iat']) < now \
and datetime.fromtimestamp(decoded_credentials['nbf']) < now \
and decoded_credentials['email_verified'] == True \
and decoded_credentials['iss'] == settings.SOCIAL_AUTH_GOOGLE_ISSUER \
and decoded_credentials['azp'] == settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY \
and decoded_credentials['aud'] == settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY :
if 'picture' in decoded_credentials:
profile_picture_url = decoded_credentials['picture']
else:
profile_picture_url = None
gv_user = get_or_create_user(decoded_credentials['email'],
decoded_credentials['given_name'],
decoded_credentials['family_name'],
None,
None,
profile_picture_url)
from django.contrib.auth import login
login(request,
gv_user,
backend='django.contrib.auth.backends.ModelBackend')
messages.add_message( request, messages.SUCCESS, "%s %s" % (_('Logged in using '), 'Google' ) )
return redirect( request.POST['redirect_url'] )
except Exception as e:
logger.exception(e)
# If any of the checks fails, we will show an error to the user
messages.add_message( request, messages.ERROR, _('There was an error when registering you. Please, check the permissions in your social network and try again') )
return redirect( request.POST['redirect_url'] )
@Jenselme
Copy link

Thanks for sharing, it was of great help! I'm more strict with validation (and use the validation method supplied by google):

@never_cache
@csrf_exempt
def google_one_tap_login(request):
    google_csrf_cookie_value = request.COOKIES.get('g_csrf_token')
    csrf_token = request.POST.get('g_csrf_token')
    if not google_csrf_cookie_value or not csrf_token or google_csrf_cookie_value != csrf_token:
        logger.info('Invalid CSRF token for one tap login')
        return HttpResponseForbidden('Invalid google CSRF token.')

    # Taken from the google documentation: https://developers.google.com/identity/sign-in/web/backend-auth#using-a-google-api-client-library  # noqa: E501
    try:
        # Specify the CLIENT_ID of the app that accesses the backend:
        idinfo = id_token.verify_oauth2_token(
            request.POST.get('credential'),
            google_requests.Request(),
            settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY,
        )
    except ValueError:
        logger.info('Invalid token in google one tap login')
        return HttpResponseForbidden('Invalid token')

    UserModel = get_user_model()
    user, __ = UserModel.objects.get_or_create(
        username=idinfo['email'],
        defaults={
            'email': idinfo['email'],
            'first_name': idinfo['given_name'],
            'last_name': idinfo['family_name'],
        },
    )
    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')

    return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('website:home')))

@alvarovelezgalvez
Copy link
Author

Seems great for me!

Whenever I have some spare time I will try to implement this validation as well.

Thanks,
ÁLvaro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment