Source code for scipost.sso

__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"


import base64
import hmac
import hashlib
from urllib import parse

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


[docs]@login_required def discourse(request): """ Callback for the Discourse instance at disc.scipost.org. Make sure to set `DISCOURSE_BASE_URL` and `DISCOURSE_SSO_SECRET` in settings.py Taken from https://gist.github.com/alee/3c6161809ef78966454e434a8ed350d1 Code was originally adapted from https://meta.discourse.org/t/sso-example-for-django/14258 but that discussion topic has been removed. """ payload = request.GET.get("sso") signature = request.GET.get("sig") if None in [payload, signature]: return HttpResponseBadRequest( "No SSO payload or signature. Please contact techsupport if this problem persists." ) # Validate the payload payload = bytes(parse.unquote(payload), encoding="utf-8") decoded = base64.decodebytes(payload).decode("utf-8") if len(payload) == 0 or "nonce" not in decoded: return HttpResponseBadRequest( "Invalid payload. Please contact support if this problem persists." ) key = bytes(settings.DISCOURSE_SSO_SECRET, encoding="utf-8") # must not be unicode h = hmac.new(key, payload, digestmod=hashlib.sha256) this_signature = h.hexdigest() if not hmac.compare_digest(this_signature, signature): return HttpResponseBadRequest( "Invalid payload. Please contact support if this problem persists." ) # Build the return payload qs = parse.parse_qs(decoded) user = request.user params = { "nonce": qs["nonce"][0], "email": user.email, "external_id": user.id, "username": user.username, "require_activation": "true", "name": user.get_full_name(), } return_payload = base64.encodebytes(bytes(parse.urlencode(params), "utf-8")) h = hmac.new(key, return_payload, digestmod=hashlib.sha256) query_string = parse.urlencode({"sso": return_payload, "sig": h.hexdigest()}) # Redirect back to Discourse discourse_sso_url = ( f"{settings.DISCOURSE_BASE_URL}/session/sso_login?{query_string}" ) return HttpResponseRedirect(discourse_sso_url)