feat(authentik): recovery email links to embedded reset-password UI
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run

Custom email template rendered via AUTH_APP_URL, mounted in Authentik,
and gitignored rendered HTML to avoid localhost hardcoding in prod.
This commit is contained in:
R3D347HR4Y 2026-06-20 01:21:30 +02:00
parent 8bbc539d77
commit f7ef89fa82
7 changed files with 82 additions and 4 deletions

View File

@ -292,7 +292,9 @@ MAIL_MICROSOFT_OAUTH_CLIENT_SECRET=
MAIL_MICROSOFT_OAUTH_TENANT=common
MAIL_OAUTH_REDIRECT_URL=
MAIL_APP_URL=http{{SECURE}}://{{PUBLIC_HOST}}/mail
# Cible nginx → suite frontend unifié mail+drive (dev: Next sur l'hôte :3004 ; prod: suite-frontend:3000)
# Origine app pour pages auth (/login, /reset-password) — template e-mail recovery Authentik.
# Dev local Next.js : AUTH_APP_URL=http://localhost:3004
AUTH_APP_URL=http{{SECURE}}://{{PUBLIC_HOST}}
# -----------------------------------------------------------------------------
# Stalwart hosted mail (optional — enable for @ultisuite.fr / custom domains)

3
.gitignore vendored
View File

@ -32,5 +32,8 @@ Thumbs.db
# Docker data (local volumes)
docker-data/
# Rendered Authentik email templates (generated by deploy/authentik/render-blueprints.sh)
deploy/authentik/templates/**/*.html
# Node.js
node_modules/

View File

@ -8,7 +8,7 @@ Blueprints in `blueprints/` are mounted into Authentik at `/blueprints/custom` a
| `02-ulti-brand.yaml` | Branding UltiSuite + lien « Créer un compte » sur login |
| `03-ulti-suite-groups.yaml` | Claim OIDC `groups` (RBAC contacts/calendar/drive/photos) |
| `04-ulti-post-migration-security.yaml` | Flow WebAuthn/TOTP post-migration (`ulti-post-migration-security`) |
| `05-ulti-recovery.yaml` | Mot de passe oublié (`ulti-recovery`) |
| `05-ulti-recovery.yaml` | Mot de passe oublié (`ulti-recovery`) + e-mail → `/reset-password` |
| `ulti-oidc.yaml` | App OIDC Ultimail |
| `nextcloud-oidc.yaml` | App OIDC Nextcloud |
| `onlyoffice-oidc.yaml` | App OIDC OnlyOffice |
@ -36,6 +36,22 @@ cd ../ulti-backend
docker exec deploy-authentik-server-1 ak apply_blueprint /blueprints/custom/02-ulti-brand.yaml
```
### Mot de passe oublié (recovery embedded)
1. L'utilisateur saisit son e-mail sur `/forgot-password` (UI Ulti).
2. Authentik envoie un e-mail avec lien vers `{AUTH_APP_URL}/reset-password?flow_token=…` (template `email/ulti_password_reset.html`).
3. `/reset-password` reprend le flow `ulti-recovery` via le BFF (`is_restored` → étapes mot de passe embedded).
Variable `.env` : `AUTH_APP_URL` (défaut dev `http://localhost:3004`). Régénérée dans le template e-mail par `./deploy/authentik/render-blueprints.sh`.
Appliquer après changement recovery :
```bash
./deploy/authentik/render-blueprints.sh # depuis ulti-backend avec .env.resolved sourcé
docker exec deploy-authentik-server-1 ak apply_blueprint /blueprints/custom/05-ulti-recovery.yaml
./deploy/compose-up.sh restart authentik-worker
```
## Inscription utilisateur
Flow public : `http://localhost/auth/if/flow/ulti-enrollment/`

View File

@ -31,7 +31,7 @@ entries:
use_global_settings: true
token_expiry: 1800
subject: UltiSuite — Réinitialisation du mot de passe
template: email/password_reset.html
template: email/ulti_password_reset.html
activate_user_on_success: true
recovery_max_attempts: 5
recovery_cache_timeout: 300

View File

@ -1,15 +1,22 @@
#!/usr/bin/env bash
# Render Authentik blueprint templates using PUBLIC_HOST / SECURE / SUITE_ORIGIN from .env.resolved.
# Render Authentik blueprint + email templates using .env.resolved variables.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BP_DIR="$ROOT/deploy/authentik/blueprints"
TPL_DIR="$ROOT/deploy/authentik/templates"
if [[ -z "${SUITE_ORIGIN:-}" || -z "${PUBLIC_HOST:-}" ]]; then
echo "render-blueprints: SUITE_ORIGIN and PUBLIC_HOST must be set (source .env.resolved first)" >&2
exit 1
fi
# Frontend origin for auth pages (/login, /reset-password). Falls back to SUITE_ORIGIN.
AUTH_APP_URL="${AUTH_APP_URL:-${NEXT_PUBLIC_APP_URL:-${SUITE_ORIGIN}}}"
if [[ "$AUTH_APP_URL" == */mail ]]; then
AUTH_APP_URL="${AUTH_APP_URL%/mail}"
fi
render_one() {
local tpl="$1"
local out="${tpl%.template}"
@ -17,6 +24,7 @@ render_one() {
-e "s|{{SUITE_ORIGIN}}|${SUITE_ORIGIN}|g" \
-e "s|{{PUBLIC_HOST}}|${PUBLIC_HOST}|g" \
-e "s|{{SECURE}}|${SECURE:-}|g" \
-e "s|{{AUTH_APP_URL}}|${AUTH_APP_URL}|g" \
"$tpl" > "$out"
echo "render-blueprints: ${out##*/}"
}
@ -25,3 +33,8 @@ shopt -s nullglob
for tpl in "$BP_DIR"/*.yaml.template; do
render_one "$tpl"
done
for tpl in "$TPL_DIR"/**/*.template "$TPL_DIR"/*/*.template; do
[[ -f "$tpl" ]] || continue
render_one "$tpl"
done

View File

@ -0,0 +1,42 @@
{% extends "email/base.html" %}
{% load i18n %}
{% load humanize %}
{% block content %}
<tr>
<td align="center">
<h1>{% trans "Réinitialiser votre mot de passe UltiSuite" %}</h1>
</td>
</tr>
<tr>
<td align="center">
<table border="0">
<tr>
<td align="center" style="max-width: 320px; padding: 20px 0; color: #212124;">
{% blocktrans with username=user.username %}
Bonjour {{ username }}, utilisez le bouton ci-dessous pour choisir un nouveau mot de passe UltiSpace.
{% endblocktrans %}
</td>
</tr>
<tr>
<td align="center" class="btn btn-primary">
<a id="confirm" href="{{AUTH_APP_URL}}/reset-password?flow_token={{ token }}" rel="noopener noreferrer" target="_blank">
{% trans "Réinitialiser le mot de passe" %}
</a>
</td>
</tr>
</table>
</td>
</tr>
{% endblock %}
{% block sub_content %}
<tr>
<td style="padding: 20px; font-size: 12px; color: #212124;" align="center">
{% blocktrans with expires=expires|naturaltime %}
Si vous n'êtes pas à l'origine de cette demande, ignorez cet e-mail. Le lien est valide {{ expires }}.
{% endblocktrans %}
</td>
</tr>
{% endblock %}

View File

@ -110,6 +110,7 @@ services:
env_file: ../.env.resolved
volumes:
- ./authentik/blueprints:/blueprints/custom:ro
- ./authentik/templates:/templates:ro
- ./authentik/branding/ultisuite-logo-light.png:/web/dist/assets/branding/ultisuite-logo-light.png:ro
- ./authentik/branding/ultisuite-logo-dark.png:/web/dist/assets/branding/ultisuite-logo-dark.png:ro
- ./authentik/branding/ultisuite-favicon.png:/web/dist/assets/branding/ultisuite-favicon.png:ro
@ -145,6 +146,7 @@ services:
env_file: ../.env.resolved
volumes:
- ./authentik/blueprints:/blueprints/custom:ro
- ./authentik/templates:/templates:ro
- ./authentik/branding/ultisuite-logo-light.png:/web/dist/assets/branding/ultisuite-logo-light.png:ro
- ./authentik/branding/ultisuite-logo-dark.png:/web/dist/assets/branding/ultisuite-logo-dark.png:ro
- ./authentik/branding/ultisuite-favicon.png:/web/dist/assets/branding/ultisuite-favicon.png:ro