website/integrations: add three more policy-expressions to discord-docs (#5760)

* - Add two policy-examples for syncing discord roles to authentik groups during enrollment or authentication
- Add policy to store oauth-info and create an authentik-avatar-attribute during enrollment or authentication

* Fix issues and lint

- Fixed issue with wrong return during provider-check
- Lint using black

Signed-off-by: Keyinator <k3yinator@gmail.com>

* Fix capitalization and punctuation

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Keyinator <k3yinator@gmail.com>

* Fix documentation link and add explanation to MAPPED_ROLES attribute

---------

Signed-off-by: Keyinator <k3yinator@gmail.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
Keyinator
2024-05-21 20:10:16 +02:00
committed by GitHub
parent 6ffef878f0
commit 09832355e3

View File

@ -154,3 +154,209 @@ return user_matched
``` ```
Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source. Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source.
### Syncing Discord roles to authentik groups
:::info
Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds.members.read` scopes added under the 'Protocol settings'.
:::
Create a new 'Expression Policy' with the content below, adjusting the variables where required.
#### Sync on enrollment
```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.
from authentik.core.models import Group
GUILD_ID = "<YOUR GUILD ID>"
MAPPED_ROLES = {
"<Discord Role Id 1>": Group.objects.get_or_create(name="<Authentik Role Name 1>")[0],
"<Discord Role Id 2>": Group.objects.get_or_create(name="<Authentik Role Name 2>")[0],
# You can add mapped roles by copying the above line and adjusting to your needs
}
# Only change below here if you know what you are doing.
GUILD_API_URL = "https://discord.com/api/users/@me/guilds/{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_info = requests.get(
GUILD_API_URL.format(guild_id=GUILD_ID),
headers={"Authorization": "Bearer " + access_token},
).json()
# Ensure user is a member of the guild
if "code" in guild_member_info:
if guild_member_info["code"] == 10004:
ak_message("User is not a member of the guild")
else:
ak_create_event(
"discord_error", source=context["source"], code=guild_member_info["code"]
)
ak_message("Discord API error, try again later.")
return False
# Add all mapped roles the user has in the guild
groups_to_add = []
for role_id in MAPPED_ROLES:
if role_id in guild_member_info["roles"]:
groups_to_add.append(MAPPED_ROLES[role_id])
request.context["flow_plan"].context["groups"] = groups_to_add
return True
```
Now bind this policy to the chosen enrollment flows for the Discord OAuth source.
#### Sync on authentication
```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.
from authentik.core.models import Group
GUILD_ID = "<YOUR GUILD ID>"
MAPPED_ROLES = {
"<Discord Role Id 1>": Group.objects.get_or_create(name="<Authentik Role Name 1>")[0],
"<Discord Role Id 2>": Group.objects.get_or_create(name="<Authentik Role Name 2>")[0],
# You can add mapped roles by copying the above line and adjusting to your needs
}
# Only change below here if you know what you are doing.
GUILD_API_URL = "https://discord.com/api/users/@me/guilds/{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_info = requests.get(
GUILD_API_URL.format(guild_id=GUILD_ID),
headers={"Authorization": "Bearer " + access_token},
).json()
# Ensure user is a member of the guild
if "code" in guild_member_info:
if guild_member_info["code"] == 10004:
ak_message("User is not a member of the guild")
else:
ak_create_event(
"discord_error", source=context["source"], code=guild_member_info["code"]
)
ak_message("Discord API error, try again later.")
return False
# Get the user's current roles and remove all roles we want to remap
new_groups = [
role for role in request.user.ak_groups.all() if role not in MAPPED_ROLES.values()
]
# Add back mapped roles which the user has in the guild
for role_id in MAPPED_ROLES:
if role_id in guild_member_info["roles"]:
new_groups.append(MAPPED_ROLES[role_id])
# Update user's groups
request.user.ak_groups.set(new_groups)
request.user.save()
return True
```
Now bind this policy to the chosen authentication flows for the Discord OAuth source.
### Store OAuth info in attribute and create avatar attribute from Discord avatar
:::info
Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds.members.read` scopes added under the 'Protocol settings'.
:::
:::info
In order to use the created attribute in authentik you will have to set authentik configuration arguments found at: https://docs.goauthentik.io/docs/core/settings#avatars
:::
Create a new 'Expression Policy' with the content below, adjusting the variables where required:
```python
import base64
import requests
AVATAR_SIZE = "64" # Valid values: 16,32,64,128,256,512,1024
# Only change below here if you know what you are doing.
AVATAR_URL = "https://cdn.discordapp.com/avatars/{id}/{avatar}.png?site={avatar_size}"
AVATAR_STREAM_CONTENT = "data:image/png;base64,{base64_string}" # Converts base64 image into html syntax useable with authentik's avatar attributes feature
def get_as_base64(url):
"""Returns the base64 content of the url"""
return base64.b64encode(requests.get(url).content)
def get_avatar_from_avatar_url(url):
"""Returns an authentik-avatar-attributes-compatible string from an image url"""
cut_url = f"{url}?size=64"
return AVATAR_STREAM_CONTENT.format(
base64_string=(get_as_base64(cut_url).decode("utf-8"))
)
# Ensure flow is only run during OAuth logins via Discord
if context["source"].provider_type != "discord":
return True
user = request.user
userinfo = request.context["oauth_userinfo"]
# Assigns the discord attributes to the user
user.attributes["discord"] = {
"id": userinfo["id"],
"username": userinfo["username"],
"discriminator": userinfo["discriminator"],
"email": userinfo["email"],
"avatar": userinfo["avatar"],
"avatar_url": (
AVATAR_URL.format(
id=userinfo["id"], avatar=userinfo["avatar"], avatar_size=AVATAR_SIZE
)
if userinfo["avatar"]
else None
),
}
# If the user has an avatar, assign it to the user
avatar_url = user.attributes["discord"].get("avatar_url", None)
if avatar_url is not None:
user.attributes["avatar"] = get_avatar_from_avatar_url(avatar_url)
user.save()
return True
```
Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source.