Ошибка входа при попытке использовать Discourse в качестве провайдера идентификации (SSO, DiscourseConnect)

Я пытаюсь следовать этой теме: Use Discourse as an identity provider (SSO, DiscourseConnect), используя код на Python. Приведённый ниже код Python выводит URL, который, насколько я могу судить, должен быть правильно подписан для доступа к https://forum.embeetle.com/session/sso_provider на моём экземпляре Discourse, но вместо этого отображается просто текст «Ошибка входа».

В логах на https://forum.embeetle.com/logs нет ошибок, поэтому я не понимаю, как решить эту проблему. Означает ли это, что подпись неверна? Есть ли другие предложения?

Кстати, не волнуйтесь: я изменю секрет SSO, как только эта проблема будет решена.

import secrets
import base64
import urllib.parse
import hmac
import hashlib

forum_url = 'https://forum.embeetle.com'
target_url = 'https://embeetle.com/#account'
sso_secret = b'JCLSVcqbAnEPXz2p2xBY'

nonce = secrets.token_urlsafe()
payload = f'nonce={nonce}&return_sso_url={target_url}'
payload_base64 = base64.b64encode(payload.encode('utf-8')).decode()
payload_for_url = urllib.parse.quote(payload_base64)
signature = hmac.new(
    sso_secret, payload_for_url.encode('utf-8'), hashlib.sha256
).hexdigest()

print(f'{forum_url}/session/sso_provider?sso={payload_for_url}&sig={signature}')

Этот код генерирует ссылку, похожую на ту, что в команде curl ниже:

johan@morla:~/sa\> curl 'https://forum.embeetle.com/session/sso_provider?sso=bm9uY2U9bExhRUZjRGFVd0NrNTFkMEVOeGc5dUtKZ0kwNHZNYng5VkpFR0RqRUg0OCZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D&sig=70e647a1baad2e09ef6cdc6d87fae70bc27c6823369be20fd1b11ea536180343'
Ошибка входаjohan@morla:~/sa\> 

Вот скриншот настроек на сайте:

Что не так или как я могу отладить это?

Ещё один шаг вперёд: если я включу подробное логирование для DiscourseConnect, в логах появляется ошибка:

image

Подробный лог SSO: Ошибка разбора подписи. Неверная подпись для полезной нагрузки sso: bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA=

Одно примечательное замечание: полезная нагрузка, упомянутая в логе, не закодирована в URL (обратите внимание на ‘=’ в конце) и в ней отсутствует второй ‘=’ закодированных данных (обратите внимание на повторяющийся %3D в конце исходной полезной нагрузки).

Понял: подпись HMAC-SHA256 должна вычисляться на основе данных в кодировке base64, а не URL-кодированных данных. Таким образом, правильный код выглядит так:

import secrets
import base64
import urllib.parse
import hmac
import hashlib

forum_url = 'https://forum.embeetle.com'
target_url = 'https://embeetle.com/#account'
sso_secret = b'JCLSVcqbAnEPXz2p2xBY'

nonce = secrets.token_urlsafe()
payload = f'nonce={nonce}&return_sso_url={target_url}'
payload_base64 = base64.b64encode(payload.encode('utf-8')).decode()
payload_for_url = urllib.parse.quote(payload_base64)

signature = hmac.new(
    sso_secret, payload_base64.encode('utf-8'), hashlib.sha256
).hexdigest()

print(f'{forum_url}/session/sso_provider?sso={payload_for_url}&sig={signature}')

Теперь о остальной части работы: обработка перенаправления, проверка nonce и т. д. Продолжение следует…