WordPress DiscourseConnect client - expired nonce

I ran into the same problem and it does it for logging in with Google. Before WordPress we use Nginx as a reverse proxy. Could this be related?

I’m assuming this is happening with logging into WordPress via your Discourse site. If that’s the case, the issue is that the nonce generated by WordPress is expired. This happens on WordPress sites that have an Object Caching enabled.

One solution is to disable the Object Cache on any pages that have the “Login With Discourse” link. For this approach, make sure the Object Cache is disabled for anonymous users.

Another solution is outlined here: Wordpress SSO Expired nonce - #15 by simon. The function in that post can by copied exactly as it is into your WordPress theme’s functions.php file.

That function adds a randomly string to the Login With Discourse URL. The random string triggers WordPress to break the cache and generate a fresh nonce for the user. @angus, this should probably get added to the plugin’s code: https://github.com/discourse/wp-discourse/blob/main/lib/sso-client/sso-client-base.php#L77. There’s no downside to it, and I don’t think there’s another way of dealing with object caching causing stale nonces to be used instead of generating new ones for each visit.

3 Likes

Thanks a lot for the response. Once we launch Discourse into live operation, we will try to disable the Nging cache on the WordPress site and if that doesn’t work we will edit the functions.php according to the instructions.

Thanks @simon!

I’m a little unclear as to why this would clear Wordpress’ object cache, as typically adding a random string to a url is used to avoid the browser’s cache. Which query is being cached in Wordpress’ object cache? I’m seeing a non-object-cache reason why that approach might work

However if that’s the case we may need a slightly different tweak. But perhaps I’m missing something?

The problem isn’t with the WordPress object cache, as far as I know it’s not persistent across requests. The problem happens with sites that have some kind of persistent caching: https://developer.wordpress.org/reference/classes/wp_object_cache/#persistent-caching. This can be configured through a plugin, but it’s also enabled by default by some hosting providers, for example WP Engine. I think for the case of WP Engine, they don’t enable the object cache on their login page, but they do enable it for anonymous users on all other pages. So on WP Engine the problem is only triggered if the “login with Discourse” link is added to a page other than the login page.

The problem with the discourse_sso_url is that when it’s always set to the same value, for sites that have a persistent cache enabled, it will always return the same nonce. Setting its discourse_sso value to a random string, instead of setting it to its default value of 1 breaks the cache. At least it’s always worked that way when I’ve tested it previously. I don’t have things configured to test it at the moment.

Edit: there are a few more details about the issue here: Discourse (as provider) + WP SSO nonce error - #14 by simon. It’s quite a while since I looked at this. The fix for the issue at the time seemed to be to both add a random string to the discourse_sso_url and to ensure that page caching wasn’t enabled on the page the login link was displayed on (otherwise, the random string won’t be unique for each visit by an anonymous user.)

I see. Thanks for the explanation.

I think we should perhaps take this one step at a time, as I’m wary of pushing a fix here without being sure it’ll be “pluralistic”, i.e. that it will work with different caching approaches.

I’m also still a little unclear as to the role of the various caches in causing this specific issue, in particular the differing roles of persistent object and page caches (if they’re present). I can see why a page cache might cause this issue, but not a persistent object cache (yet). If it’s the latter, we may want to tweak the queries in our Nonce class instead.

I’ll need to test this in a bit more depth next week.

@Petr_Mišák Could you please first try disabling any page caching you have on the page with the login link and let us know how you go with that?

I will try to disable our Nginx Microcache for a test login page on WordPress. Our test page where we login to WordPress through Discourse is here Test Discourse Login | Svět Androida

I have (occasionally) noticed the problem if I use Google login to log in.
Snímek obrazovky 2023-11-14 132812

But now I’m surprised that the Discourse login links that we put on the page using the shortcode have disappeared from our test page.

Anyone have any idea why this is happening

Until I solve the problem with the test login page, I have no “expired nonce” to test it.

Please first make sure there isn’t some kind of caching issue. Let us know how you go with that.

I understand, we will solve it step by step, I agree.

It seems that the error also occurs with the official login. Here is an email to simulate it so you can try it yourself.

  1. go to the page Přihlásit se ‹ Svět Androida — WordPress
  2. log in using “Log in with Discourse” and use your Google account there
  3. after logging in Discourse, you will be redirected back to https://www.svetandroida.cz/ or https://www.svetandroida.cz/wp-login.php with an error message, see screenshot.

It’s poorly tested because the problem doesn’t always show up.
Please try and let me know if you get the error before I disable Nginx Cache. Thanks

1 Like

I checked the status of our Nginx Microcache at Přihlásit se ‹ Svět Androida — WordPress, but it seems that Nginx Microcache is not used there at all. The problem with the “expired nonce” will therefore be related to something else.

1 Like

Hey @Petr_Mišák just checking if you managed to find the culprit for this?

We have searched but so far, unfortunately, without success. But we will continue to search and look for the cause of the problem.

I’m fairly sure the issue with logging into https://www.svetandroida.cz/ through Discourse is related to the nonce being cached. The way I tested this was by clicking the “Log in with Discourse” link (https://www.svetandroida.cz/?discourse_sso=1&redirect_to=https%3A%2F%2Fwww.svetandroida.cz%2F).

The first time I did that I was redirected to https://komunita.svetandroida.cz/, created an account on the Discourse site with Gmail, then redirected back to your WordPress site as a logged in user.

I then logged out of your WordPress site and tried logging back in by clicking the “Login with Discourse” link again. This time I got the “expired nonce” error.

I then generated a login link with a random value for the discourse_sso URL parameter, for example https://www.svetandroida.cz/?discourse_sso=181253058&redirect_to=https%3A%2F%2Fwww.svetandroida.cz%2F and was able to login to your WordPress site through Discourse without any issues.

I’ve tried this a few times, both with the login link that’s generated by the plugin (https://www.svetandroida.cz/?discourse_sso=1&redirect_to=https%3A%2F%2Fwww.svetandroida.cz%2F) and with login links that have a random value set for the discourse_sso parameter. It seems that the nonce that’s being returned is being cached for at least a few minutes.

Without fully debugging the issue, I’m fairly sure you can get things to work just by adding the following to your theme’s functions.php file (it will set a random string to the discourse_sso URL parameter. This should work as long as there isn’t also “page caching” enabled on your login page.)

add_filter('wpdc_sso_client_query', 'wpdc_custom_sso_client_query' );
function wpdc_custom_sso_client_query() {
    return wp_generate_password( 12, false );
}

If you do want to debug the issue, here’s what I’m seeing for a successful request. Note the Cache-Svetzitrka: STALE line. This might indicate that there’s a custom caching layer in place and that the cache was STALE for the successful request (so a fresh nonce was generated.)

Summary
Request URL:
https://www.svetandroida.cz/?discourse_sso=1&redirect_to=https%3A%2F%2Fwww.svetandroida.cz%2F
Request Method:
GET
Status Code:
302 Found
Remote Address:
93.185.102.156:443
Referrer Policy:
strict-origin-when-cross-origin
Cache-Control:
max-age=0
Cache-Svetzitrka:
STALE
Content-Length:
0
Content-Type:
text/html; charset=UTF-8
Date:
Mon, 11 Dec 2023 09:38:05 GMT
Expires:
Mon, 11 Dec 2023 09:21:47 GMT
Location:
https://komunita.svetandroida.cz/session/sso_provider?sso=bm9uY2U9MGU3NTNjYWNhNjMwNmMzNzM5M2MyODk4MjZlYzMxMjQmcmV0dXJuX3Nzb191cmw9aHR0cHMlM0ElMkYlMkZ3d3cuc3ZldGFuZHJvaWRhLmN6JTJG&sig=32ddcc85bd2dd7175f963e791cc9ac734a607355d773422d3abec6173c9f656b
Server:
nginx
Strict-Transport-Security:
max-age=10886400; includeSubdomains; preload
X-Content-Type-Options:
nosniff
X-Frame-Options:
SAMEORIGIN
X-Redirect-By:
WordPress

Here’s what I’m seeing for a failed request. The Cache-Control: no-cache, no-store line seems to indicate that the response shouldn’t be cached, but the from service worker entry in the response indicates that the response could be coming from a service worker’s cache.

Summary
Request URL:
https://www.svetandroida.cz/?discourse_sso=1&redirect_to=https%3A%2F%2Fwww.svetandroida.cz%2F
Request Method:
GET
Status Code:
302 Found (from service worker)
Referrer Policy:
strict-origin-when-cross-origin
Cache-Control:
no-cache, no-store
Content-Security-Policy:
upgrade-insecure-requests; base-uri 'self'; object-src 'none'; script-src https://komunita.svetandroida.cz/logs/ https://komunita.svetandroida.cz/sidekiq/ https://komunita.svetandroida.cz/mini-profiler-resources/ https://komunita.svetandroida.cz/assets/ https://komunita.svetandroida.cz/extra-locales/ https://komunita.svetandroida.cz/highlight-js/ https://komunita.svetandroida.cz/javascripts/ https://komunita.svetandroida.cz/plugins/ https://komunita.svetandroida.cz/theme-javascripts/ https://komunita.svetandroida.cz/svg-sprite/ https://www.google-analytics.com/analytics.js https://www.googletagmanager.com/gtag/js 'sha256-8uAKDaK4QxxCeYZl0Wxad2Nnj2tgKyA14hYBh66pnn0='; worker-src 'self' https://komunita.svetandroida.cz/assets/ https://komunita.svetandroida.cz/javascripts/ https://komunita.svetandroida.cz/plugins/; frame-ancestors 'self'; manifest-src 'self'
Content-Type:
text/html; charset=utf-8
Date:
Mon, 11 Dec 2023 09:38:05 GMT
Discourse-Logged-Out:
1
Location:
https://komunita.svetandroida.cz/login
Referrer-Policy:
strict-origin-when-cross-origin
Server:
nginx
Set-Cookie:
sso_payload=sso%3Dbm9uY2U9MGU3NTNjYWNhNjMwNmMzNzM5M2MyODk4MjZlYzMxMjQmcmV0dXJuX3Nzb191cmw9aHR0cHMlM0ElMkYlMkZ3d3cuc3ZldGFuZHJvaWRhLmN6JTJG%26sig%3D32ddcc85bd2dd7175f963e791cc9ac734a607355d773422d3abec6173c9f656b; path=/; SameSite=Lax
Set-Cookie:
_forum_session=kGW2K6gafsjS90qQMEmxzjggEYo4tZPZe76XZNVro34ilyuuHsaYt2nEzC9h6tfiSBmY9XoDdxh1SV3S8n%2BwqrbsD58UvJBz6khjm%2Fty83ufkgry8daHDdyoTfFwQOjAbXrWeGIwkS4edGY1XetNwXhu%2FNJUghqmq8BEUycBt7098KUO%2BmRYDl5iSL0FNhUzo5Hc7xwRg0tfxuxmb%2FIyVLnbFz6IJuGB3Y95PRcU5DYIwAAny1GQbKQ23kSjgALxAThG7aA%2B7LCI9cJNWV1JRSy%2FTElDN3iugKuVpaQcrSPhV3SvQaiNH3MCfLwu6yxlp%2BZ%2BwTyw22czX8bb197z36WhlbghYtxvKYGRjONJQUagisjPpMrCAcGeTKsGB4JgnUKCtlrwIoFvaDxjec7hMo3aCnibbbkmcxWc6LvD6G2xaxkDgebe7RpvfTYdG8cn8j6rNwX3hM8la4RqZnmma0%2FQlSrfj0BjfY7lnan6TYm28vLwH%2FFfdZoRbo6JdTs5AFjCJvx9UXSjFmoXHH1R1yfAizPeKDFnpiuUs4a%2FBzWafQ%3D%3D--8PEvbWwpqBuJMSRJ--CzzhBea4mmv58a7KLEnukw%3D%3D; path=/; secure; HttpOnly; SameSite=Lax
Set-Cookie:
_t=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax
Strict-Transport-Security:
max-age=31536000
Vary:
Accept
X-Content-Type-Options:
nosniff
X-Discourse-Route:
session/sso_provider
X-Download-Options:
noopen
X-Frame-Options:
SAMEORIGIN
X-Permitted-Cross-Domain-Policies:
none
X-Request-Id:
001750b9-94f2-4bf0-8503-9d673463b91e
X-Runtime:
0.012335
X-Xss-Protection:
0
2 Likes