Pushing preferences to the Hub

There are a few security/design problems with getting your preferences to the Discourse Hub. You can’t have the server make the request, because pushing should only happen at the request of the user. Using a GET redirect is bad, because you shouldn’t be modifying data on a GET. And due to Cross-Origin rules, it’s hard to get data from one domain to another.

But not impossible, thanks to Cross-Document Messaging (wikipedia).

The Discourse site will create a modal with an <iframe> in it, then execute some code that looks like this:

var user = Discourse.User.current();

// Build data
var preferences = {
  "email": user.get('email'),
  "full_name": user.get('name'),
  "bio": user.get('bio_raw'), // don't pass bio_cooked - shouldn't be trusted
  "website": user.get('website'),
  "email_digest_days": user.get('digest_after_days'),
  "email_quotes_replies_mentions": user.get('email_direct'),
  // ...
  "use_custom_avatar": user.get('use_uploaded_avatar'),
  "custom_avatar_url": absoluteUrl(user.get('avatar_template')),
  "profile_background_url": absoluteUrl(user.get('profile_background')),
  "dynamic_favicon": user.get('dynamic_favicon'),
  "last_pushed_from": Discourse.SiteSettings.title,
  "prefs_protocol_version": 1

// Get element
var hubFrame = document.getElementById('hub-iframe');
// Send message
hubFrame.contentWindow.postMessage(JSON.stringify(preferences), "https://hub.discourse.org");

The magic line is that last one: hubFrame.contentWindow.postMessage(JSON.stringify(preferences), "https://hub.discourse.org");

We get to pass JSON into the iframe, and we’re assured that it’s actually the hub (because of the other-origin check on the second parameter).

I’ve already written a tentative implementation of the reciever:

Here’s what it looks like with one of my example data sets right now, first the whole page when you load it, and second after clicking one of the - buttons:

I kinda skimmed over a few things - it doesn’t treat Gravatar correctly, and I’m not quite sure who’s supposed to host the profile backgrounds.

The code also doesn’t look too great.


I was just thinking about this with regard to custom avatars. If one of the users of my tiny gaming site created a profile and uploaded a custom avatar, it would be published to S3. Then if they pushed their settings up to the Discourse Hub and it just copied the custom avatar URL to Wildly Popular Discourse Site™, could I potentially have a boatload of S3 transfer charges? (Yes, I realize that one avatar or background image is unlikely to break the bank … but it’s the principle of the thing … work with me here ;))

In my opinion each forum should host its own resources.
Both avatars and profile backgrounds can be uploaded via URL instead of file upload, but only when using the master API key.

We’d probably start with a few simple preferences first as a proof of concept before even attempting images.


That’s probably a good idea. I should also set up the forum-side POC so I have example code I can copy from…

I like the mockup, but I’m not sure if the green, yellow and red colors are useful, or maybe I don’t understand what they indicate.

Another option (to avoid the cross origin issue) is to allow the discourse server to do the talking with the hub instead of the browser, like we currently do when checking for available usernames.

A security concern is: are you who you say you are? If a Discourse site pushes a change for niceguy@example.com to set his name to “Stinky Face”, was it really him who made that push? The simplest solution would be for the hub to send an email to Stinky Face niceguy asking him to click a link to verify the change.


Continued here: