33 lines
1.6 KiB
Python
33 lines
1.6 KiB
Python
# Authentik expression policy: allow Discord logins only for members of one Discord server (guild).
|
|
# Paste into: Customization -> Policies -> Create -> Expression Policy (name: discord-guild-member)
|
|
# Bind to BOTH flows: default-source-enrollment and default-source-authentication
|
|
# (Flows & Stages -> Flows -> <flow> -> Policy / Group / User Bindings -> Bind existing policy)
|
|
# Requires the "guilds" scope on the Discord source (source -> Protocol settings -> Scopes: guilds).
|
|
#
|
|
# Server ID: Discord -> User Settings -> Advanced -> Developer Mode on; right-click the server -> Copy Server ID.
|
|
|
|
ACCEPTED_GUILD_ID = "1387203044084551690" # <- your Discord server ID
|
|
GUILD_NAME = "Vanguard" # <- shown in the denial message
|
|
|
|
GUILD_API_URL = "https://discord.com/api/users/@me/guilds"
|
|
|
|
# Only act on logins that came through the Discord source; let everything else (akadmin password login) pass.
|
|
source = context.get("source")
|
|
if not source or getattr(source, "provider_type", "") != "discord":
|
|
return True
|
|
|
|
connection = context.get("goauthentik.io/sources/connection")
|
|
if not connection:
|
|
ak_message("No Discord connection in context.")
|
|
return False
|
|
|
|
resp = requests.get(GUILD_API_URL, headers={"Authorization": f"Bearer {connection.access_token}"}, timeout=10)
|
|
if resp.status_code != 200:
|
|
ak_message(f"Could not read Discord server list ({resp.status_code}). Try again.")
|
|
return False
|
|
|
|
is_member = any(g.get("id") == ACCEPTED_GUILD_ID for g in resp.json())
|
|
if not is_member:
|
|
ak_message(f"You must be a member of the {GUILD_NAME} Discord server to log in.")
|
|
return is_member
|