ENbext.dev
DocumentationFonctionnalités

Traduction automatique depuis l’anglais. Consulter l’original.

Sur cette page

Authentification

La fonctionnalité Authentification répond à une question par requête : qui est l'appelant ? Un plugin Auth prend une requête, examine les informations d'identification qu'il contient (un JWT dans l'en-tête d'autorisation, un cookie de session, un rappel OAuth) et renvoie soit un AuthUser, soit rien du tout. Ce qu'il fait, et non, c'est stocker les sessions. C'est le travail du Capacité de session, et la séparation est délibérée : il vous permet de mélanger une stratégie JWT avec un magasin de sessions Redis, ou une authentification par lien magique avec des sessions uniquement par cookies, sans qu'aucun des plugins ne connaisse l'autre.

Cette page couvre la surface des traits, les types de fournisseur qu'un plugin Auth peut être et comment le plugin de référence @bext/auth-jwt utilise la forme pour valider les jetons du porteur.

Le trait#

Chaque plugin Auth implémente un trait, AuthPlugin, défini dans bext-plugin-api::auth. Il a une méthode obligatoire, trois méthodes facultatives et une forme pour les données qu'il transmet :

rust
pub trait AuthPlugin: Send + Sync {
    fn name(&self) -> &str;
    fn provider_kind(&self) -> AuthProviderKind;

    fn resolve(&self, ctx: &AuthRequestContext)
        -> Result<Option<AuthUser>, String>;

    fn begin_login(&self, ctx: &AuthRequestContext, params: LoginParams)
        -> Result<LoginAction, String> { /* default: not supported */ }

    fn complete_login(&self, ctx: &AuthRequestContext, callback: CallbackData)
        -> Result<AuthUser, String> { /* default: not supported */ }

    fn logout(&self, ctx: &AuthRequestContext) -> Result<(), String> {
        Ok(())
    }
}

Seuls name, provider_kind et resolve doivent être implémentés. Un backend de validation uniquement comme @bext/auth-jwt conserve les trois autres par défaut, car il n'y a pas de flux de connexion interactif à exécuter : le jeton est validé ou non. Un plugin OAuth ou magic-link complet remplace les quatre.

resolve renvoie Ok(None) pour « non connecté » — c'est la forme attendue d'une requête anonyme, pas une erreur. Seules les informations d'identification mal formées ou un échec du backend doivent renvoyer Err(..), que le runtime affiche comme 401 ou 500.

AuthUser#

Le type de retour d'un resolve réussi est AuthUser, et l'ensemble de champs est délibérément petit :

rust
pub struct AuthUser {
    pub subject: String,
    pub tenant_id: Option<String>,
    pub role: String,
    pub permissions: Vec<String>,
    pub attributes: HashMap<String, String>,
}
  • subject est l’identifiant unique stable de l’utilisateur au sein de ce fournisseur.
  • tenant_id est la portée du locataire sur les déploiements multi-locataires.
  • role est le nom du rôle principal, permissions est la liste affinée.
  • attributes est la trappe de secours pour les revendications spécifiques au fournisseur (extras de jeton d'identification OAuth, attributs SAML, revendications personnalisées) de sorte que le trait lui-même n'augmente jamais les champs spécifiques au fournisseur.
Conseil

Les champs tels que l'adresse e-mail et le nom d'affichage ne se trouvent pas directement sur AuthUser : ils se trouvent dans attributes. C'est intentionnel : tous les fournisseurs ne les fournissent pas, et élever l'un d'entre eux imposerait la forme d'un fournisseur à tous les autres. Configurez votre plugin Auth pour copier des revendications spécifiques dans attributes si vous en avez besoin en aval.

Il n'y a pas de email, pas de display_name, pas de avatar_url. Ceux-ci vivent dans attributes, car tous les fournisseurs ne les ont pas et la forme est par ailleurs suffisamment différente pour que l'élévation de l'un d'entre eux reviendrait simplement à élever la forme d'un fournisseur par rapport à celle de tous les autres.

Types de fournisseurs#

Un plugin Auth indique au runtime quelle forme ** du flux qu'il implémente avec AuthProviderKind :

Gentil Quand utiliser Implémente le flux de connexion ?
Token JWT sans état, support opaque, clés API Non
Password Nom d'utilisateur + mot de passe pour un magasin Oui
OAuth OAuth 2 / OIDC basé sur la redirection Oui
MagicLink Lien unique envoyé par e-mail ou SMS Oui
Passkey WebAuthn/passkeys — se termine en un seul appel Oui

Le runtime l'utilise pour décider quelles routes HTTP monter. Un plugin OAuth a besoin d'une route /auth/callback ; un plugin de jeton ne le fait pas. Les fournisseurs apatrides ignorent complètement les méthodes de flux de connexion et valident simplement les informations d'identification figurant sur chaque demande.

Comment @bext/auth-jwt utilise la forme#

@bext/auth-jwt est le plugin Auth de référence pour la validation de token sans état. Il implémente AuthPlugin en une centaine de lignes :

  1. provider_kind renvoie AuthProviderKind::Token.
  2. resolve extrait un jeton de la demande (en-tête d'autorisation d'abord, puis un cookie nommé) et l'exécute via jsonwebtoken::decode. Un jeton valide devient un AuthUser ; un jeton mal formé renvoie une erreur ; un jeton manquant renvoie Ok(None).
  3. begin_login, complete_login et logout conservent leurs valeurs par défaut. Il n'y a pas de flux interactif pour les jetons au porteur : soit le jeton est arrivé à la demande, soit il ne l'est pas.

Les revendications JWT correspondent à AuthUser comme ceci :

  • sub → subject
  • tenant_id (ou tenantId ) → tenant_id
  • roles[0] (ou role ) → role
  • permissions → permissions
  • Tout le reste reste intact dans le JWT (aucune fuite dans attributes à moins que le plugin ne soit configuré pour copier des revendications spécifiques).

HS256 (secret partagé) et RS256 (clé publique PEM pré-analysée) sont pris en charge. La récupération JWKS est délibérément hors de portée du plugin de référence : les opérateurs qui le souhaitent doivent pré-récupérer au démarrage et remettre le PEM décodé à JwtAuthPlugin::new_rs256_pem. La récupération à l'intérieur du plugin lierait l'implémentation de référence à un client HTTP, et le but d'un plugin de référence est d'être la plus petite chose qui exerce la forme.

Construire un plugin#

Le plugin a deux constructeurs et un modificateur, tous synchronisés :

rust
use bext_auth_jwt::JwtAuthPlugin;

// HS256 with a shared secret
let plugin = JwtAuthPlugin::new_hs256("your-shared-secret");

// RS256 with a pre-parsed public key
let plugin = JwtAuthPlugin::new_rs256_pem(pem_bytes)?;

// Override the cookie name (default: "next-auth.session-token")
let plugin = JwtAuthPlugin::new_hs256(secret)
    .with_cookie_name("my-session");

Configuration de l'authentification dans bext.config.toml#

Vous n'appelez jamais un plugin d'authentification directement à partir du code de l'application. Vous l'installez, pointez [auth] dessus dans la configuration, et le runtime effectue le câblage :

toml
[auth]
provider = "jwt"

[auth.jwt]
# HS256 with a shared secret (development)
secret = "env:BEXT_AUTH_JWT_SECRET"

# Or RS256 with a PEM file (production)
# public_key_pem = "/etc/bext/jwt-public.pem"

# Optional: where to read the token from
cookie_name = "next-auth.session-token"

[auth] sélectionne le fournisseur actif ; [auth.jwt] contient les paramètres spécifiques au plugin. D'autres plugins déclarent requires_capabilities = ["auth"] dans leur manifeste et utilisent l'API de la fonction hôte pour lire l'utilisateur actuel — aucun plugin ne touche jamais directement un JWT.

L'authentification dépend de la session#

L'installation de @bext/auth-jwt est très bien en soi, car l'authentification par jeton du porteur est sans état et ne nécessite pas de magasin de session. Mais la plupart des fournisseurs interactifs – OAuth, magic-link, mots de passe – ont besoin d’un endroit où conserver la preuve qu’une connexion a eu lieu. Il s'agit du Capacité de session, et chaque plugin Auth interactif déclare requires_capabilities = ["session"] dans son manifeste.

Lorsque vous installez un plugin Auth via la CLI, le résolveur de registre lit les exigences et vous guide dans la sélection d'un backend de session si vous n'en avez pas déjà configuré. Vous ne vous retrouverez pas avec un plugin OAuth qui n'a nulle part où stocker ses sessions, car le résolveur l'attrape au moment de l'installation.

Le câblage d'exécution pour un flux de connexion interactif ressemble à ceci :

  1. L'utilisateur accède à un itinéraire protégé sans session. Le runtime appelle begin_login sur le plugin Auth actif ; le plugin renvoie un LoginAction::Redirect (OAuth), LoginAction::Prompt (mot de passe, lien magique) ou LoginAction::Complete (clés d'accès).
  2. L'utilisateur revient via la route de rappel. Le runtime appelle complete_login avec les données de rappel. Le plugin renvoie un AuthUser en cas de succès.
  3. Le runtime transmet à l'utilisateur le plugin actif Session, qui émet une session et renvoie un identifiant. L'identifiant va dans un cookie.
  4. Lors des requêtes suivantes, resolve lit l'identifiant de session à partir de AuthRequestContext::session_id, recherche la session via la fonctionnalité Session et renvoie l'utilisateur.

Le plugin Auth ne conserve jamais l'état de session lui-même. Cette séparation est la seule raison pour laquelle Auth et Session sont deux capacités au lieu d'une.

Où aller ensuite#

  • Aperçu des capacités – comment fonctionne l’échelle de promotion des capacités et pourquoi les cinq principes fondamentaux sont réunis.
  • Capacité de session — l'autre moitié de l'histoire de connexion et où se trouve réellement l'état de la session.
  • Présentation du système de plugins — les niveaux sandbox (WASM, QuickJS, nsjail) dans lesquels un plugin Auth peut s'exécuter.
  • Construire un plugin — échafaudage d'un plugin et déclaration de provides_capabilities = ["auth"] dans le manifeste.
Modifier cette page ↗Besoin d’aide ? ↗
Rechercher dans la documentation

Saisissez un sujet, une commande ou une question.