Add support for OAuth1 logins
authorMagnus Hagander <magnus@hagander.net>
Tue, 25 Jun 2019 14:18:15 +0000 (16:18 +0200)
committerMagnus Hagander <magnus@hagander.net>
Tue, 25 Jun 2019 14:18:15 +0000 (16:18 +0200)
Most providers today are OAuth2, but not all. Much is the same, but some
parts of the flow are fundamentally completely different. So we need a
separate implementation for it.

pgweb/account/oauthclient.py

index 794e383455a8ab6ca3ec186ca43f1d3ad067d841..e832d05d6b0f40a2c4ea5d07450e8de6b8bb55b7 100644 (file)
@@ -25,8 +25,37 @@ def configure():
     os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
 
 
+def _perform_oauth_login(request, provider, email, firstname, lastname):
+    try:
+        user = User.objects.get(email=email)
+    except User.DoesNotExist:
+        log.info("Oauth signin of {0} using {1} from {2}. User not found, offering signup.".format(email, provider, get_client_ip(request)))
+
+        # Offer the user a chance to sign up. The full flow is
+        # handled elsewhere, so store the details we got from
+        # the oauth login in the session, and pass the user on.
+        request.session['oauth_email'] = email
+        request.session['oauth_firstname'] = firstname or ''
+        request.session['oauth_lastname'] = lastname or ''
+        return HttpResponseRedirect('/account/signup/oauth/')
+
+    log.info("Oauth signin of {0} using {1} from {2}.".format(email, provider, get_client_ip(request)))
+    if UserProfile.objects.filter(user=user).exists():
+        if UserProfile.objects.get(user=user).block_oauth:
+            log.warning("Account {0} ({1}) is blocked from OAuth login".format(user.username, email))
+            return HttpResponse("OAuth login not allowed to this account.")
+
+    user.backend = settings.AUTHENTICATION_BACKENDS[0]
+    django_login(request, user)
+    n = request.session.pop('login_next')
+    if n:
+        return HttpResponseRedirect(n)
+    else:
+        return HttpResponseRedirect('/account/')
+
+
 #
-# Generic OAuth login for multiple providers
+# Generic OAuth2 login for multiple providers
 #
 def _login_oauth(request, provider, authurl, tokenurl, scope, authdatafunc):
     from requests_oauthlib import OAuth2Session
@@ -58,32 +87,7 @@ def _login_oauth(request, provider, authurl, tokenurl, scope, authdatafunc):
             log.warning("Oauth signing using {0} was missing data: {1}".format(provider, e))
             return HttpResponse('OAuth login was missing critical data. To log in, you need to allow access to email, first name and last name!')
 
-        try:
-            user = User.objects.get(email=email)
-        except User.DoesNotExist:
-            log.info("Oauth signin of {0} using {1} from {2}. User not found, offering signup.".format(email, provider, get_client_ip(request)))
-
-            # Offer the user a chance to sign up. The full flow is
-            # handled elsewhere, so store the details we got from
-            # the oauth login in the session, and pass the user on.
-            request.session['oauth_email'] = email
-            request.session['oauth_firstname'] = firstname or ''
-            request.session['oauth_lastname'] = lastname or ''
-            return HttpResponseRedirect('/account/signup/oauth/')
-
-        log.info("Oauth signin of {0} using {1} from {2}.".format(email, provider, get_client_ip(request)))
-        if UserProfile.objects.filter(user=user).exists():
-            if UserProfile.objects.get(user=user).block_oauth:
-                log.warning("Account {0} ({1}) is blocked from OAuth login".format(user.username, email))
-                return HttpResponse("OAuth login not allowed to this account.")
-
-        user.backend = settings.AUTHENTICATION_BACKENDS[0]
-        django_login(request, user)
-        n = request.session.pop('login_next')
-        if n:
-            return HttpResponseRedirect(n)
-        else:
-            return HttpResponseRedirect('/account/')
+        return _perform_oauth_login(request, provider, email, firstname, lastname)
     else:
         log.info("Initiating {0} oauth2 step from {1}".format(provider, get_client_ip(request)))
         # First step is redirect to provider
@@ -97,6 +101,51 @@ def _login_oauth(request, provider, authurl, tokenurl, scope, authdatafunc):
         return HttpResponseRedirect(authorization_url)
 
 
+#
+# Generic Oauth1 provider
+#
+def _login_oauth1(request, provider, requesturl, accessurl, baseauthurl, authdatafunc):
+    from requests_oauthlib import OAuth1Session
+
+    client_id = settings.OAUTH[provider]['clientid']
+    client_secret = settings.OAUTH[provider]['secret']
+    redir = '{0}/account/login/{1}/'.format(settings.SITE_ROOT, provider)
+
+    if 'oauth_verifier' in request.GET:
+        log.info("Completing {0} oauth1 step from {1}".format(provider, get_client_ip(request)))
+
+        oa = OAuth1Session(client_id, client_secret)
+        r = oa.parse_authorization_response(request.build_absolute_uri())
+        verifier = r.get('oauth_verifier')
+
+        ro_key = request.session.pop('ro_key')
+        ro_secret = request.session.pop('ro_secret')
+
+        oa = OAuth1Session(client_id, client_secret, ro_key, ro_secret, verifier=verifier)
+        tokens = oa.fetch_access_token(accessurl)
+
+        try:
+            (email, firstname, lastname) = authdatafunc(oa)
+            email = email.lower()
+        except KeyError as e:
+            log.warning("Oauth1 signing using {0} was missing data: {1}".format(provider, e))
+            return HttpResponse('OAuth login was missing critical data. To log in, you need to allow access to email, first name and last name!')
+
+        return _perform_oauth_login(request, provider, email, firstname, lastname)
+    else:
+        log.info("Initiating {0} oauth1 step from {1}".format(provider, get_client_ip(request)))
+
+        oa = OAuth1Session(client_id, client_secret=client_secret)
+        fr = oa.fetch_request_token(requesturl)
+        authorization_url = oa.authorization_url(baseauthurl)
+
+        request.session['login_next'] = request.GET.get('next', '')
+        request.session['ro_key'] = fr.get('oauth_token')
+        request.session['ro_secret'] = fr.get('oauth_token_secret')
+        request.session.modified = True
+        return HttpResponseRedirect(authorization_url)
+
+
 #
 # Google login
 #  Registration: https://console.developers.google.com/apis/