Implementing SSO, nonce immediately expires

AI-generated summary

A user, dim_taburetkin, encountered an ‘Account login timed out’ error while implementing SSO in Discourse. The issue stemmed from incorrectly encoding the SSO payload, specifically replacing ‘=’ with its URL-encoded equivalent during the encoding process, which caused the nonce validation to fail. After correcting the payload creation method—encoding individual values rather than the entire string—the problem was resolved. Another user, tdekoekkoek, is seeking clarification on how to properly construct the payload in JavaScript.

Hi, i am only trying to implement SSO.

Every time i redirect browser to “/session/sso_login?<…>” i see this error:
Account login timed out.

here is my workflow:

  1. user open discourse forum at http://forum.mysite.com and get auto redirect to sso end point http://mysite.com/sso?sso=...&sig=…
  2. http://mysite.com/sso backend takes sso param from querystring, validate it against sig param and if validation success unpack it urldecode(frombase64(urldecode(sso))) and store nonce value.
  3. http://mysite.com/sso backend create test payload with test user {nonce=storednonce,external_id=mytestid, email=myvalidemail}
  4. convert payload to sso param: sso = urlencode(base64(urlencode(payload)))
  5. create sig param as sig = hmacsha256(sso)
  6. send browser to http://forum.mysite.com/session/sso_login?sso=...&sig=…

but i always get error: Account login time out

i’ve searched topic about sso and find this
https://meta.discourse.org/t/how-to-avoid-account-login-timed-out-please-try-logging-in-again-when-the-payload-had-expired-in-sso/35682/9?u=dim_taburetkin

it seems that i can see this error even if my nonce is not correct at all.
so i’ve checked that i send back to discourse nonce i exactly receive.
same result.

there is no change if i do it immediately after receiving nonce or after some time in range of ten minutes
same result

what i am missing?
does discourse check if http://mysite.com/sso realy exist in the web?

Solved.

My mistake was in preparing payload for redirect back to discourse
so nonce did not pass validation at the /session/sso_login
but the reason phrase is “nonce expired”.

my wrong method:

payload = "{nonce=aaaabbb&external_id=blablabla...}";
encoded = urlencode(payload); // <- mistake because of '=' replaced with encoded equivalent,
base64 = toBase64(encoded);
sig = hmac(base64);

correct method:

payload = "{nonce=aaabbb,external_id=[urlencoded value],...}; //encoded values instead of encoded whole string
base64 = toBase64(payload);
sig = hmac(base64)
sso = urlencode(base64)

and finaly sso and sig goes to querystring

I’m still having this issue. Can you show me how you create your payload? I am trying to create it in Javascript. thanks