From d8de60b053192801f908dbdf2b58ea6c5b163504 Mon Sep 17 00:00:00 2001 From: Aterfax Date: Sun, 21 May 2023 13:41:59 +0100 Subject: [PATCH] website/integrations: Update discord integration with guild and role check (#5701) * Update Discord OAuth instructions - index.md Adds two sections to this document describing how the required expression policies needed to check users are a member of a certain guild or a member of a certain guild with a certain role. Signed-off-by: Aterfax * Linting and styleguide amendments. * Remove spurious empty lines. * Add an extra line to space comments out. * Moved warning in wrong place. * Apply suggestions from code review Refactor as per BeryJu's suggestions. Co-authored-by: Jens L. Signed-off-by: Aterfax --------- Signed-off-by: Aterfax Co-authored-by: Jens L. --- website/integrations/sources/discord/index.md | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/website/integrations/sources/discord/index.md b/website/integrations/sources/discord/index.md index 4db7236d52..302d22ec5a 100644 --- a/website/integrations/sources/discord/index.md +++ b/website/integrations/sources/discord/index.md @@ -52,3 +52,93 @@ Save, and you now have Discord as a source. :::note For more details on how-to have the new source display on the Login Page see [here](../general#add-sources-to-default-login-page). ::: + +### Checking for membership of a Discord Guild + +:::info +Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds` scope added under the 'Protocol settings'. +::: + +Create a new 'Expression Policy' with the content below, adjusting the variables where required: + +```python +# To get the guild ID number for the parameters, open Discord, go to Settings > Advanced and enable developer mode. +# Right-click on the server/guild title and select "Copy ID" to get the guild ID. + +ACCEPTED_GUILD_ID = "123456789123456789" +GUILD_NAME_STRING = "The desired server/guild name in the error message." + +# Only change below here if you know what you are doing. + +# Ensure flow is only run during OAuth logins via Discord +if context['source'].provider_type != "discord": + return True + +# Get the user-source connection object from the context, and get the access token +connection = context.get("goauthentik.io/sources/connection") +if not connection: + return False +access_token = connection.access_token + +guilds = requests.get( + "https://discord.com/api/users/@me/guilds", + headers= { + "Authorization": f"Bearer {access_token}", + } +).json() + +user_matched = any(ACCEPTED_GUILD_ID == g["id"] for g in guilds) +if not user_matched: + ak_message(f"User is not a member of {GUILD_NAME_STRING}.") +return user_matched +``` + +Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source. + +### Checking for membership of a Discord Guild role + +:::info +Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds guilds.members.read` scopes added under the 'Protocol settings'. +::: + +Create a new 'Expression Policy' with the content below, adjusting the variables where required: + +```python +# To get the role and guild ID numbers for the parameters, open Discord, go to Settings > Advanced and +# enable developer mode. +# Right-click on the server/guild title and select "Copy ID" to get the guild ID. +# Right-click on the server/guild title and select server settings > roles, right click on the role and click +# "Copy ID" to get the role ID. + +ACCEPTED_ROLE_ID = "123456789123456789" +ACCEPTED_GUILD_ID = "123456789123456789" +GUILD_NAME_STRING = "The desired server/guild name in the error message." +ROLE_NAME_STRING = "The desired role name in the error message." + +# Only change below here if you know what you are doing. +GUILD_API_URL = f"https://discord.com/api/users/@me/guilds/{ACCEPTED_GUILD_ID}/member" + +# Ensure flow is only run during OAuth logins via Discord +if context['source'].provider_type != "discord": + return True + +# Get the user-source connection object from the context, and get the access token +connection = context.get("goauthentik.io/sources/connection") +if not connection: + return False +access_token = connection.access_token + +guild_member_object = requests.get( + GUILD_API_URL, + headers= { + "Authorization": f"Bearer {access_token}", + } +).json() + +user_matched = any(ACCEPTED_ROLE_ID == g for g in guild_member_object["roles"]) +if not user_matched: + ak_message(f"User is not a member of the {ROLE_NAME_STRING} role in {GUILD_NAME_STRING}.") +return user_matched +``` + +Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source.