Files
k8scd/authentik/blueprint-scouting.yaml
Ruben Hensen 96c13c63f7 authentik: serve auth.scoutingmbg.nl
A Gateway listener with its own certificate, the authentik route on both
hostnames, and an authentik brand for auth.scoutingmbg.nl that logs in
through Scouts Online and then sends people to the photos. The
authentik.rubenhensen.nl brand stays as it is.
2026-10-24 18:52:24 +02:00

290 lines
11 KiB
YAML

# Login for the Scouting MBG photos (immich on foto.scoutingmbg.nl).
# Mounted as a directory, unlike the other blueprints, so authentik picks up
# changes without a restart; secrets come from files in /secrets/scouting
# for the same reason (see apps/templates/authentik-helm.yaml).
apiVersion: v1
kind: ConfigMap
metadata:
name: authentik-blueprint-scouting
data:
immich-oidc.yaml: |
version: 1
metadata:
name: immich OIDC
entries:
# The source and the login flow below come from scouts-online.yaml.
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
path: custom/scouting/scouts-online.yaml
required: true
# Filled by the Scouts Online source on every login: every member, and
# the members with an active Kaderlid role.
- model: authentik_core.group
id: mbg-leden
state: present
identifiers:
name: MBG leden
- model: authentik_core.group
id: mbg-kader
state: present
identifiers:
name: MBG kader
# immich syncs both claims on every login. A quota of 0 makes immich
# refuse uploads, -1 means unlimited.
- model: authentik_providers_oauth2.scopemapping
id: immich-scope
state: present
identifiers:
name: "immich: role and quota"
attrs:
name: "immich: role and quota"
scope_name: immich
description: Upload rights in the Scouting MBG photos
expression: |
admin = ak_is_group_member(request.user, name="authentik Admins")
uploader = admin or ak_is_group_member(request.user, name="MBG kader")
return {
"immich_role": "admin" if admin else "user",
"immich_quota": -1 if uploader else 0,
}
- model: authentik_providers_oauth2.oauth2provider
id: immich-provider
state: present
identifiers:
name: immich
attrs:
name: immich
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
# Straight on to Scouts Online. Admins with a password log in at
# authentik first.
authentication_flow: !Find [authentik_flows.flow, [slug, scouts-online-login]]
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: confidential
client_id: immich
client_secret: !File /secrets/scouting/immich-oidc-client-secret
redirect_uris:
- matching_mode: strict
url: https://foto.scoutingmbg.nl/auth/login
# "Link to OAuth" in immich's account settings
- matching_mode: strict
url: https://foto.scoutingmbg.nl/user-settings
- matching_mode: strict
url: app.immich:///oauth-callback
signing_key: !Find [authentik_crypto.certificatekeypair, [name, "authentik Self-signed Certificate"]]
property_mappings:
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-openid]]
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-email]]
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-profile]]
- !KeyOf immich-scope
- model: authentik_core.application
id: immich-app
state: present
identifiers:
slug: immich
attrs:
name: Scouting MBG foto's
provider: !KeyOf immich-provider
meta_launch_url: https://foto.scoutingmbg.nl
policy_engine_mode: any
- model: authentik_policies.policybinding
state: present
identifiers:
order: 0
target: !KeyOf immich-app
attrs:
group: !KeyOf mbg-leden
# auth.scoutingmbg.nl: straight to Scouts Online, then on to the
# photos. Other domains keep the default brand.
- model: authentik_brands.brand
state: present
identifiers:
domain: auth.scoutingmbg.nl
attrs:
branding_title: Scouting MBG
flow_authentication: !Find [authentik_flows.flow, [slug, scouts-online-login]]
default_application: !KeyOf immich-app
- model: authentik_policies.policybinding
state: present
identifiers:
order: 1
target: !KeyOf immich-app
attrs:
group: !Find [authentik_core.group, [name, "authentik Admins"]]
scouts-online.yaml: |
version: 1
metadata:
name: Scouts Online
entries:
- model: authentik_sources_oauth.oauthsourcepropertymapping
id: user-mapping
state: present
identifiers:
name: "Scouts Online: user"
attrs:
name: "Scouts Online: user"
expression: |
# info is the userinfo of Scouts Online (https://login.scouting.nl/docs/api).
# It only holds the roles within the organisations of this connection,
# which is just the group.
from datetime import date
today = date.today().isoformat()
roles = {role.get("id"): role for role in (info.get("roles") or {}).values()}
kader = False
for assignment in info.get("role_assignments") or []:
start = (assignment.get("start_date") or "")[:10]
end = (assignment.get("end_date") or "")[:10]
if (start and start > today) or (end and end < today):
continue
role = roles.get(assignment.get("role_id")) or {}
if "Kaderlid" in (role.get("member_type") or []):
kader = True
member_id = info.get("member_id")
email = info.get("email") or ""
# Family members can share an email address; immich needs one per account.
if email and info.get("multiple_accounts"):
local, _, domain = email.partition("@")
email = f"{local}+{member_id}@{domain}"
return {
"username": f"sol-{member_id}",
"email": email,
"name": info.get("name") or "",
"groups": ["MBG leden", "MBG kader"] if kader else ["MBG leden"],
"attributes": {"sol_member_id": member_id},
}
# The source's flows can only be reached through the source.
- model: authentik_policies_expression.expressionpolicy
id: sso-only
state: present
identifiers:
name: scouts-online-sso-only
attrs:
expression: return ak_is_sso_flow
# New members become external users: they can use the apps they are
# let into, not authentik's own interface.
- model: authentik_stages_user_write.userwritestage
id: user-write
state: present
identifiers:
name: scouts-online-user-write
attrs:
user_creation_mode: always_create
user_type: external
- model: authentik_stages_user_login.userloginstage
id: user-login
state: present
identifiers:
name: scouts-online-user-login
- model: authentik_flows.flow
id: enrollment-flow
state: present
identifiers:
slug: scouts-online-enrollment
attrs:
name: Scouts Online enrollment
title: Scouting MBG
designation: enrollment
authentication: none
- model: authentik_flows.flowstagebinding
state: present
identifiers:
order: 0
target: !KeyOf enrollment-flow
stage: !KeyOf user-write
- model: authentik_flows.flowstagebinding
state: present
identifiers:
order: 1
target: !KeyOf enrollment-flow
stage: !KeyOf user-login
- model: authentik_policies.policybinding
state: present
identifiers:
order: 0
target: !KeyOf enrollment-flow
policy: !KeyOf sso-only
- model: authentik_flows.flow
id: authentication-flow
state: present
identifiers:
slug: scouts-online-authentication
attrs:
name: Scouts Online authentication
title: Scouting MBG
designation: authentication
authentication: none
- model: authentik_flows.flowstagebinding
state: present
identifiers:
order: 0
target: !KeyOf authentication-flow
stage: !KeyOf user-login
- model: authentik_policies.policybinding
state: present
identifiers:
order: 0
target: !KeyOf authentication-flow
policy: !KeyOf sso-only
# SOL only lets in members of the organisations picked in its
# connection, and only sends roles within them. The callback URL is
# https://auth.scoutingmbg.nl/source/oauth/callback/scouts-online/ (and
# authentik.rubenhensen.nl, from before auth.scoutingmbg.nl existed)
- model: authentik_sources_oauth.oauthsource
id: source
state: present
identifiers:
slug: scouts-online
attrs:
name: Scouts Online
slug: scouts-online
enabled: true
provider_type: openidconnect
consumer_key: !File /secrets/scouting/scouts-online-client-id
consumer_secret: !File /secrets/scouting/scouts-online-client-secret
authorization_url: https://login.scouting.nl/authorize
access_token_url: https://login.scouting.nl/token
profile_url: https://login.scouting.nl/oidc/userinfo
oidc_well_known_url: https://login.scouting.nl/.well-known/openid-configuration
oidc_jwks_url: https://login.scouting.nl/.well-known/openid-jwks.json
additional_scopes: membership roles
pkce: S256
authorization_code_auth_method: basic_auth
user_matching_mode: identifier
# Links to the groups that immich-oidc.yaml creates
group_matching_mode: name_link
user_path_template: goauthentik.io/sources/scouts-online
enrollment_flow: !KeyOf enrollment-flow
authentication_flow: !KeyOf authentication-flow
user_property_mappings:
- !KeyOf user-mapping
# Where immich sends people who aren't logged in. An identification
# stage without fields and with one source goes straight to it.
- model: authentik_stages_identification.identificationstage
id: identification
state: present
identifiers:
name: scouts-online-identification
attrs:
user_fields: []
sources:
- !KeyOf source
show_source_labels: true
- model: authentik_flows.flow
id: login-flow
state: present
identifiers:
slug: scouts-online-login
attrs:
name: Scouts Online login
title: Scouting MBG
designation: authentication
- model: authentik_flows.flowstagebinding
state: present
identifiers:
order: 0
target: !KeyOf login-flow
stage: !KeyOf identification