Discord
Allows users to authenticate using their Discord credentials
Preparation
The following placeholders will be used:
authentik.company
is the FQDN of the authentik install.
Discord
- Create an application in the Discord Developer Portal (This is Free) https://discord.com/developers/applications
- Name the Application
Select OAuth2 from the left Menu
Copy the Client ID and save it for later
Click to Reveal the Client Secret and save it for later
Click Add Redirect and add https://authentik.company/source/oauth/callback/discord/
Here is an example of a completed OAuth2 screen for Discord.
authentik
Under Directory -> Federation & Social login Click Create Discord OAuth Source
Name: Choose a name (For the example I used Discord)
Slug: discord (You can choose a different slug, if you do you will need to update the Discord redirect URLand point it to the correct slug.)
Consumer Key: Client ID from step 4
Consumer Secret: Client Secret from step 5
Here is an example of a complete authentik Discord OAuth Source
Save, and you now have Discord as a source.
For more details on how-to have the new source display on the Login Page see here.
Checking for membership of a Discord Guild
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:
# 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
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:
# 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()
# The response for JSON errors is held within guild_member_object['code']
# See: https://discord.com/developers/docs/topics/opcodes-and-status-codes#json
# If the user isn't in the queried guild, it gives the somewhat misleading code = 10004.
if "code" in guild_member_object:
if guild_member_object['code'] == 10004:
ak_message(f"User is not a member of {GUILD_NAME_STRING}.")
else:
ak_create_event("discord_error", source=context['source'], code=guild_member_object['code'])
ak_message("Discord API error, try again later.")
# Policy does not match if there is any error.
return False
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.