Discourse Oauth

Hi.

I’m finding the SSO and OAuth stuff overwhelming. I’ve got a passwordless Meteor app (login with email link only) and I would like to login to a Discourse forum with just a click of a link. Having to type a new username is fine, but what’s the fastest way to achieve this? I know Discourse supports SSO and OAuth2, but I just can’t get my head around what it is I need.

The official SSO guide is just out of this world. There’s also some talk about implementing OAuth which seems more comprehensible, but would still like some advice before jumping in to this - could take days.

Has anyone implemented a comfy login system to an external application?

Have you seen the Discourse SSO npm package? The instructions for using it a fairly straightforward, and might help. There is an Atmosphere wrapper package as well - maazalik:discourse-sso.

yeah seen that. SSO still needs to be set up on Discourse and that’s the “out of this world” link. Soo… hoping to dodge hell here :slight_smile:

edit: hmm, maybe i’m too pessimistic. Let me dig into this once more…

ah so I figured it out. Pretty much perfect. I’ll paste my code here so you wouldn’t need to reinvent the wheel.

First fill the settings in your discourse forum. It’s the very first part in the official guide.

Also make sure you have the npm package
meteor add meteorhacks:npm

packages.js

{
  "discourse-sso": "1.0.3"
}

client/router.js

FlowRouter.route('/discourse_sso', {
	name: "discourse_sso",
	action: function(params) {
		var sig = params.query.sig
		var sso = params.query.sso
		var userId = Meteor.userId()
		Meteor.call("discourseSSO", userId, sso, sig, function(err, result) {
			console.log("ROUTER DONE", err, result);
			if (err || !result)
				ReactLayout.render(DiscourseSSO, {error: err}); // renders error if needed
			else {
				window.location.replace("http://spice.toitla.com/session/sso_login?"+result)
			}
		})
	}
});

server/discourseSSO.js

Meteor.methods({
    discourseSSO:function(userId, payload, sig){
        discourse_sso = Meteor.npmRequire('discourse-sso');

        if (!userId)
            throw new Meteor.Error(401, "Not logged in. Please log into Toitla before going to the forum.")
        if (!payload || !sig)
            throw new Meteor.Error(401, "Forum sent data incorrect: "+payload+" - "+sig)

        check(userId, String);
        check(payload, String);
        check(sig, String);

        var user = Meteor.users.findOne(userId)

        if (!user.profile || !user.profile.name)
            throw new Meteor.Error(401, "Please fill your profile before going to the forum.")

        var sso = new discourse_sso(process.env.DISCOURSE_SSO_SECRET);

        if (!sso.validate(payload, sig))
            throw new Meteor.Error(401, "Payload and Sig do not match")

        var userparams = {
            // Required, will throw exception otherwise
            "nonce": sso.getNonce(payload),
            "external_id": user._id,
            "email": user.getEmail(),
            "username": user.profile.name.replace(/[^a-z0-9]+/gi, "_"),
            "name": user.profile.name
        }

        return sso.buildLoginString(userparams);
    }
});

Now when the user goes to the forum, she is automatically redirected to my meteor app where the login stuff happens and redirected back as a logged in user.

7 Likes

Could you please help me in understanding the changes i need to do with react router , instead of flow router .
-Mahesh

haven’t used reactRouter so no idea. What you need to do is understand what the code is actually doing and you’ll handle everything in no time.

Thank you. It will be great if you could explain the client part of the code .
Following is my understanding by reading your code snippet .Please correct me if I am wrong . I havent tried the code.

  1. User clicks on forum to submit some thing
    2)Forum checks the login state and and uses SSO to make the user login .
  2. if the user is already logged in , nothing is visible to user .
  3. If the user has not logged in , there will be a prompt to log in .
  4. All the user state validations happens through flowrouter ( as the entry point )
  5. Flow router executes action () when request is received to mysite.com/discourse_sso?params
  6. It provides the login window if the user has not logged in . Else action is silent ?

regards
Mahesh

Beautiful. :cry:

Just so beautiful. :sob:

1 Like

Add this to your Flowrouter to show your log in forms instead of just an error message:

triggersEnter: [AccountsTemplates.ensureSignedIn]

Thanks for sharing your code @KristerV, saved me a lot of time!

I know this is old, but it’s bad form and a big security hole to call Meteor.userId() on the client then pass it to the server. You should never do that. It can easily be called securely on the server meteor method.