DiscourseConnect payload hash encoding mismatch

Hello,

I am new to working with Discourse and AWS, so I was hoping that someone could assist or point me in the right direction. I’m trying to setup DiscourseConnect with my website, following the instructions on this forum https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045/1

In the real world example, when the base64 encoded payload is hashed, I get a different value than the signature.
Payload: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI= (newline omitted)
Signature: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
My HMAC Hash (generated from devglan online tools): 1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471

When I create a signature for the return URL, I get the same result as the website, so I am not quite sure why the answers do not match. If you are able to provide any insight into why this is, it would be greatly appreciated.

Can you share an example payload, the hash which you get, and the secret? (make sure to change the secret on your test site before posting it here)

Newlines are important, and will affect the signature. It looks like HMAC-SHA256 Hash Generator | Devglan strips newlines before calculating the hash. You might have better luck with another tool like Free Online HMAC Generator / Checker Tool (MD5, SHA-256, SHA-512) - FreeFormatter.com

Also, you need to calculate the HMAC of the url-encoded base64 payload. So you should never be calculating the hash of a payload including a raw newline.

Instead, it should be %0A. If you’re using a web framework, bear in mind that it may have un-encoded the payload automatically. You will need to find a way to disable this, or re-encode the value.

3 Likes

I have not tested this on my website yet, I’m trying to work out the steps by hand before I do that.

From the forum post referenced earlier:

Given the following settings:
Discourse domain: http://discuss.example.com
DiscourseConnect url : http://www.example.com/discourse/sso
DiscourseConnect secret: d836444a9e4084d5b224a60c208dce14

User attempt to login

  • Nonce is generated: cb68251eefb5211e58c00ff1395f0c0b
  • Raw payload is generated: nonce=cb68251eefb5211e58c00ff1395f0c0b
  • Payload is Base64 encoded: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n
  • Payload is URL encoded: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A
  • HMAC-SHA256 is generated on the Base64 encoded Payload: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56

Here is where I get a different answer. When I used a the HMAC encoder you suggested on the base64 non-URL encoded payload, I get d26d5adf900de48890a0c3dcdeec108acd91b44a4b76c90c59955a5ba7b957f7 instead of 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56. When I use it on the URL encoded payload, I get 46e749cd26dcabc84eed323ff31f830da674dc87c77a2fcb1b296f76402ea900

However, later in the tutorial, during the creation of the new payload:

Unsigned payload is generated:
nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123&require_activation=true
(order does not matter, values are URL encoded)

Payload is Base64 encoded
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==

Payload is URL encoded
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D

Base64 encoded Payload is signed
3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3

This signature is generated from hashing the Base64 non-URL encoded payload, so that’s why I’m a little bit unsure of why it doesn’t work on the first payload.

Interesting! I am also unable to replicate the signature using that online generator. In ruby, I can do this, which works as expected:

pry(main)> OpenSSL::HMAC.hexdigest("sha256", "d836444a9e4084d5b224a60c208dce14", "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n")
=> "2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"

On a hunch, I tried generating the signature for a string with a carriage return and a newline at the end (i.e. windows style line-endings), and managed to get the same signature as the freeformatter.com tool

pry(main)> OpenSSL::HMAC.hexdigest("sha256", "d836444a9e4084d5b224a60c208dce14", "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\r\n")
=> "200c03f1e5d7b859170be102b436d74f761040261be9682b4afec67eb908fabf"

So I think the best thing is to avoid these online tools, and try a minimal implementation in whatever programming language you’re going to use.

6 Likes

Thanks for your suggestion! I coded parts of the process in JavaScript and when I tested them (with the nodeForge, urlencode, and js-base64 libraries) in RunKit, it returned the same thing as the tutorial.

3 Likes