Dans ce projet, il fallait qu'un site wordpress puisse se connecter à l'admin de wordpress en utilisant l'authentification d'une application symfony d'un autre site, disposant déjà d'une base d'utilisateurs.
- FOSUserBundle : gestion des utilisateurs sur l’app Symfony
- FOSRestBundle : gestion de l’API sur l’app Symfony
- FOSOAuthServerBundle : support de l’OAuth2 sur l’app Symfony
- WordPress Social Login
Concernant la création de l'application oAuth sous symfony, je ne rentrerais pas dans les détails, je vous invite plutôt à lire le tuto de Guillaume, un véritable expert symfony, qui est très bien fait. Vous remarquerez juste que je n'utilise pas le même plugin WordPress que lui, ce qui ne change rien à la mise en place des bundles symfony.
J'ai choisi le plugin WordPress Social Plugin plutôt que WP-oauth pour une question purement technique : Le code de Wordpress social plugin est plus propre, mieux structuré, puisque l'on peut utiliser des extends pour ajouter des providers. La solution proposé par Guillaume nécessite d'éditer le plugin, ce qui peut s'avérer compliquer lors des mises à jour WordPress (tout le monde n'utilise pas git en deploy ou composer pour maintenir des repos perso).
Fonctionnement de WordPress Social Login
Ce plugin est basé sur la lib HybridAuth, mais sur un fork adapté. Vous pouvez donc partir sur cette lib PHP plutôt que ce plugin WordPress si vous avez le temps de dev les différents filtres et hooks pour les commentaires, le login, etc ....
WordPress Social Login inclut déjà de nombreux providers (Facebook, Github, Twitter .....), dont plusieurs utilisent oAuth2. Ajouter un nouveau provider est simple, il suffit de faire un extends de Hybrid_Provider_Model_OAuth2
<?php /*! * HybridAuth * http://hybridauth.sourceforge.net | https://github.com/hybridauth/hybridauth * (c) 2009-2011 HybridAuth authors | hybridauth.sourceforge.net/licenses.html */ /** * Hybrid_Providers_Symfony */ class Hybrid_Providers_Symfony extends Hybrid_Provider_Model_OAuth2 { // default permissions // (no scope) => public read-only access (includes public user profile info, public repo info, and gists). public $scope = "user"; /** * IDp wrappers initializer */ function initialize() { parent::initialize(); // Provider api end-points $this->api->api_base_url = "http://symfony/app_dev.php/api/v1/user/oauth/user"; $this->api->authorize_url = "http://symfony/app_dev.php/oauth/v2/auth"; $this->api->token_url = "http://symfony/app_dev.php/oauth/v2/token"; $this->api->curl_authenticate_method = "GET"; } /** * load the user profile from the IDp api client */ function getUserProfile() { // refresh tokens if needed $this->refreshToken(); $data = $this->api->api("http://symfony/app_dev.php/api/v1/user/oauth/user","GET"); if ( ! isset( $data->username ) ){ throw new Exception( "User profile request failed! {$this->providerId} returned an invalid response.", 6 ); } $this->user->profile->identifier = @ $data->username; $this->user->profile->firstName = @ $data->firstname; $this->user->profile->lastName = @ $data->lastname; $this->user->profile->displayName = @ $data->firstname.' '.$data->lastname; //$this->user->profile->email = @ $data->email; $this->user->profile->emailVerified = @ $data->email; $this->user->profile->photoURL = @ $data->avatar; return $this->user->profile; } }
Et voilà, rien de plus simple, il suffit de charger cette class dans un plugin après le chargement de WordPress social Login, et votre provider est pris en compte.
IL reste maintenant à ajouter l'admin de ce provider, pour ajouter les clés et l'activer/désactiver à la volé. WordPress Social Login utilise un array dans une variable global pour récupérer tous les providers : $WORDPRESS_SOCIAL_LOGIN_PROVIDERS_CONFIG. Il suffit donc de "pusher" dans cet array. Dans mon cas, je voulais que ce provider apparaisse en tête de liste de tous les providers dans l'admin.
public function add_symfony_wsl(){ global $WORDPRESS_SOCIAL_LOGIN_PROVIDERS_CONFIG; $symfony= [ "provider_id" => "Symfony", "provider_name" => "Symfony", "require_client_id" => true, "callback" => true, "new_app_link" => "http://symfony", "default_api_scope" => "user", "default_network" => true, "cat" => "socialnetworks" ]; array_unshift($WORDPRESS_SOCIAL_LOGIN_PROVIDERS_CONFIG, $symfony); } add_action( 'init', [$this,'add_symfony_wsl'] );
A ce stade, vous pouvez administrer dans wordpress votre plugin. Vous remarquerez que certains images sont absentes. Le plugin WSL cherche en effet dans son propre dossier l'image symfony.png. Malheureusement, il n'y a pas de filtres ou autre pour changer cela (et c'est bien dommage). IL faudra donc en passer par une autre solution (jquery, css, rewrite rules ...).
Le projet en question nécessite d'empêcher la création de compte et le login via l'authentification classique de wordpress. J'ai donc décidé de rediriger ce genre de requête directement vers l'app symfony : login, register, retrieve password, et, au cas ou, l'url de login de wordpress.
Pour cela, j'utilise wp_safe_redirect, qui nécessite d'ajouter tout d'abord la liste des sites qui sont autorisés.
/** * List of safe allowed host */ public function my_allowed_redirect_hosts($content){ $content[] = 'symfony.domain.dev'; $content[] = 'symfony.domain.prod'; return $content; } add_filter( 'allowed_redirect_hosts', [$this, 'my_allowed_redirect_hosts'] , 10 );
Ensuite, Deux petites functions pour les redirections :
/** * Redirect Login url to Symfony Login */ public function redirect_login_page() { $provider='Symfony'; $redirect_to = wsl_get_current_url(); $url=home_url( '/wp-login.php?action=wordpress_social_authenticate&mode=login&provider='.$provider.'&redirect_to=' . urlencode( $redirect_to ) ); wp_safe_redirect( $url ); exit; } /** * Redirect lost password */ public function redirect_loast_pwd(){ $provider= 'Symfony; $domain='http://symfony/'; $url=$domain.'mot-de-passe-oublie/demande'; wp_safe_redirect( $url ); exit; } add_action( 'login_form_login', [$this, 'redirect_login_page']); add_action( 'login_form_register',[$this, 'redirect_login_page']); add_action( 'login_form_lostpassword',[$this, 'redirect_loast_pwd']); add_action( 'login_form_retrievepassword',[$this, 'redirect_loast_pwd']);
Voilà. IL ne vous reste plus qu'à styler le bouton de votre nouveau provider dans vos formulaires.
A titre perso, j'utilise deux applications, une pour le dev et stage, l'autre pour la prod. J'ai donc ajouter une exception afin d'intégrer la bonne app en fonction de l'environnement :
/** * Check env */ public function is_prod(){ if(getenv('WP_ENV')=='development' || getenv('WP_ENV')=='staging') return false; elseif(getenv('WP_ENV')=='production') return true; }
Ensuite, lors de l'array_unshift, ou des redirections, il suffit d'utiliser cette exception
if($this->is_prod()===false){ array_unshift($WORDPRESS_SOCIAL_LOGIN_PROVIDERS_CONFIG, $symfony_dev); }elseif($this->is_prod()===true){ array_unshift($WORDPRESS_SOCIAL_LOGIN_PROVIDERS_CONFIG, $symfony); }
De même pour le provider :
public function get_provider(){ if($this->is_prod()===false){ $provider='symfony_dev'; }elseif($this->is_prod()===true){ $provider='Symfony'; } return $provider; }
Ensuite, le $provider est remplit via $provider = $this->get_provider();
Dans ce cas là, vous aurez compris qu'il vous faudra donc 2 providers, Hybrid_Providers_Symfony et Hybrid_Providers_Symfony_dev