- Add a configure_webauthn helper to sync.sh that sets the
WebAuthn policy (both 2FA and passwordless variants) on a
realm and enables the webauthn-register and
webauthn-register-passwordless required actions so users can
self-enroll passkeys via the account console.
- Apply it to both master (RP "E-Cosplay Auth") and ecosplay
(RP "E-Cosplay") on every sync run, idempotent.
- Mirror the same policy fields and required actions in the
ecosplay realm import JSON for fresh installs. Sensible
defaults: ES256/RS256/EdDSA, user verification preferred,
no attestation, resident key not specified.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gitea 1.25.5 and earlier do not send PKCE code_challenge_method
on OIDC sources, so enforcing PKCE in Keycloak causes:
Missing parameter: code_challenge_method
at the /auth endpoint. Drop the pkce.code.challenge.method
attribute from the ecosplay_code client block in the realm
import JSON, and add a set_client_pkce helper to sync.sh that
clears the attribute on existing installs. All other clients
(ecosplay_web, eticket) keep S256.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Gitea OAuth2 provider name changed from esy_lock to
ecosplay_code, so the callback path follows:
/user/oauth2/ecosplay_code/callback
Update both the realm import JSON and sync.sh reconciliation
for code.e-cosplay.fr and cos.local.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the tls { dns cloudflare ... } directive and fall back to
Caddy's default automatic HTTPS (HTTP-01 / TLS-ALPN). The
Cloudflare DNS plugin was causing issues during cert provisioning;
standard ACME works fine as long as port 80/443 reach the server.
Also drop the now-unused cloudflare_token variable from group_vars.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
scripts/backup.sh runs pg_dump inside the ecosplay-auth-db
container, writes a gzipped, timestamped dump to /backup/
(overridable via BACKUP_DIR), and keeps the latest N dumps
(RETENTION=14 by default).
Only the Postgres volume carries state that isn't reproducible
from code (users, credentials, TOTP secrets, sessions, brute-
force counters), so the rest of the repo is not bundled here.
Intended as a base that can later feed a cron job or be piped
to off-site storage (S3, SFTP, borg).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename the bootstrap human admin from jovann@siteconseil.fr to
jovann@e-cosplay.fr in docker-compose env vars and in the realm
import JSON. Keycloak identifies users by username so a new user
is created on the next sync run; the old jovann@siteconseil.fr
is left in place and can be deleted manually from the admin UI.
- Introduce a service account client `sync-bot` in the master
realm (confidential, service accounts enabled, direct grants off)
granted the `admin` realm role. sync.sh now authenticates via
client_credentials, falling back to the bootstrap admin only on
the very first run — so reconciliation keeps working after the
default admin is disabled.
- Add disable_default_admin() at the end of the sync script. It
first verifies that sync-bot can authenticate, then flips the
`admin` user's `enabled` flag to false. Idempotent and safe:
refuses to run if sync-bot auth is broken, and is a no-op if
admin is already disabled.
- SYNC_BOT_CLIENT / SYNC_BOT_SECRET env vars added to the init
container for both bootstrap authentication and service client
secret reconciliation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pages we don't override with a custom .ftl (change password, OTP,
verify email, required actions, etc.) render their inner form
with Keycloak's stock PatternFly/Bootstrap classes. The brutalist
card shell was styled but the fields inside were not.
Add resources/css/brutalist.css with targeted overrides on
.pf-c-form-control, .pf-c-button, .pf-c-input-group, .checkbox,
.form-group, alerts and headings, then link it from template.ftl
so every Keycloak auto-generated page inherits the E-Cosplay look
without touching each individual .ftl file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Caddy failed to start because the caddy user could not open
/var/log/caddy/auth.e-cosplay.fr.log. Rather than manage a
dedicated log directory + permissions, remove the custom `log`
block from the vhost so Caddy logs to stderr, which systemd
captures via journald (read with `journalctl -u caddy -f`).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The target server uses /etc/caddy/sites/ (plural) for per-site
config files, not /etc/caddy/site/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Self-contained playbook intended to be run locally on the target
server, where this repo is already cloned (typically at
/var/www/e-auth). No SSH / inventory needed — hosts: localhost
with connection: local.
What it does:
- Installs Docker Engine + compose plugin from the official repo
(idempotent, no-op if already present).
- Ensures /etc/caddy/site exists and templates the vhost file at
/etc/caddy/site/e-auth.conf with the Cloudflare DNS-01 token for
caddy-dns/cloudflare, reverse-proxying to 127.0.0.1:9450.
- Validates the Caddy config and reloads the service on change.
- Runs `docker compose pull` and `docker compose up -d` from the
repo root.
Assumes Caddy is already installed with the caddy-dns/cloudflare
plugin and loads per-site files from /etc/caddy/site/*.conf.
Usage (on the server):
cd /var/www/e-auth/ansible && ansible-playbook deploy.yml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bundle logo.jpg in the repo root as the source asset and copy it
into themes/ecosplay/login/resources/img and
themes/ecosplay/account/resources/img so Keycloak serves it under
${url.resourcesPath}/img/logo.jpg.
- Login: render the logo above the auth card in a brutalist white
frame (black border, offset hard shadow), 160x160.
- Account console: inject a 64x64 brand mark in the masthead via
a ::before pseudo-element on .pf-v5-c-masthead__brand using the
theme's resources/img/logo.jpg as background.
- Email: inline the logo as a base64 data URI (resized to 400x400
JPEG @ q82 ~14KB) directly in html/template.ftl, so external image
blocking in mail clients does not hide it. Rendered as a 160x160
framed brand mark above the message body.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename OIDC client ecosplay-web -> ecosplay_web in the realm import
JSON. The client is used by the internal e-cosplay site for OAuth.
- Replace wildcard redirect URIs with the two exact callbacks:
https://www.e-cosplay.fr/oauth/keycloak and
https://cos.local/oauth/keycloak. webOrigins and post-logout URIs
follow the same hosts.
- Add helpers to sync.sh (client_internal_id, rename_client,
set_client_uris) and a reconciliation step that renames any legacy
ecosplay-web -> ecosplay_web and idempotently re-applies the URIs
on every run, so live installs are migrated automatically.
- Set the bootstrap admin user's real first/last name (jovann Serreau)
in both the env vars and the realm import JSON.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move the inline keycloak-init bash block out of docker-compose.yml
into init/sync.sh, mounted into the init container at /opt/init.
The script is fully idempotent and is the new entry point for any
future role/group/user/realm configuration changes — re-run with
`docker compose up -d keycloak-init --force-recreate`.
- Add reusable helper functions (ensure_user, ensure_group,
ensure_user_in_group, ensure_user_realm_role, ensure_user_client_role)
on top of kcadm.sh, with safe parsing of user/group IDs.
- Bootstrap admin identity jovann@siteconseil.fr (password Shoko1997@)
in both realms:
* master realm: granted the global `admin` role.
* ecosplay realm: granted realm-management/realm-admin and added
to groups super_admin_asso and superadmin.
Both users have CONFIGURE_TOTP as a required action so OTP enrollment
is forced at first login.
- Mirror the ecosplay user in the realm import JSON for fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Declare the 10 application groups (gp_asso, gp_contest, gp_mail,
gp_mailling, gp_member, gp_ndd, gp_sign, gp_ticket, super_admin_asso,
superadmin) in the realm import JSON for fresh installs.
- Extend keycloak-init to idempotently create them via kcadm on every
boot, so existing installs (where the realm is already imported and
--import-realm is a no-op) also get them in sync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go-live:
- Switch keycloak from start-dev to start --import-realm (production
mode with auto-build at boot, no Dockerfile needed yet).
- Set KC_HOSTNAME=https://auth.e-cosplay.fr and KC_PROXY_HEADERS=
xforwarded so Keycloak emits correct issuer URLs and trusts
Caddy's X-Forwarded-* headers.
- Replace deprecated KEYCLOAK_ADMIN env vars with KC_BOOTSTRAP_ADMIN_*.
- Bind the public port to 127.0.0.1 only (Caddy is colocated).
- Add a Keycloak healthcheck against /health/ready on the management
port (9000) using bash /dev/tcp; init container now waits on
service_healthy instead of service_started.
Architecture:
- New realms/ecosplay-realm.json mounted into /opt/keycloak/data/import
and imported on first boot. Defines the dedicated 'ecosplay' realm
(separate from master) with French i18n, brute-force protection,
strong password policy, SES SMTP, and an OIDC client 'ecosplay-web'
pointing at e-cosplay.fr (confidential + PKCE S256).
Theme coverage:
- themes/ecosplay/account: PatternFly v5 overlay (parent=keycloak.v2)
bringing the neo-brutalist colors, thick borders, italic uppercase
typography, and offset hard shadows to the user account console.
- themes/ecosplay/email: branded HTML wrapper template (table layout
with inline styles for email-client safety) plus a matching plain
text wrapper. All Keycloak emails now ship with the E-Cosplay
identity without needing per-template overrides.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- keycloak-init now enables i18n on master with French as the only
supported locale and the default, so all login pages render in fr.
- Replace dynamic realm.displayName tag (which showed 'Keycloak') with
hardcoded '// Connexion sécurisée' in the theme header.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Guard locale references with null-safe defaults — the master realm
ships with internationalization off, so locale is undefined and
${locale.currentLanguageTag} threw InvalidReferenceException.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Custom theme under themes/ecosplay/login (extends keycloak parent)
with template.ftl and login.ftl matching the e-cosplay.fr style:
thick black borders, hard offset shadows, italic uppercase, indigo
accent, hover translate effect, marquee header, watermark.
- Tailwind via Play CDN for utility classes (no build step).
- Mount the theme dir read-only into the Keycloak container.
- Init container now also sets loginTheme=ecosplay on master realm
alongside the SMTP config; service renamed keycloak-init.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add keycloak-smtp-init service that uses kcadm.sh to apply SES SMTP
settings to the master realm at startup (idempotent, env-driven).
- Set KC_HOSTNAME to https://auth.e-cosplay.fr and trust X-Forwarded-*
headers for the upcoming Caddy reverse proxy in front.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>