Migrate the google SSO from openid to oauth2

(imported from commit 6938c1cc5d245cc5642043279470365ff04df903)
This commit is contained in:
Jason Michalski 2015-01-28 23:59:41 -08:00
parent 3f68c56554
commit 439b86fe3b
5 changed files with 94 additions and 2 deletions

View File

@ -41,7 +41,7 @@ $(function () {
company email address to sign up. Otherwise, we wont be able to
connect you with your coworkers.</div>
<div class="register-google">
<a href="/accounts/login/openid/" class="zocial google register-google-button">Sign up with Google</a>
<a href="{% url 'zerver.views.start_google_oauth2' %}" class="zocial google register-google-button">Sign up with Google</a>
</div>
</div>

View File

@ -92,7 +92,7 @@ autofocus('#id_username');
{% if not desktop_sso_dispatch %}
<div class="login-google">
or <a href="/accounts/login/openid/" class="login-google-button zocial google">Sign in with Google</a>
or <a href="{% url 'zerver.views.start_google_oauth2' %}" class="login-google-button zocial google">Sign in with Google</a>
</div>
{% endif %}

View File

@ -15,6 +15,7 @@ from django.contrib.auth.views import login as django_login_page, \
logout_then_login as django_logout_then_login
from django.db.models import Q, F
from django.core.mail import send_mail, EmailMessage
from django.middleware.csrf import get_token
from django.db import transaction
from zerver.models import Message, UserProfile, Stream, Subscription, \
Recipient, Realm, UserMessage, bulk_get_recipients, \
@ -74,6 +75,8 @@ from zproject.backends import password_auth_enabled
from confirmation.models import Confirmation
import requests
import subprocess
import calendar
import datetime
@ -86,6 +89,8 @@ import time
import logging
import os
import jwt
import hashlib
import hmac
from collections import defaultdict
from zerver.lib.rest import rest_dispatch as _rest_dispatch
@ -697,6 +702,83 @@ def handle_openid_errors(request, issue, openid_response=None):
def process_openid_login(request):
return login_complete(request, render_failure=handle_openid_errors)
def google_oauth2_csrf(request, value):
return hmac.new(get_token(request).encode('utf-8'), value, hashlib.sha256).hexdigest()
def start_google_oauth2(request):
uri = 'https://accounts.google.com/o/oauth2/auth?'
cur_time = str(int(time.time()))
csrf_state = '{}:{}'.format(
cur_time,
google_oauth2_csrf(request, cur_time),
)
prams = {
'response_type': 'code',
'client_id': settings.GOOGLE_OAUTH2_CLIENT_ID,
'redirect_uri': ''.join((
settings.EXTERNAL_URI_SCHEME,
settings.EXTERNAL_HOST,
reverse('zerver.views.finish_google_oauth2'),
)),
'scope': 'profile email',
'state': csrf_state,
}
return redirect(uri + urllib.urlencode(prams))
def finish_google_oauth2(request):
error = request.GET.get('error')
if error == 'access_denied':
return redirect('/')
elif error is not None:
logging.error('Error from google oauth2 login %r', request.GET)
return HttpResponse(status=400)
value, hmac_value = request.GET.get('state').split(':')
if hmac_value != google_oauth2_csrf(request, value):
raise Exception('Google oauth2 CSRF error')
resp = requests.post(
'https://www.googleapis.com/oauth2/v3/token',
data={
'code': request.GET.get('code'),
'client_id': settings.GOOGLE_OAUTH2_CLIENT_ID,
'client_secret': settings.GOOGLE_OAUTH2_CLIENT_SECRET,
'redirect_uri': ''.join((
settings.EXTERNAL_URI_SCHEME,
settings.EXTERNAL_HOST,
reverse('zerver.views.finish_google_oauth2'),
)),
'grant_type': 'authorization_code',
},
)
if resp.status_code != 200:
raise Exception('Could not convert google pauth2 code to access_token\r%r' % resp.text)
access_token = resp.json['access_token']
resp = requests.get(
'https://www.googleapis.com/plus/v1/people/me',
params={'access_token': access_token}
)
if resp.status_code != 200:
raise Exception('Google login failed making API call\r%r' % resp.text)
body = resp.json
try:
full_name = body['name']['formatted']
except KeyError:
# Only google+ users have a formated name. I am ignoring i18n here.
full_name = '{} {}'.format(
body['name']['givenName'], body['name']['familyName']
)
for email in body['emails']:
if email['type'] == 'account':
break
else:
raise Exception('Google oauth2 account email not found %r' % body)
email_address = email['value']
user_profile = authenticate(username=email_address, use_dummy_backend=True)
return login_or_register_remote_user(request, email_address, user_profile, full_name)
def login_page(request, **kwargs):
template_response = django_login_page(
request, authentication_form=OurAuthenticationForm, **kwargs)

View File

@ -143,6 +143,14 @@ else:
GOOGLE_CLIENT_ID = "835904834568-77mtr5mtmpgspj9b051del9i9r5t4g4n.apps.googleusercontent.com"
if DEPLOYED:
GOOGLE_OAUTH2_CLIENT_ID = ''
GOOGLE_OAUTH2_CLIENT_SECRET = ''
else:
# Google OAUTH2 for dev with the redirect uri set to http://localhost:9991/accounts/login/google/done/
GOOGLE_OAUTH2_CLIENT_ID = '607830223128-4qgthc7ofdqce232dk690t5jgkm1ce33.apps.googleusercontent.com'
GOOGLE_OAUTH2_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxx'
# Administrator domain for this install
ADMIN_DOMAIN = "zulip.com"

View File

@ -24,6 +24,8 @@ urlpatterns = patterns('',
url(r'^accounts/login/openid/done/$', 'django_openid_auth.views.login_complete', name='openid-complete'),
url(r'^accounts/login/sso/$', 'zerver.views.remote_user_sso', name='login-sso'),
url(r'^accounts/login/jwt/$', 'zerver.views.remote_user_jwt', name='login-jwt'),
url(r'^accounts/login/google/$', 'zerver.views.start_google_oauth2'),
url(r'^accounts/login/google/done/$', 'zerver.views.finish_google_oauth2'),
# We have two entries for accounts/login to allow reverses on the Django
# view we're wrapping to continue to function.
url(r'^accounts/login/', 'zerver.views.login_page', {'template_name': 'zerver/login.html'}),