Sécurité et Server ActionseQuantic.UI facilite la communication entre le client et le serveur par les Server Actions, tout en garantissant que ce pont est sûr par défaut.
Une Server Action est une méthode C# définie dans un composant, qui peut être invoquée directement depuis le navigateur. Le compilateur génère automatiquement le pont d'appel (Fetch API) et le middleware du serveur résout l'exécution.
[Authorize(Roles = "Admin")]
public async Task<bool> DeleteUser(Guid userId) {
// Logique exécutée seulement sur le serveur
🔒 Une page qui exige une autorisation
Depuis 0.2.0-preview.61
[Page("/backoffice/queue")]
[Authorize(Policy = "Backoffice")]
public sealed class VerificationQueuePage : StatefulComponent, IServerPrefetch { … }
Les mêmes [Authorize] et [AllowAnonymous] qu'une Server Action accepte gardent une page. Sa route les porte comme métadonnées d'endpoint, comme le ferait RequireAuthorization, donc l'autorisation d'ASP.NET Core décide avec les schémas et les politiques que l'application a configurés (AddAuthentication, AddAuthorization) :
•
un chargement complet anonyme est challengé par le schéma par défaut, la redirection OpenID Connect vers la connexion, par exemple ;
•
une visiteuse connectée sans la politique reçoit un 403 ;
•
une navigation client vers la page reçoit un 401 ou un 403 au lieu de la page, et le routeur charge alors la route en entier, si bien que le challenge ou le 403 se produit comme à n'importe quelle visite ;
•
une requête refusée n'atteint jamais la page : son IServerPrefetch ne tourne pas, et aucun de ses champs n'est écrit dans le HTML.
[Authorize] sans politique demande seulement une visiteuse connectée. Sous une FallbackPolicy applicable à toute l'application, une page marquée [AllowAnonymous] (la page de connexion) se sert toujours, ainsi que le runtime et les modules de page dont elle a besoin. Un [Authorize] au niveau de la classe ne gardait que les Server Actions de la page ; il garde aussi la page maintenant.
Aux pages 404 et 500 qu'une application enregistre on pose la même question avant de les dessiner à la place d'une route, comme le font leurs propres routes : une page qui ne nomme aucune exigence est sous la FallbackPolicy, la requête est la ressource de la politique, et une page que la visiteuse n'a pas le droit de voir n'est pas dessinée, et son IServerPrefetch ne tourne pas non plus.
eQuantic.UI prend en charge les attributs [Authorize] et [AllowAnonymous].
•
Le ServerActionsMiddleware vérifie si l'utilisateur courant a les permissions nécessaires avant d'invoquer la méthode.
•
Si l'autorisation échoue, le serveur renvoie une erreur 403 Forbidden ou 401 Unauthorized, bloquant l'exécution de la logique métier.
Pour prévenir les attaques par injection et la consommation excessive de ressources :
•
Limite de taille : le middleware impose des limites strictes à la taille du corps de la requête.
•
Liste blanche de types : la désérialisation des arguments est restreinte à des types connus et sûrs, ce qui prévient les attaques par désérialisation non sûre.
3. Enregistrement des actions
Seules les méthodes marquées explicitement [ServerAction] peuvent être invoquées. Il n'est pas possible d'appeler n'importe quelle méthode publique arbitraire via l'API, ce qui garantit que la surface d'attaque est maîtrisée.
4. D'où venait la requête
Depuis 0.2.0-preview.61
Une Server Action refuse une requête que la page d'un autre site envoie, avant d'en lire quoi que ce soit. Le navigateur dit d'où une requête est venue, et l'endpoint répond 403 Forbidden quand :
•
l'Origin n'est ni l'hôte propre de l'application ni une origine que l'application autorise, l'Origin: null opaque compris ;
•
il n'y a pas d'Origin, et Sec-Fetch-Site dit cross-site ou same-site.
Une requête sans aucun des deux en-têtes n'est pas celle d'un navigateur, et elle tourne. L'hôte propre de l'application est le Host de la requête : derrière un proxy qui le réécrit, app.UseForwardedHeaders() le restaure depuis les proxies auxquels l'application fait confiance, et un X-Forwarded-Host brut n'est jamais lu. L'hôte et le port sont comparés, pas le schéma, et un Host sans port représente le port par défaut de l'origine, donc une application derrière un proxy qui termine TLS n'a besoin d'aucun réglage.
Une page servie depuis un autre domaine que ses actions liste cette origine, dans appsettings.json :
"AllowedOrigins": [ "https://admin.example.com" ]
ou dans Program.cs :
builder.Services.AddUI(options => options.AllowServerActionOrigins("https://admin.example.com"));
Chaque origine est scheme://host[:port], sans chemin ni query. Une qui ne l'est pas arrête l'application au démarrage, en nommant la valeur.
📡 Protocole de communication
La communication se fait par HTTP POST vers l'endpoint réservé /api/_equantic/actions.
•
Payload : contient l'ID unique de l'Action et la liste des arguments sérialisés.
•
Réponse : renvoie l'objet résultat ou une erreur structurée.