1. Authentification sociale avec Angular 1 3ième Webday ESGI - Paris - 28/04/2016 2. Présentation Mickaël ROLO Develop’hackeur chez Darkmira
[email protected] Expert Zend Framework 2 2 3. Une panoplie de réseaux sociaux • Plus de 1 milliard de sites internet dans le monde •68% des internautes sont actifs sur les réseaux sociaux en 2015 •Facebook, Twitter, Google+,Github, Instagram, LinkedIN, Viadeo, Twitch… des réseaux sociaux en masse ! 3 4. Comment gérer simplement la connexion des différents réseaux sociaux sur une application Angular ? 4 5. Sommaire • Chemin d’une connexion avec un réseau social •Satellizer : configurer les connexions en un seul endroit • Connexion côté backend •Echanges connectés entre backend et frontend 5 6. Chemin de connexion 6 7. Des connexions Cross Domain Appli frontend sur un serveur A (Angular) http://frontend.com Appli backend sur un serveur F (ZF2) http://backend.com • Cross Domain : venant de noms de domaine différents Chemin de connexion > Satellizer > Backend > Echanges Que se passe-t-il si je clique sur un de ces logos ? 7 8. Que se passe-t-il si je clique ? Utilisateur Appli frontend sur un serveur A (Angular) Clique sur logo Facebook Voici mon id d’app facebook Peux tu authentifier cet internaute ? Qui es-tu et autorises tu cette appli à accéder à tes infos ? 1 2 3 5 Voici son id et un code pour demander des infos Login mp et accepter 4 Appli backend sur un serveur B (ZF2) 7 Voici le code et ma clé secrète (fournie par facebook), qui est l’utilisateur derrière cet id utilisateur ? 8 Il s’agit de ….. JWT 6 On le connaît ? 9 Chemin de connexion > Satellizer > Backend > Echanges 8 9. Un JWT ? • Json Web Token : « passeport » de l’utilisateur Appli frontend sur un serveur A (Angular) http://frontend.com Appli backend sur un serveur C (php Zf2) http://backend.com Appli backend sur un serveur F (NodeJS) JWTJWT JWT Chemin de connexion > Satellizer > Backend > Echanges 9 10. JWT : une structure définie { "alg" : "HS256", "typ" : "JWT, "iss" : http://backend.com, "aud" : http://frontend.com, "iat" : strtotime("now"), //date de création du token "exp" : 5000+ strtotime("now"), //temps de validité du token "sub" : "description of the token", "nbf" : strtotime("now") + $this->nbf,//invalide avant cette date 'context' : $context //infos diverses (utilisateur par exemple) } eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJhdWQiOiIiLCJpYXQiOjE0NjE2NzU4MzMsI mV4cCI6MTQ2MjAwODIzMywic3ViIjoiZGVzY3JpcHRpb24gb2YgdGhlIHRva2VuIiwibmJmIjoxNDYxN jc1ODMzLCJjb250ZXh0Ijp7InNDbGllbnROYW1lIjoic3VwZXJhdXRvIiwic0NmYUxpYmVsbGUiOiIiLCJz Um9sZSI6IndlYnNpdGUifX0.N3YxMuXn_RsvfJHq3a64oZQgG5c3M3r2mzNzdiR47TE Chemin de connexion > Satellizer > Backend > Echanges • RFC 7519 : Json Web Token •Crypté en base 64 en 3 parties 10 11. Déclarer notre app sur Facebook •https://developpers.facebook.com •Ajouter votre appli en tant que site web Chemin de connexion > Satellizer > Backend > Echanges 11 12. Déclarer notre app sur Facebook •Paramètres > ajouter une plateforme > site web •Puis indiquer nom de domaine et ip serveur (Avancé) Chemin de connexion > Satellizer > Backend > Echanges 12 13. •Examen des app > Rendre frontend publique Déclarer notre app sur Facebook •La procédure est similaire sur les autres •ATTENTION : certaines n’acceptent pas “.localhost” Chemin de connexion > Satellizer > Backend > Echanges 13 14. Satellizer Chemin de connexion > Satellizer > Backend > Echanges 14 15. Satellizer, on centralise ! • Réseaux sociaux similaires mais pas identiques ! • Centralise et uniformise l’authentification •https://github.com/sahat/satellizer Chemin de connexion > Satellizer > Backend > Echanges 15 16. Satelizer, simple à installer Index : importer satellizer angular.module('defaultApp', [ //Angular modules … 'satellizer', // My Modules ‘userModule’ …. 1 <script src="bower_components/satellizer/satellizer.min.js"></scrip> 2 Ajouter Satellizer à l’app 16 Chemin de connexion > Satellizer > Backend > Echanges 17. Satellizer, simple à configurer 3 angular.module('userModule') .config(['$authProvider',function ($authProvider) { $authProvider.facebook( clientId: ‘public key', //name: 'facebook', url: appConfig.server + 'user/facebook', //http:/backend.com/user/facebook //authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', //redirectUri: window.location.origin + '/', //requiredUrlParams: ['display', 'scope'], //scope: ['email'], //scopeDelimiter: ',', //display: 'popup', //type: '2.0', //popupOptions: { width: 580, height: 400 }); } Configurer Satellizer 17 Chemin de connexion > Satellizer > Backend > Echanges 18. 3 Configurer Satellizer $authProvider.twitter( clientId: *****************************, url: appConfig.server + 'user/twitter' //authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', //redirectUri: window.location.origin, //type: '1.0', //popupOptions: { width: 495, height: 645 } ); Satellizer, simple à configurer 18 Chemin de connexion > Satellizer > Backend > Echanges 19. angular.module('userModule').controller('SocialController', ['$scope', '$auth', '$localStorage', '$location', function ($scope, $auth, $localStorage, $location) { $scope.socialLoading = false; $scope.authenticate = function (provider) { $scope.socialLoading = true; $auth.authenticate(provider) .then(function (response) { //console.log('Response : ',response); $localStorage.user = response.data.oUser; $scope.socialLoading = false; $location.path('/profil'); }) .catch(function (response) { // Something went wrong. }); }; }]); SocialController4 Satellizer, simple à utiliser 19 Chemin de connexion > Satellizer > Backend > Echanges 20. 4 Page.html <element ng-click="authenticate('facebook')" ></element> <element ng-click="authenticate(‘twitter')" ></element> <element ng-click="authenticate(‘instagram')" ></element> Satellizer, simple à utiliser 20 Chemin de connexion > Satellizer > Backend > Echanges 21. Backend 21 Chemin de connexion > Satellizer > Backend > Echanges 22. Backend : Vérifier et générer •Cross domain header('Access-Control-Allow-Origin: ' . 'http://frontend.com'); header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS"); header('Access-Control-Allow-Headers: Content-Type, x-xsrf-token,x-session-token,Authorization'); header('Access-Control-Allow-Credentials: true'); FacebookController 1 2 3 Récupérer les paramètres (code et id client) Demander un token à Fb avec la clé secrète Récupérer les infos utilisateurs via l’api facebook 4 Traiter les infos utilisateurs 5 Générer un JWT 6 Retourner le JWT ainsi que des infos en clair (nom utilisateur…) 22 Chemin de connexion > Satellizer > Backend > Echanges 23. Backend : Vérifier et générer • FacebookController $sPostdata = file_get_contents("php://input"); $aPostDatas = json_decode($sPostdata, true); // : $request->getPost() $sCode = $aPostDatas['code']; $sIdClient = $aPostDatas['clientId']; $sRedirectUri = $aPostDatas['redirectUri']; 3 Récupérer les infos utilisateurs via l’api facebook 23 Chemin de connexion > Satellizer > Backend > Echanges 24. • FacebookController 2 Demander un token à Fb avec la clé secrète $client = new GuzzleHttpClient(); $params = [ 'code' => $sCode, 'client_id' => $sIdClient, 'redirect_uri' => $sRedirectUri, 'client_secret' => $this->aFacebookConfig['app_secret'] ]; // Step 1. Exchange authorization code for access token. $accessTokenResponse = $client->request('GET', 'https://graph.facebook.com/v2.5/oauth/access_token', [ 'query' => $params ]); $aAccessToken = json_decode($accessTokenResponse->getBody(), true); $sAccessToken = $aAccessToken['access_token']; 24 Chemin de connexion > Satellizer > Backend > Echanges Backend : Vérifier et générer 25. • FacebookController 3 Récupérer les infos utilisateurs via l’api facebook try { // Returns a `FacebookFacebookResponse` object $response = $this->oFacebook->get('/me?fields=id,name,email', $sAccessToken); } catch (FacebookExceptionsFacebookResponseException $e) { echo 'Graph returned an error: '.$e->getMessage(); exit; } catch (FacebookExceptionsFacebookSDKException $e) { echo 'Facebook SDK returned an error: '.$e->getMessage(); exit; } $user = $response->getGraphUser(); $aDatas = array( 'iId' => $user['id'], 'sName' => $user['name'], 'sEmail' => $user['email'] ); 25 Backend : Vérifier et générer Chemin de connexion > Satellizer > Backend > Echanges 26. 4 • FacebookController Traiter les infos utilisateurs //Search if this user exists in our database $oUser = $this->oUserService->findUserFacebook($aDatas); if($oUser === false){ $oUser = $this->oUserService->saveUserFacebook($aDatas); } 26 Backend : Vérifier et générer Chemin de connexion > Satellizer > Backend > Echanges 27. • FacebookController //Return valid token if($oUser === false){ $aRes['success'] = false; }else{ $aRes = $this->oUserService->generateTockenUser($oUser); $aRes['success'] = true; } 5 Générer un JWT 6 Retourner le JWT ainsi que des infos en clair (nom utilisateur…) return new JsonModel($aRes); 27 Backend : Vérifier et générer Chemin de connexion > Satellizer > Backend > Echanges 28. Echanges 28 Chemin de connexion > Satellizer > Backend > Echanges 29. A chaque requête, le JWT ! angular.module('userModule').controller('SocialController', ['$scope', '$auth', '$localStorage', '$location', function ($scope, $auth, $localStorage, $location) { $scope.socialLoading = false; $scope.authenticate = function (provider) { $scope.socialLoading = true; $auth.authenticate(provider) .then(function (response) { //console.log('Response : ',response); $localStorage.user = response.data.oUser; $scope.socialLoading = false; $location.path('/profil'); }) .catch(function (response) { // Something went wrong. }); }; }]); • SocialController 29 Chemin de connexion > Satellizer > Backend > Echanges 30. angular.module('userModule').factory('TokenInterceptor', function ($q, $localStorage, $location ) { //AuthenticationService) { return { request: function (config) { config.headers = config.headers || {}; if ($localStorage.satellizer_token) { config.headers.Authorization = 'Bearer ' + $localStorage.satellizer_token; //config.headers['x-session-token']= 'Bearer '+$localStorage.satellizer_token; }else{ config.headers['x-session-token']= ''; } return config; }, •TokenInterceptorService 30 Chemin de connexion > Satellizer > Backend > Echanges A chaque requête, le JWT ! 31. angular.module('defaultApp').config(function ($httpProvider) { $httpProvider.interceptors.push('TokenInterceptor'); }); •Config.js 31 A chaque requête, le JWT ! Chemin de connexion > Satellizer > Backend > Echanges 32. $sRole = 'guest'; $oJwt = null; $oContainer = new Container('oUser'); $oAuthorization = $oMvcEvent->getTarget()->getRequest()->getHeaders('Authorization'); //$oAuthorization = $oMvcEvent->getTarget()->getRequest()->getHeaders(x-session-token); if( $oAuthorization !== false && $oAuthorization != ''){ $sJWT = $oAuthorization->getFieldValue(); $sJWT = substr($sJWT,7);//7 = size of 'BEARER ' $oJWTService = $oMvcEvent->getApplication()->getServiceManager()->get('JWTService'); try{ $oJwt = $oJWTService->decode($sJWT); $sRole = $oJwt->context->user->sRole; $oContainer->oUser = $oJwt->context->user; }catch(ExpiredException $oException){ }catch(Exception $oException){ } •Module.php 32 Chemin de connexion > Satellizer > Backend > Echanges A chaque requête, le JWT ! 33. Aller plus loin •Regarder les fichiers de démo sur le github de satellizer (très bien faits et doc très claire !) •Penser à relier les comptes en base avec les ids des réseaux sociaux •Détruire le JWT dans le localStorage à la deconnexion •Ne pas oublier que l’on peut utiliser plusieurs backend donc plusieurs applis avec le JWT ! 33 34. Conclusion • Les réseaux sociaux ont une façon similaire de proposer l’authentification •Très simple de centraliser les connexion avec Satellizer 34 35. Sources et webographie •Site dédié aux JWT : documentations, librairies, debuger... https://jwt.io/introduction/ •RFC 7519 : Json Web Token https://tools.ietf.org/html/rfc7519 •Tuto angular et JWT http://www.ekino.com/introduction-aux-json-web-tokens/ • Nombre de sites internet dans le monde http://hitek.fr/bonasavoir/combien-site-internet-dans-monde_588 • Pourcentage d’actifs sur réseaux sociaux http://www.blogdumoderateur.com/chiffres-reseaux-sociaux/ •Scratchpad angular contenant l’authentification par réseau social https://github.com/Lymke/angular-scratchpad 35 36. Merci pour votre écoute Si vous avez des questions, je peux peut-être y répondre :) 36