feat(authentik): recovery email links to embedded reset-password UI
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:
parent
8bbc539d77
commit
f7ef89fa82
@ -292,7 +292,9 @@ MAIL_MICROSOFT_OAUTH_CLIENT_SECRET=
|
|||||||
MAIL_MICROSOFT_OAUTH_TENANT=common
|
MAIL_MICROSOFT_OAUTH_TENANT=common
|
||||||
MAIL_OAUTH_REDIRECT_URL=
|
MAIL_OAUTH_REDIRECT_URL=
|
||||||
MAIL_APP_URL=http{{SECURE}}://{{PUBLIC_HOST}}/mail
|
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)
|
# Stalwart hosted mail (optional — enable for @ultisuite.fr / custom domains)
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -32,5 +32,8 @@ Thumbs.db
|
|||||||
# Docker data (local volumes)
|
# Docker data (local volumes)
|
||||||
docker-data/
|
docker-data/
|
||||||
|
|
||||||
|
# Rendered Authentik email templates (generated by deploy/authentik/render-blueprints.sh)
|
||||||
|
deploy/authentik/templates/**/*.html
|
||||||
|
|
||||||
# Node.js
|
# Node.js
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
@ -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 |
|
| `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) |
|
| `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`) |
|
| `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 |
|
| `ulti-oidc.yaml` | App OIDC Ultimail |
|
||||||
| `nextcloud-oidc.yaml` | App OIDC Nextcloud |
|
| `nextcloud-oidc.yaml` | App OIDC Nextcloud |
|
||||||
| `onlyoffice-oidc.yaml` | App OIDC OnlyOffice |
|
| `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
|
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
|
## Inscription utilisateur
|
||||||
|
|
||||||
Flow public : `http://localhost/auth/if/flow/ulti-enrollment/`
|
Flow public : `http://localhost/auth/if/flow/ulti-enrollment/`
|
||||||
|
|||||||
@ -31,7 +31,7 @@ entries:
|
|||||||
use_global_settings: true
|
use_global_settings: true
|
||||||
token_expiry: 1800
|
token_expiry: 1800
|
||||||
subject: UltiSuite — Réinitialisation du mot de passe
|
subject: UltiSuite — Réinitialisation du mot de passe
|
||||||
template: email/password_reset.html
|
template: email/ulti_password_reset.html
|
||||||
activate_user_on_success: true
|
activate_user_on_success: true
|
||||||
recovery_max_attempts: 5
|
recovery_max_attempts: 5
|
||||||
recovery_cache_timeout: 300
|
recovery_cache_timeout: 300
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
BP_DIR="$ROOT/deploy/authentik/blueprints"
|
BP_DIR="$ROOT/deploy/authentik/blueprints"
|
||||||
|
TPL_DIR="$ROOT/deploy/authentik/templates"
|
||||||
|
|
||||||
if [[ -z "${SUITE_ORIGIN:-}" || -z "${PUBLIC_HOST:-}" ]]; then
|
if [[ -z "${SUITE_ORIGIN:-}" || -z "${PUBLIC_HOST:-}" ]]; then
|
||||||
echo "render-blueprints: SUITE_ORIGIN and PUBLIC_HOST must be set (source .env.resolved first)" >&2
|
echo "render-blueprints: SUITE_ORIGIN and PUBLIC_HOST must be set (source .env.resolved first)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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() {
|
render_one() {
|
||||||
local tpl="$1"
|
local tpl="$1"
|
||||||
local out="${tpl%.template}"
|
local out="${tpl%.template}"
|
||||||
@ -17,6 +24,7 @@ render_one() {
|
|||||||
-e "s|{{SUITE_ORIGIN}}|${SUITE_ORIGIN}|g" \
|
-e "s|{{SUITE_ORIGIN}}|${SUITE_ORIGIN}|g" \
|
||||||
-e "s|{{PUBLIC_HOST}}|${PUBLIC_HOST}|g" \
|
-e "s|{{PUBLIC_HOST}}|${PUBLIC_HOST}|g" \
|
||||||
-e "s|{{SECURE}}|${SECURE:-}|g" \
|
-e "s|{{SECURE}}|${SECURE:-}|g" \
|
||||||
|
-e "s|{{AUTH_APP_URL}}|${AUTH_APP_URL}|g" \
|
||||||
"$tpl" > "$out"
|
"$tpl" > "$out"
|
||||||
echo "render-blueprints: ${out##*/}"
|
echo "render-blueprints: ${out##*/}"
|
||||||
}
|
}
|
||||||
@ -25,3 +33,8 @@ shopt -s nullglob
|
|||||||
for tpl in "$BP_DIR"/*.yaml.template; do
|
for tpl in "$BP_DIR"/*.yaml.template; do
|
||||||
render_one "$tpl"
|
render_one "$tpl"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for tpl in "$TPL_DIR"/**/*.template "$TPL_DIR"/*/*.template; do
|
||||||
|
[[ -f "$tpl" ]] || continue
|
||||||
|
render_one "$tpl"
|
||||||
|
done
|
||||||
|
|||||||
@ -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 %}
|
||||||
@ -110,6 +110,7 @@ services:
|
|||||||
env_file: ../.env.resolved
|
env_file: ../.env.resolved
|
||||||
volumes:
|
volumes:
|
||||||
- ./authentik/blueprints:/blueprints/custom:ro
|
- ./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-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-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
|
- ./authentik/branding/ultisuite-favicon.png:/web/dist/assets/branding/ultisuite-favicon.png:ro
|
||||||
@ -145,6 +146,7 @@ services:
|
|||||||
env_file: ../.env.resolved
|
env_file: ../.env.resolved
|
||||||
volumes:
|
volumes:
|
||||||
- ./authentik/blueprints:/blueprints/custom:ro
|
- ./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-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-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
|
- ./authentik/branding/ultisuite-favicon.png:/web/dist/assets/branding/ultisuite-favicon.png:ro
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user