Ce plugin fournit à SPIP une implémentation native du protocole OAuth2 (RFC 6749) ainsi que de son extension OpenID Connect (Core 1.0). Il permet à un site SPIP d’agir comme:
- un client d’un serveur d’autorisation OAuth2
- un client d’un fournisseur d’identité OpenID Connect (Keycloak...)
L’implémentation est autonome et ne repose sur aucune bibliothèque tierce (ni Composer ni league/oauth2-client). Il constitue une brique technique destinée à être utilisée par d’autres plugins SPIP (API exposées).
Installation
Le plugin s’installe comme tous les autres plugins SPIP. Pré-requis:
- SPIP 4.x
- PHP 8.x recommandé
- Extension OpenSSL activée
- cURL activé
- Accès à un provider OAuth2
Principes
- Agnostique : compatible avec tout provider OAuth2 / OIDC standard
- Configuration dynamique à chaque appel
- Architecture extensible (Providers / Grants / Pipelines)
Le plugin est un socle technique destiné à être utilisé par d’autres plugins SPIP.
Sécurité
Sécurité OAuth2 (RFC 6749)
- Authorization Code Flow
- Support Refresh Token sécurisé
- Support Client Credentials
- Nettoyage automatique des paramètres sensibles (
code,state) - Suppression du contexte après usage
- Gestion transactionnelle des erreurs
Sécurité PKCE (RFC 7636)
- Génération cryptographiquement sécurisée du
code_verifier - Support méthode S256
- Stockage temporaire en session
- Vérification automatique lors de l’échange
- Nettoyage automatique après validation
- Nettoyage des contextes expirés (> 10 minutes)
Sécurité OpenID Connect (Core 1.0)
Lorsque OIDC est activé :
- Vérification signature JWT RS256
- Récupération dynamique JWKS
- Cache JWKS (1h)
- Validation des claims :
issaudexp(+ tolérance 60s)nonceat_hash(si présent)
- Nettoyage automatique nonce + PKCE après validation
Validation cryptographique
- Conversion JWK vers PEM sans dépendance externe
- Vérification via OpenSSL PHP natif
Gestion du state
Le plugin utilise un stockage serveur du state afin de sécuriser et fiabiliser le flux OAuth2.
Chaque state est stocké temporairement (fichiers) avec :
session_iduser_agentappcreated_at
API principales
Obtenir un token: get_access_token()
oauth2_client_get_access_token(string $app, array $config, string $mode = 'session', ?int $user_id = null): string|false
$app est l’identifiant logique de la configuration OAuth2 utilisée (clé interne du plugin), permettant de distinguer plusieurs connexions/providers et de stocker séparément leurs tokens.
Il peut être le même que celui déclaré auprès du provider (sans obligation): “monApp”, “SPIP”...
Modes supportés :
- session : flux utilisateur (par défaut)
- user : token persistant lié à un utilisateur SPIP
- cron : token technique global
Cette API :
- Retourne un access_token valide
- Gère automatiquement :
- expiration
- refresh_token
- échange authorization_code
- PKCE
- validation OIDC
- nettoyage d’URL - gestion d’erreurs transactionnelles
Elle peut déclencher une redirection HTTP (mode session uniquement)
Configuration complète
Exemple de Keycloak avec un Provider générique en mode Authorization Code + PKCE + OIDC
include_spip('inc/oauth2_client');
$token = oauth2_client_get_access_token($app, [
// Obligatoire : type de provider
'provider' => 'generic',
// Obligatoire : endpoints OAuth2
'authorize_endpoint' => 'http://localhost:8080/realms/demo/protocol/openid-connect/auth',
'token_endpoint' => 'http://localhost:8080/realms/demo/protocol/openid-connect/token',
// Obligatoire : identifiants client
'client_id' => 'VOTRE_ID',
'client_secret' => 'VOTRE_SECRET',
// Obligatoire : URI de redirection enregistrée dans Keycloak
'redirect_uri' => $redirect_uri,
// Facultatif : Si PKCE (conseillé)
'pkce' => [
'enabled' => true,
'method' => 'S256',
],
// Facultatif : Obligatoire pour activer OIDC
'oidc' => [
'enabled' => true,
],
// Facultatif : Obligatoire si OIDC activé
'issuer' => 'http://localhost:8080/realms/demo',
// Facultatif : Obligatoire selon contexte (openid...)
'scope' => 'openid profile email',
]);
Configuration minimale
Exemple de Dropbox avec un Provider spécifique (exemple du Plugin Dropbox)
include_spip('inc/oauth2_client');
$token = oauth2_client_get_access_token($app, [
'provider' => 'dropbox',
'client_id' => $client_id ?? null,
'client_secret' => $client_secret ?? null,
'redirect_uri' => $redirect_uri ?? null,
]);
Obtenir un utilisateur: oauth2_client_get_user()
include_spip('inc/oauth2_client');
$user = oauth2_client_get_user($app, $mode = 'user', $user_id = null);
Retourne :
[
'sub' => '',
'email' => '',
'username' => '',
'given_name' => '',
'family_name' => '',
'name' => '',
]
Extensibilité
Providers
Les providers encapsulent la configuration et les endpoints d’un serveur OAuth2 / OIDC. Deux types sont possibles :
- Provider générique: Les endpoints (authorization, token, userinfo, etc.) sont fournis dynamiquement lors de l’appel.
- Providers spécifiques: Dédiés à des services ou serveurs particuliers (paramètres, authentification client, particularités d’implémentation).
Ils n’ont pas vocation à être intégrés dans ce plugin cœur, mais peuvent être ajoutés pour étendre la classe générique.
Pour créer un provider spécifique, il suffit d’ajouter une classe héritant du Provider générique dans le dossier oauth_provider/MonProvider.php d’un plugin tiers (ou dans squelettes/). La classe Dropbox est fournie à simple titre d’exemple.
Grants
Les grants implémentent les différents flux OAuth2. Les flux actuellement pris en charge sont:
- AuthorizationCode
- RefreshToken
- ClientCredentials
Chaque grant est implémenté sous forme de classe dédiée, ce qui permet d’en ajouter de nouveaux si nécessaire (par exemple Device Code, JWT Bearer, etc.).
Pour créer un grant spécifique, il suffit d’ajouter une classe héritant du Grant OAuth2ClientGrantAbstract dans le dossier oauth_grant/MonGrant.php d’un plugin tiers (ou dans squelettes/).
Pipelines
Le plugin OAuth2 Client propose plusieurs pipelines permettant aux plugins tiers de modifier dynamiquement les providers et les grants à différentes étapes du flux OAuth2.
oauth2_client_authorization_provider
Ce pipeline est appelé lors de la génération de l’URL d’autorisation OAuth2, après la création (instance) du provider.
$provider = pipeline('oauth2_client_authorization_provider',
[
'args' => [
'app' => $app,
'config' => $config,
],
'data' => $provider,
]);
oauth2_client_token_provider
Ce pipeline est appelé lors de la récupération ou du rafraîchissement d’un token, après la création (instance) du provider.
$provider = pipeline('oauth2_client_token_provider',
[
'args' => [
'app' => $app,
'config' => $config,
],
'data' => $provider,
]);
oauth2_client_grant
Ce pipeline est appelé lors de la récupération du Grant, après la création (instance) du grant.
$grant = pipeline('oauth2_client_grant',
[
'args' => [
'app' => $app,
'grant_type' => $config['grant_type'],
'config' => $config,
],
'data' => $grant,
]);
Stockage
Les tokens sont stockés dans la meta oauth2_client:
$meta[$storage_key] = [
access_token,
refresh_token,
id_token,
token_type,
expires_at,
user,
stored_at
];
La clé de stockage dépend du contexte :
- session : basé sur session_id
- user : basé sur user_id
- cron : global par application
Démonstration
En cours de réalisation. Actuellement une version “brut” est disponible sous demo/demo.html (accessible depuis l’administtration du plugin) sur la base d’un Docker/Keycloack en local
- realms = ’demo’
- client_id = VOTRE_ID
- client_secret = VOTRE_SECRET
Version en cours de stabilisation.
No discussion
Add a comment
Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :
Merci d’avance pour les personnes qui vous aideront !
Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.
Follow the comments:
|
