Getting signed data from the server

Hey all. This question is to the developers, including @codinghorror possibly. I would like our platform to integrate with Discourse seamlessly. In order for this to happen, a user authenticating with Discourse needs to also authenticate with our platform at the same time and with the same id every time.

I see in Javascript that I can call Discourse.User.current() and get a wealth of info including the user id. But, we cannot trust client input. Is there a way to get this information (some of it, at least the user id) signed by the server? This is what Facebook does, for example, with signed_request .

This would be really useful in allowing outside software like ours, which may not have necessarily been written in the same programming language and may not have access to the same server, to still work alongside yours and integrate seamlessly with it. Together with webhooks, it can do a lot.

I’m unclear what this is asking, we already have single-sign on support and API keys. Make the requests over HTTPS of course, as anyone would do in today’s world.

1 Like

Yes, sure you can add it to the SSO plugin. The thing is, HTTPS based requests require HTTP redirects, which can be especially ugly on slower connections like Mobile. There should also be a way to load an iframe in which a special url from the Discourse forum is loaded, and it will postMessage a response back with the requested information, signed either with public key cryptography or a shared secret for that particular origin / domain.

In any case, it’s good to sign the data you export to API clients. In this case the API clients need the user agent to relay them the information, and without signatures, anyone can simply spoof the user-agent and give fake information.

Errr what? No they don’t.

1 Like

I am really not following this, are you saying you want our JSON API to have an extra param of “I am making my request over HTTPS but I do not trust TLS so please sign the JSON payload using your public key”

So instead of https://meta.discourse.org/latest.json you would do https://meta.discourse.org/latest.json?https_is_broken_so_sign_again_for_good_luck=true ?

Note, user API key supports establishment using a signed handshake but that is mainly to stop replay attacks when all payloads are passed via GET.

3 Likes

@sam @codinghorror think there’s some kind of misunderstanding. How am I supposed to get the logged-in user’s id unless I have a cookie or some kind of authenticated session in a user-agent? So, I need the request to be made via the user-agent when the user is logged in, like Facebook does it. The information is returned to the user-agent, not directly to my program on the server. Now, when the user-agent sends this data on to my program (e.g. to tell me which Discourse user is logged in), there is no way to know anymore that the data was really sent by the Discourse program running on its server. THAT is why the data needs to be signed.

This sounds like you could implement the client side of the Discourse SSO protocol in your application and make Discourse the SSO provider?

What you are describing is a very unique type of solution I am still unclear at what the specific problem is. I would like a specific example here.

As it stands you would require a custom plugin to make this happen

2 Likes

We are trying to make our software’s login compatible with Discourse, Wordpress and others. People install our app alongside Discourse, say. They log into Discourse. Then they go visit a page on our app. They shouldn’t have to log in again. They shouldn’t have to have two separate accounts. We should be able to use Discourse to authenticate the user.

Yes, you should, and you can. This is what the Discourse SSO protocol is for. That linked topic is written from the perspective of Discourse authenticating to an external auth provider, but Discourse can also act as the auth provider to a different app (an example of which is the discourse-auth-proxy, using the same protocol.

4 Likes

Look, even in the topic you listed it already says that Discourse SIGNS the payload. And further down it says:

" 1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b TODO update example - this is not correct signature"

So this is what I am talking about! Discourse has to sign the payload! What does it sign it with, though? Either a private/public key pair, in which case we should know the public key … or if you want to be quantum resistant, then a secret shared key pair with each app. Where is this in the API? That’s what I’m talking about.

I will emphasize the main point: SSO can only happen if you have a valid way of authenticating with the provider already. That’s usually a cookie, but can be other ways. Anyway, this is done through the user-agent. The information is NOT returned to our servers directly, but by way of the user-agent. Which may be running malicious code, e.g. XSS. This is why we need to SIGN the payload! So it can be checked on the server.

Now, what I am asking about is the details of this signature scheme. Any API clients should have a way to get a shared secret or public key of the signing server.

The signature scheme is described in the topic @mpalmer linked above – it’s using a shared secret.

There is no way to get the shared secret because it’s… well… secret? The shared secret has to be distributed by the admin setting up SSO.

1 Like

But that’s exactly what I was talking about! The shared secret is generated by the admin setting up SSO. And then you should sign the payloads. Sounds like that’s already being done.

Yes! That’s why we keep pointing you to the SSO topic :wink:

2 Likes

I read this topic. Makes sense, and I guess I’m asking the following: Does Discourse support the other way? Suppose I already have a lot of users on Discourse, and I don’t want all those users to re-register using MY SSO and somehow claim the account. I want to use their existing Discourse login, and seamlessly integrate with it.

In other words, I want to use Discourse web hooks that say when a user registered, and create a corresponding user account for them on our app. And if they visit our app while logged into Discourse (regardless of whether they used Google, Facebook or other method to authenticate with Discourse) then I would like to get a signed payload of the data I found in Discourse.User.current() or some subset that at least includes the id. Sorry if I am not making myself clear … I want to go the other way and add a new app to an existing Discourse community, not the other way around.

Yes, Discourse implements both sides of the protocol.
(But note that webhooks have nothing to do with SSO.)

OK, that’s very good news. So I think we can already use it. (By the way for successful user registration webhook, this we can do directly server-to-server. I need SSO only for auth, not registration.)

So let me ask the remaining questions this way:

  1. What is the endpoint that I should call from my site in order to get Discourse to return signed data? How do I pass the URL to redirect to, the “state” parameter from oAuth etc.?

  2. Does this plugin support oAuth 2 or anything like that, or do I implement a custom handler?

  3. Does it sanitize the redirect, do the latest additions to oAuth 2 like appsecret_proof etc?

Like this (but remember that the roles of your application and Discourse are swapped):

You can’t, Discourse will always send the user back here:

From there, you application can redirect the user wherever you want.

No, it’s a custom protocol.

Since Discourse redirects to a fixed URL to complete authentication, there’s no sanitization going on :slight_smile:

1 Like

To be clear, I want to use Discourse to do the authentication. I still need to understand, in that case:

  1. Which URL do I redirect to, on http://discourse_site/...

  2. How do I specify where to redirect back, as well as a nonce (“state” parameter in oAuth)

  3. “Discourse will always send the user back here” lists http://discourse_site but I am talking about redirecting back to https://somesite.com/success?sso=PAYLOAD&sig=SIG

It seems to me, after reading the above, that the SSO plugin is NOT designed for this. It’s designed the other way - using my site for authentication. For example, as soon as you turn on enable_sso then:

So, fine. It seems that this is only designed to go one way. Then my question becomes: how can I migrate our existing users? I guess I can create accounts with the exact same email addresses in our app, but then how do we automatically link their account and data to these new accounts? Does Discourse automatically match the SSO to existing accounts by email address? If so that will solve everything for our use case.

The wp-discourse plugin can function as an SSO client to Discourse, so you might have a look at it as an example. GitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog.