Discourse / Discord OAuth integration

Discourse / Discord OAuth integration
0

This open-sponsored project has two goals

  1. Establish an OAuth 2.0 Authenticated connection between a Discourse and Discord instance
  2. Match a User’s Discourse Group Membership to Discord Roles

You can sponsor this project here: https://paypal.me/pools/c/8fzrDylkFE

@merefield will be taking dev lead on this project with assistance from @angus

The projected completion date is 2019-06-21T16:00:00Z

Authentication

There are two ways to handle this piece. Either

  1. build on top of the Discord OAuth 2.0 plugin, i.e. rely on individual users to authenticate and connect their Discourse and Discord accounts; or

  2. create a single persistent OAuth 2.0 connection on the backend which you use to sync the two services.

The virtues of 1 are that it builds on top of the existing plugin and requires association of Discourse and Discord User Ids to function. The vice of this approach is that it relies on the user, i.e. to authenticate using Discord.

The virtues and vices of 2 are essentially the reverse of 1. It doesn’t rely on the user, but would require more work and can’t guarantee a userId match, i.e. you’d need to rely on email matching.

Sync

The initial requirement is to sync Discourse Groups with Discord Roles, however once a connection is established anything supported by the Discord API can be synced.

I think it makes sense to go with approach 2?

The admin of the forum will surely be maintaining a (sub) community on Discord, too? Therefore it makes sense for the admin to manage that relationship? I assume her account will have access via their Discord API credentials linked to their account?

The other question is to make this fully standalone, or require the installation of the Custom Wizard Plugin to carry out the syncing tasks or just handle the authorisation at least? If the latter then it will be an interesting challenge to make that solution general enough to be used in other use cases involving other platforms.

We should probably discuss these choices over video conference …

Found reference material here:

Yes, I agree that approach 2 is the way to go, but we won’t be using the Custom Wizard Plugin at all here. This needs to be an independent customisation. Otherwise we’ll spend two much time trying to generalise everything.

But yes, we can definitely adopt the same approach to OAuth 2.0 authorization we just implemented in the Custom Wizard plugin :+1:

You’ll find that you re-use pieces of work like this frequently. I understand the tendency to want to combine things, however it’s best to keep functionality associated with defined products. There isn’t any overlap between the Custom Wizard and the Discord integration on the product level so we need to keep them seperate on the technical level, even though there is an overlap on the technical level.

2 Likes

That makes it substantially simpler in some ways … cool

1 Like

Let’s have a call about this one tomorrow night my time? As the meat of this, the authorization piece, is something we just did for RStudio, I’d like to get some runs on the board here quickly. In the meantime, I’ll see if we can get any more backing for it on meta.

sounds great, talk to you tomorrow. I’m available from 8:30am my time.

Best of luck guys, I’m suuuuper hyped on this. :+1:

2 Likes

@merefield Can we have a full update on the progress here?

1 Like

Sure!

I have a basic OAuth 2.0 authorization flow working with Discord. However, I’ve backed up a little bit as realised I needed to focus on data requirements to achieve a reliable, “unworkroundable” sync between Discourse and Discord.

So I identified that this plugin is a perfect partner for the work I want to complete:

Unfortunately, it’s not quite feature rich enough to help yet, so …

So I’m currently quite far way through developing a PR of the existing User Authentication Discord Plugin to enhance it to store more Discord information (email, user id). At the same time the aim is to improve that plugin while I’m at it where I can (to utilise the new Reviewable API). Whilst that’s not in scope of this work, the refactor I’m making breaks the existing way the trusted guild funcitonality works, so I’m presently trying to restore that. Everything else is working and in fact the plugin is shrinking by 30% as I’m better leveraging core.

Post about that here:

The justification for this approach is well made by Falco and @Stephen in that Topic.

This PR is a dependency but of course I don’t need the PR to be accepted for people to get going with the end result as they can simply use my repo.

After this I will address the sync which should be a whole bunch easier once we have all users authenticated by Discord and will have all the required keys in Discourse.

I hope to finish the PR today on the user login plugin, then I will turn to the sync.

Progress on the PR is here:

Ok cool.

How are you thinking the combination of the Discord OAuth2 Plugin and this will work from a product perspective? i.e. will you just require all users to Auth with Discord? How will that work?

Yes, individual users authenticating via that plugin (which btw, appears to be official :+1:) will be required and a dependency.

Our’s will sit on top of that and simply depend on the data. We will own all the ‘syncing’ logic in our plugin.

Yup :+1:

My questions is more

How does the user get told they need to authenticate Discourse with Discord to access Discord (i.e. this dependency is not immediately obvious)?

Perhaps this is just something sites can communicate with their userbase in their own way, however say if I already had a Discourse and a Discord setup (like Ackerly Green has), I install this plugin and click ‘sync’, only a small subset of my users will have already auth’d Discourse with Discord, so a fair number of people may lose access to roles on Discord before everyone gets around to authing Discourse with Discord. This is not ideal.

It’d be good to start thinking about how you’ll handle that problem now as it may change how the sync itself works.

Most definitely.

However, to me it definitely doesn’t make sense to rebuild the wheel here.

Also, I believe the only proper way to solve this is by having user accounts pre-authenticated with Discord whilst recording the Discord keys locally using strong authentication approach that can’t be ‘got-around’.

You don’t want, for example, a dependency on a username that can be changed in the front end and allows people to dodge the intent of the plugin.

I’d like to go with this approach for version 1.0.

If it turns out we can add additional fallbacks on top so we don’t have to rely on pre-authentication, then that to me is version 2.0. It would be a pretty easy check to test if user record doesn’t exist in user_associated_accounts and then invoke the fallback.

Cool. Looking forward to testing it. Friday? What’s an eta for this?

I don’t know how hard the next bit will be. I’d be more confident to say Monday.

1 Like

Update:

STAGE 1 complete:

STAGE 2 complete:

Discord Bot :robot: running on Discourse server :rocket:

=> Booting Puma
=> Rails 5.2.3 application starting in development 
=> Run `rails server -h` for more startup options
Starting CSS change watcher
-------------------------------------
Bot spawned, say "Ping!" on Discord!
-------------------------------------
[INFO : websocket @ 2019-06-30 11:00:05.038] Discord using gateway protocol version: 6, requested: 6
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.1-p33), codename: Llamas in Pajamas
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

image

Please explain why we need a bot here? Can’t we just use the REST api?

The entire Discord extension ecosystem is built around bots (as soon as you start to delve into the API docs that becomes very clear)

  • The permissioning framework assumes bots.
  • Developers are encouraged to use the semi~official libraries that already exist to manipulate them. This is why the last attempt to achieve this was using the bot framework.
  • the library nicely abstracts the bot commands and takes care of house keeping inc. respecting rate limits to eliminate risk of a ban.

The difference in this project is that I’m integrating that directly into Discourse which exposes the static data. That lack of integration is why that previous attempt got no further (it was using a JavaScript library running on a separate server - my solution uses the recommended Ruby library and runs as part of the Discourse processes so has access to all the data on the Discourse instance)

Happy to discuss over a quick call if required Can also demo what has been built so far.

1 Like

Cool, can you set up an example using https://staging.thepavilion.io and a new discord test server at https://discord.trypavilion.com (let’s try this domain for now).

I’ve invited you to a new “Pavilion” DigitalOcean team I set up which currently houses this site, staging, a wordpress instance and the domain trypavilion.com (I just transferred it). These test servers have a few purposes:

  1. for our own internal testing; and

  2. for client testing, before they deploy it to their own production environment

1 Like