Discourse -> SSO external site provider, problems


(Peter Bäckgren) #1

Been trying to get Discourse to SSO to an external provider but can’t even get the sig to match.

Thought this was a good place to ask but maybe it got buried (would copy it here for reference but the limit on links is too infuriating):

https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045/236?source_topic_id=32977

Might be better to continue in a new thread anyway.

Will publish code for .NET when/if ( :frowning: ) I get this to work.


(Peter Bäckgren) #2

Things started working after the provider had to restart the Discource instance. Uhum.

Unfortunately logging in works far too seldom. Especially when logging in with several accounts you start to get “The website cannot display the page” or Discourse locks up for long periods of time. Maybe a security protection measure.

So is there any way to enable some decent debugging?

Nonce sent=xxx
Sso=xxxx
Sig=xxxx

Similarly for received
sig, sso, nonce, external_data

Maybe I’m decoding things wrong and some certain characters mess up things. But debugging is a nightmare when an account works one instance and not the next. Unfortunately Visual Studio chokes when directed at Discourse as well which I probably should try to figure out first. Almost as helpful as getting zero replies here.


(Peter Bäckgren) #3

Just got some confirmation at least another user can login.

Which starts to mean either I have a encode/decode problem (which would be weird given the supposed straightforwardness) OR Discourse gets “confused” when getting hit with a lot of users from the same browser/site (which might be a security countermeasure).


(Kane York) #4

Sounds like you’re hitting the ratelimiting, then. If you’re using the docker install, you’ll want to remove this line:

  - "templates/web.ratelimited.template.yml"

and rebuild.


(Peter Bäckgren) #5

A reply, yes!

We will try.

Doesn’t quite sound like the problem we are having though, unless there is something looking at originating ip addresses and stuff as well. So still suspicious of my own code :D. Just hellish to test given the conditions.

From my computer (different browsers or virtual machines don’t matter which is weird in itself) I can almost always login with one account, a second account works “for a while”. Some accounts don’t work at all but the only one tested by someone else than me has been verified working from “outside”.

Is there btw some other support options than the $100/month? We would probably have this on our own server and we first need to ask the members if they like this “new” forum approach so going all in isn’t at the top of the priority list (but paying for dedicated support might be).


(Kane York) #6

This is the other support option :wink:

Anyway, I’d double check that you’re doing the right {url,base64}{encode,decode} calls in your code then. Maybe you’re using the wrong base64 encoding?


(Rafael dos Santos Silva) #7

SSO should work always.

Im running a pretty custom SSO (LUA script) and it never fails with 1k+ users per day.

I had a rough time making it work, but when everything is in place is set and forget.

Catch some Discourse SSO requests and emulate then via cURL to test. When you can reliably fake SSO requests everything is better.


(Peter Bäckgren) #8

Doing this a few days a week so not responding so fast myself :smile:

Suspecting encoding/decoding but at least when I feed the return url to myself I see the same nonce I got from discourse initially.

I can see things returning like this but then things just sit there at a blank page in the browser when things go wrong
http://discourseXXXtest.io/session/sso_login?sso=…&sig=…

With so many people asking about this I’m just amazed there is no “sso test” help which you could enable that would give you “nonce sent, return url sent, received nonce, received, username”… yes I know I said the same earlier on in this thread.

I dump urls and respond to them already but maybe downloading cUrl (for Windows, yak :smile:)) might work better. That the request dies after one attempt makes this so much fun. So will consider alternatives although this might not help with that particular problem.

Will get a few more people involved so I have different machines/browsers and more raw material to work with.

Thanks so far. I’m bloody going to get this to work lol.


(Rafael dos Santos Silva) #9

That’s the spirit. :thumbsup:

I’m not a TDD poster child myself, but replying requests with cURL make this way better!


(Peter Bäckgren) #10

Since I don’t have access to Discourse myself maybe a simple test could help if someone is willing to change their sso redirect for a few seconds. I just have a feeling seeing it work once and then trying with one of the accounts that doesn’t work could make someone go “ahaaa”.

sso test service:
eskimo68.no-ip.org/hsp
secret: mLB1DaQZbvxtOAQIE7hWBZ

username (non-existent email, don’t worry)
hspadmin@gmail.com
and with a simple password just this once… x

Doesn’t require email validication and uses a userid of 936645524 which hopefully does not collide :wink:


(Peter Bäckgren) #11

Doh. The guy handling the Discourse part actually came back to me with a log last night. Which shows sso, sig and expected sig so that’s why Discourse sometimes gives me a blank page. Was under the impression there wasn’t anything helpful like this :frowning:

In any case, now I at least have something to work with so I can probably solve the whole thing.

(and will still publish the code once it’s 100%)


(Peter Bäckgren) #12

Since the code has been running for a few weeks now I might as well publish it, put some tags in the code comments just in case someone else actually would ever find this. Not exactly a lot of .net sso solutions published (and I’ll probably piss off some C# users since I used VB but conversion is trivial in any case).

But maybe this gives something back to the community. Good luck (or in this case almost good riddance :open_mouth: ).

Private function SSOAuthenticate(Byval sso As string,byval sig As string,Optional secret As String="daQRsshogwashreplaceme") As Boolean
  'VB.NET Single Sign On SSO signature Discourse
  'Given the sso and sig of an url, authenticate according to given secret (for publication purposes done as a kludge like this :))
  '
  'Works in a scandinavian character set environment (which shouldn't make it any worse, careful with UTF8/ASCII etc changes though)
  'not checked for security leaks (nothing known but haven't done it yet)

  SSOAuthenticate=false

  Dim nonce As String = ""
  Dim returnurl As String = ""
  Dim ltemp As Long
  Dim ltemp2 As Long
  Dim stemp As String
  Dim decodedText As String
  Dim decodedBytes As byte()
  Dim encodedText As String
  Dim bytesToEncode As Byte()

  Dim textencoding As New System.Text.UTF8Encoding
  Dim secretbytes As Byte() = textencoding.GetBytes(secret)
  Dim coderhmacsha256  As New  HMACSHA256(secretbytes)
  Dim messagebytes As Byte() = textencoding.GetBytes(sso)
  Dim hashbytes As Byte() = coderhmacsha256.ComputeHash(messagebytes)

  'although I shouldn't have to use ucase this code has gone through several revisions 
  'where some functions return ucase hex or lcase hex (i.e. I don't want to spend time wondering about it if I ever change this again)
  If UCase(sig)<>UCase(bytetostringX2(hashbytes)) then
    Response.Write("Signature mismatch! Terminating.")
    Response.end
  End If

  decodedBytes = Convert.FromBase64String (sso)
  decodedText = Encoding.UTF8.GetString (decodedBytes)
  'at this point we still have %2F and stuff and I rather have plain text (mismatch with discourse sso how-to documentation but this works)
  decodedText =HttpUtility.UrlDecode(decodedText)

  'assume we get proper content
  ltemp=InStr(decodedText,"nonce=")
  ltemp2=InStr(decodedText,"return_sso_url=")

  'order is undetermined so just in case I use this for another sso solution
  try
    If ltemp<ltemp2 then
      nonce=decodedText.Substring(6,ltemp2-ltemp-7)
      returnurl=decodedText.Substring(ltemp2+14)
    Else
      returnurl=decodedText.Substring(ltemp2+14,ltemp-ltemp2-16)
      nonce=decodedText.Substring(ltemp+5)
    End If
  catch
    'and fail, uh,  gracefully if we didn't get proper content
    Response.Write("Bad content in sso!")
    Response.end
  End Try

  stemp="nonce=" & nonce
  'these should be self explanatory enough (note that Discourse converts away special characters in the alias so fullname might differ from same fullname used in alias) 
  stemp+="&name=" & Session("fullname") & "&username=" & Session("alias") & "&email=" & Session("email") & "&external_id=" & Session("userid")

  bytesToEncode = textencoding.GetBytes(stemp)
  encodedText = Convert.ToBase64String (bytesToEncode)

  Dim encodedUrl As String= HttpUtility.UrlEncode(encodedText)
  Dim outmessagebytes As Byte() = textencoding.GetBytes(encodedText)
  Dim outhashmessagebytes As Byte() = coderhmacsha256.ComputeHash(outmessagebytes)

  sso=encodedUrl
  sig=LCase(bytetostringX2(outhashmessagebytes))

  'debugging purposes (at least my .net environment gets really confused by Discourse redirects)
  'Response.Write("</br>" & returnurl & "?sso=" & sso & "&sig=" & sig)
  'Response.End

  Response.Redirect(returnurl & "?sso=" & sso & "&sig=" & sig )

  SSOAuthenticate=true

End Function

Function bytetostringX2(ByVal inp As Byte()) As String
  Dim stemp As String=""
  Dim ltemp As Long
  
  For ltemp = 0 To inp.Length - 1
    stemp += inp(ltemp).ToString("X2")
  Next
  bytetostringX2=stemp

End Function

Change email for SSO user?