Skip to content

Commit

Permalink
Update Discourse SSO to use custom PrivateMixin
Browse files Browse the repository at this point in the history
This is required to retain correct redirect behavior
when login is required.
  • Loading branch information
madprime committed Feb 4, 2020
1 parent 9ae871e commit 326ca2d
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 73 deletions.
6 changes: 3 additions & 3 deletions discourse/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.conf.urls import url
from django.urls import re_path

from .views import single_sign_on
from .views import SingleSignOn

urlpatterns = [url(r"^sso/$", single_sign_on)]
urlpatterns = [re_path(r"^sso/$", SingleSignOn.as_view())]
144 changes: 74 additions & 70 deletions discourse/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,78 @@

from urllib.parse import parse_qs, unquote, urlencode

from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.conf import settings


@login_required
def single_sign_on(request):
"""
Support Discourse single sign-on.
"""
payload = request.GET.get("sso")
signature = request.GET.get("sig")

if None in [payload, signature]:
return HttpResponseBadRequest(
"No SSO payload or signature. Please "
"contact support if this problem persists."
)

# Validate the payload
try:
payload = unquote(payload).encode("utf-8")
decoded = base64.decodebytes(payload).decode("utf-8")

assert "nonce" in decoded
assert len(payload) > 0

except AssertionError:
return HttpResponseBadRequest(
"Invalid payload. Please contact support if this problem persists."
)

key = settings.DISCOURSE_SSO_SECRET.encode("utf-8")
h = hmac.new(key, payload, digestmod=hashlib.sha256)
this_signature = h.hexdigest()

if this_signature != signature:
return HttpResponseBadRequest(
"Invalid payload. Please contact support if this problem persists."
)

# Build the return payload
qs = parse_qs(decoded)

if not request.user.member.primary_email.verified:
return HttpResponseBadRequest("Please verify your Open Humans email address.")

params = {
"nonce": qs["nonce"][0],
"name": request.user.member.name,
"email": request.user.member.primary_email.email,
"external_id": request.user.id,
"username": request.user.username,
}

try:
params["avatar_url"] = request.user.member.profile_image.url
params["avatar_force_update"] = "true"
except ValueError:
pass

return_payload = urlencode(params).encode("utf-8")
b64_return_payload = base64.b64encode(return_payload)
h = hmac.new(key, b64_return_payload, digestmod=hashlib.sha256)

query_string = urlencode({"sso": b64_return_payload, "sig": h.hexdigest()})

# Redirect back to Discourse
url = "%s/session/sso_login" % settings.DISCOURSE_BASE_URL

return HttpResponseRedirect("%s?%s" % (url, query_string))
from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.views.generic.base import View

from common.mixins import PrivateMixin


class SingleSignOn(PrivateMixin, View):
def get(self, request):
"""
Support Discourse single sign-on.
"""
payload = request.GET.get("sso")
signature = request.GET.get("sig")

if None in [payload, signature]:
return HttpResponseBadRequest(
"No SSO payload or signature. Please "
"contact support if this problem persists."
)

# Validate the payload
try:
payload = unquote(payload).encode("utf-8")
decoded = base64.decodebytes(payload).decode("utf-8")

assert "nonce" in decoded
assert len(payload) > 0

except AssertionError:
return HttpResponseBadRequest(
"Invalid payload. Please contact support if this problem persists."
)

key = settings.DISCOURSE_SSO_SECRET.encode("utf-8")
h = hmac.new(key, payload, digestmod=hashlib.sha256)
this_signature = h.hexdigest()

if this_signature != signature:
return HttpResponseBadRequest(
"Invalid payload. Please contact support if this problem persists."
)

# Build the return payload
qs = parse_qs(decoded)

if not request.user.member.primary_email.verified:
return HttpResponseBadRequest(
"Please verify your Open Humans email address."
)

params = {
"nonce": qs["nonce"][0],
"name": request.user.member.name,
"email": request.user.member.primary_email.email,
"external_id": request.user.id,
"username": request.user.username,
}

try:
params["avatar_url"] = request.user.member.profile_image.url
params["avatar_force_update"] = "true"
except ValueError:
pass

return_payload = urlencode(params).encode("utf-8")
b64_return_payload = base64.b64encode(return_payload)
h = hmac.new(key, b64_return_payload, digestmod=hashlib.sha256)

query_string = urlencode({"sso": b64_return_payload, "sig": h.hexdigest()})

# Redirect back to Discourse
url = "%s/session/sso_login" % settings.DISCOURSE_BASE_URL

return HttpResponseRedirect("%s?%s" % (url, query_string))

0 comments on commit 326ca2d

Please sign in to comment.