Skip to content

Commit

Permalink
feat: add Discourse SSO names and group syncing
Browse files Browse the repository at this point in the history
Resolves #345 and #352
  • Loading branch information
MatthewL246 committed Dec 9, 2024
1 parent a82e731 commit 3a0f021
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 28 deletions.
30 changes: 2 additions & 28 deletions src/routes/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,20 +454,7 @@ router.get('/sso/discourse', async (request, response, next) => {
try {
const accountData = await util.getUserAccountData(request, response);

// * Discourse REQUIRES unique emails, however we do not due to NN also
// * not requiring unique email addresses. Email addresses, for now,
// * are faked using the users PID. This will essentially disable email
// * for the forum, but it's a bullet we have to bite for right now.
// TODO - We can run our own SMTP server which maps fake emails ([email protected]) to users real emails
const payload = Buffer.from(new URLSearchParams({
nonce: decodedPayload.get('nonce'),
external_id: accountData.pid,
email: `${accountData.pid}@invalid.com`, // * Hack to get unique emails
username: accountData.username,
name: accountData.username,
avatar_url: accountData.mii.image_url,
avatar_force_update: true
}).toString()).toString('base64');
const payload = util.createDiscoursePayload(decodedPayload.get('nonce'), accountData);

const query = new URLSearchParams({
sso: payload,
Expand Down Expand Up @@ -538,20 +525,7 @@ router.post('/sso/discourse', async (request, response, next) => {

const accountData = await util.getUserAccountData(request, response);

// * Discourse REQUIRES unique emails, however we do not due to NN also
// * not requiring unique email addresses. Email addresses, for now,
// * are faked using the users PID. This will essentially disable email
// * for the forum, but it's a bullet we have to bite for right now.
// TODO - We can run our own SMTP server which maps fake emails ([email protected]) to users real emails
const payload = Buffer.from(new URLSearchParams({
nonce: decodedPayload.get('nonce'),
external_id: accountData.pid,
email: `${accountData.pid}@invalid.com`, // * Hack to get unique emails
username: accountData.username,
name: accountData.username,
avatar_url: accountData.mii.image_url,
avatar_force_update: true
}).toString()).toString('base64');
const payload = util.createDiscoursePayload(decodedPayload.get('nonce'), accountData);

const query = new URLSearchParams({
sso: payload,
Expand Down
46 changes: 46 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,51 @@ async function removeDiscordMemberTesterRole(memberId) {
}
}

function createDiscoursePayload(nonce, accountData) {
const groups = config.discourse.groups;

const accessLevelGroupMapping = {
0: {
addGroups: '',
removeGroups: `${groups.tester},${groups.moderator},${groups.developer}`
},
1: {
addGroups: groups.tester,
removeGroups: `${groups.moderator},${groups.developer}`
},
2: {
addGroups: groups.moderator,
removeGroups: `${groups.tester},${groups.developer}`
},
3: {
addGroups: groups.developer,
removeGroups: `${groups.tester},${groups.moderator}`
}
};

const { addGroups, removeGroups } = accessLevelGroupMapping[accountData.access_level] || accessLevelGroupMapping[0];

// * Discourse SSO Payload
// * https://meta.discourse.org/t/official-single-sign-on-for-discourse-sso/13045

// * Discourse REQUIRES unique emails, however we do not due to NN also
// * not requiring unique email addresses. Email addresses, for now,
// * are faked using the users PID. This will essentially disable email
// * for the forum, but it's a bullet we have to bite for right now.
// TODO - We can run our own SMTP server which maps fake emails ([email protected]) to users real emails
return Buffer.from(new URLSearchParams({
nonce: nonce,
external_id: accountData.pid,
email: `${accountData.pid}@invalid.com`, // * Hack to get unique emails
username: accountData.username,
name: accountData.mii.name,
avatar_url: accountData.mii.image_url,
avatar_force_update: true,
add_groups: addGroups,
remove_groups: removeGroups
}).toString()).toString('base64');
}

function signDiscoursePayload(payload) {
return crypto.createHmac('sha256', config.discourse.sso.secret).update(payload).digest('hex');
}
Expand All @@ -281,5 +326,6 @@ module.exports = {
assignDiscordMemberTesterRole,
removeDiscordMemberSupporterRole,
removeDiscordMemberTesterRole,
createDiscoursePayload,
signDiscoursePayload
};

0 comments on commit 3a0f021

Please sign in to comment.