Sécurité & Authentification — Architecture DYNORS¶
Ce document décrit l'architecture cible de l'authentification dans l'écosystème DYNORS. Elle remplace toute auth locale par app, tout token en
localStorage/sessionStorage, et tout cookie partagé entre apps.Cookies (prod) : ADR-0014 — noms neutres
sid(access) /rid(refresh), préfixes__Host-/__Secure-, host-only (pas deDomainparent). Source unique :com.dynors.commons.web.DynorsAccessCookie.Autorisation runtime : rôles et droits produit via dynors-entitlements — pas dans le JWT utilisateur.
1. Vue d'ensemble — dynors-platform/auth¶
L'authentification est centralisée dans un service dédié : dynors-auth, hébergé dans dynors-platform et routé via SLY sur le préfixe /auth/**.
┌─────────────────────────────────────────────────────────────────┐
│ DYNORS AUTH (dynors-platform) │
│ │
│ Provider email/password ──► apps SaaS (SuperGest, Mediconnect, │
│ TRACIUM, BOOKS, ELISA, DAWALALE…) │
│ Provider LDAP ─────────────► apps internes (RAGNAR, TAKKU, │
│ HEISENBERG, RED) │
│ Provider JWT refresh ──────► toutes les apps │
│ │
│ Sortie : JWT signé, posé en cookie HttpOnly Secure SameSite=Lax │
└─────────────────────────────────────────────────────────────────┘
dynors-security reste dans chaque application et ne change pas de rôle : il valide les JWT. Ce qui change, c'est que l'émission des tokens est désormais la responsabilité exclusive de dynors-auth, pas de chaque app.
2. Rôles et responsabilités¶
| Composant | Responsabilité |
|---|---|
dynors-auth (dynors-platform) |
Émission des JWT, refresh, logout, switch tenant. Providers : email/pwd, LDAP. |
dynors-security (dynors-core) |
Validation des JWT dans chaque app. TenantContextFilter, JwtAuthFilter, SlyTransitFilter. |
dynors-db (dynors-core) |
Isolation données par tenant, alimenté par TenantContext. |
InterAppCallService (dynors-commons) |
Appels inter-app via SLY — ajoute X-Source-App, X-Tenant-Id, X-Request-Id et signature HMAC. |
3. Structure du JWT utilisateur¶
Le JWT émis par dynors-auth contient uniquement l'identité (ADR-0011 / ADR-0013) :
{
"iss": "dynors-auth",
"sub": "user-uuid",
"tenant": "nom-du-tenant",
"aud": "sgst",
"exp": 1752000000
}
Ce qui n'est PAS dans le JWT utilisateur
roles/ permissions produit →GET /entitlements/me(dynors-entitlements).source-app→ headerX-Source-Appposé parInterAppCallService(inter-app M2M uniquement).
Le token est posé en cookie HttpOnly par dynors-auth (sid en prod avec préfixe __Host-). Jamais en localStorage ni en sessionStorage. Le navigateur n'utilise pas Authorization: Bearer pour la session utilisateur.
4. Endpoints de dynors-auth¶
Tous les endpoints sont accessibles via SLY au préfixe /auth/**.
| Méthode | Chemin | Description |
|---|---|---|
POST |
/auth/login |
Login email/password → JWT en cookie |
POST |
/auth/ldap/login |
Login LDAP (apps internes) → JWT en cookie |
POST |
/auth/refresh |
Renouvelle le JWT à partir du refresh token cookie |
POST |
/auth/logout |
Révoque les tokens, efface les cookies |
GET |
/auth/me |
Retourne l'identité de l'utilisateur connecté |
POST |
/auth/tenant/switch |
Bascule sur un autre tenant (si multi-tenant user) |
5. Flux de connexion (app SaaS)¶
Utilisateur
│
│ GET /login (frontend app, ex. SuperGest)
│
▼
Frontend (Angular)
│
│ Redirect vers SLY : GET sly.dynors.com/auth/login?app=supergest&return=https://supergest.dynors.com/dashboard
│
▼
dynors-auth (via SLY /auth/**)
│ POST /auth/login { email, password, tenant }
│ → vérifie credentials
│ → génère JWT { sub, tenant, aud }
│ → Set-Cookie: __Host-sid=<JWT>; HttpOnly; Secure; SameSite=Lax; Path=/
│
▼
Redirect vers return URL (SuperGest dashboard)
│
▼
Requêtes suivantes — navigateur envoie le cookie automatiquement
│
▼
dynors-security (JwtAuthFilter dans SuperGest-API)
→ valide le JWT
→ alimente TenantContext
→ autorise la requête
6. Flux de connexion (app interne LDAP)¶
Employé DYNORS
│
│ GET /login (frontend RAGNAR / RED / TAKKU / HEISENBERG)
│
▼
dynors-auth (via SLY /auth/ldap/login)
│ → vérifie credentials LDAP
│ → génère JWT { sub, tenant:"dynors-internal", aud }
│ → Set-Cookie: cookie host-only (sid / __Host-sid en prod)
│
▼
App interne (RAGNAR, RED…)
→ dynors-security valide, TenantContext alimenté
7. Appels inter-app (service-to-service)¶
Les appels entre apps DYNORS ne passent jamais par le token utilisateur directement. Ils utilisent InterAppCallService qui signe chaque requête HMAC et ajoute :
X-Source-App: supergest
X-Tenant-Id: nom-du-tenant
X-Request-Id: uuid-trace
X-Sly-Timestamp: <epoch-ms>
X-Sly-Signature: <hmac-sha256>
// Exemple dans SuperGest — appel vers FISCAL via SLY
@Service @RequiredArgsConstructor
public class FiscalClient {
private final InterAppCallService interApp;
public FiscalResponse certify(String tenant, FiscalPayload payload) {
return interApp.post(
"/fiscal/api/v1/certify",
payload,
FiscalResponse.class,
tenant
);
}
}
SlyTransitFilter côté FISCAL vérifie la signature HMAC avant de traiter la requête.
8. Intégration dans chaque app — ce qui ne change pas¶
dynors-security s'utilise de la même façon dans chaque app. La seule différence est que /auth/** est désormais délégué à SLY → dynors-auth et non géré localement.
@Configuration @EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthFilter jwtAuthFilter; // fourni par dynors-security
@Autowired
private SlyTransitFilter slyTransitFilter; // fourni par dynors-commons
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(slyTransitFilter, JwtAuthFilter.class);
return http.build();
}
}
Plus d'endpoint /api/auth/login local
Chaque app ne gère plus son propre endpoint de login. Elle reçoit des requêtes avec un cookie JWT déjà émis par dynors-auth. Le SecurityConfig n'a plus de permitAll() sur /api/auth/** car cet endpoint n'existe plus dans l'app.
9. Configuration application.yml¶
dynors:
security:
jwt:
secret: ${JWT_SECRET} # même clé que dynors-auth (symétrique) ou clé publique RSA
expiration: 3600 # 1h — le refresh est géré par dynors-auth
interapp:
source-app: supergest # code de l'app courante
gateway-url: ${SLY_BASE_URL:https://sly.dynors.com}
return-base-url: ${SLY_BASE_URL}/supergest
sly-forward-secret: ${SLY_FORWARD_SECRET}
sly-signature-window-ms: 30000
10. Cookies et sécurité frontend¶
| Règle | Détail |
|---|---|
| Host-only | Pas de Domain parent (.dynors.com) — ADR-0014. |
| HttpOnly | Cookie non accessible depuis JavaScript (anti-XSS). |
| Secure | Obligatoire en prod ; désactivé en local (http://localhost). |
| SameSite=Lax | Atténuation CSRF ; Strict sur refresh si applicable. |
| Noms neutres | Base sid / rid — pas de dynors_token. |
| Jamais localStorage | Interdit en production DYNORS. |
Le frontend Angular lit l'identité via GET /auth/me, puis les droits via GET /entitlements/me — sans décoder le cookie côté client.
11. Autorisation — entitlements et RBAC¶
Droits produit (scopes, tiers, features) : résolus à runtime par dynors-entitlements via EntitlementsClient + EntitlementsGuard (module partagé dynors-entitlements-client). Appels via SLY uniquement.
RBAC employé / rôles Spring (apps internes ou rôles techniques JWT legacy transitoires) : @PreAuthorize + dynors-security — ne pas confondre avec les entitlements SaaS.
@Service @RequiredArgsConstructor
public class InvoiceService {
private final EntitlementsGuard entitlements;
public InvoiceDto get(String id) {
entitlements.require("app:supergest", "invoice:read");
// ...
}
}
12. Partenaires B2B — SLY Pass¶
Les intégrations techniques partenaires (webhooks entrants, API B2B) utilisent le mécanisme X-Sly-Pass géré par SLY, distinct du flux de connexion utilisateur. Voir Gateway SelebeYone.
13. Références¶
| Sujet | Document |
|---|---|
Architecture complète dynors-auth |
dynors-auth |
| Cookies isolés par app | ADR-0014 |
| État d'implémentation auth/entitlements | Auth · Entitlements · Sécurité — implémentation |
| SLY — Gateway et routage | Gateway SelebeYone |
| Routage inter-app | Routage inter-app |
| Multi-tenancy | Multi-tenancy |
| Secrets — Vault | DevOps — Vault |
| Base de données multi-tenant | Base de données multi-tenant |