Intégration de la connexion Facebook dans l'application AngularJS avec Satellizer

Publié: 2022-03-11

Avec l'arrivée de frameworks frontaux riches en fonctionnalités tels que AngularJS, de plus en plus de logique est mise en œuvre sur le front-end, comme la manipulation/validation des données, l'authentification, etc. Satellizer, un module d'authentification basé sur des jetons facile à utiliser pour AngularJS, simplifie le processus de mise en œuvre du mécanisme d'authentification dans AngularJS, La bibliothèque est livrée avec un support intégré pour Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch et comptes Microsoft (Windows Live).

Dans cet article, nous allons créer une application Web très simple similaire à celle-ci qui vous permet de vous connecter et de voir les informations de l'utilisateur actuel.

Authentification vs autorisation

Ce sont 2 mots effrayants que vous rencontrez souvent une fois que votre application commence à intégrer un système utilisateur. Selon Wikipédia :

L' authentification est l'acte de confirmer la véracité d'un attribut d'une seule donnée (une donnée) déclarée vraie par une entité.

L'autorisation est la fonction de spécifier les droits d'accès aux ressources liées à la sécurité de l'information et à la sécurité informatique en général et au contrôle d'accès en particulier.

En termes simples, prenons l'exemple d'un site Web de blog avec des personnes travaillant dessus. Les blogueurs écrivent des articles et le responsable valide le contenu. Chaque personne peut s'authentifier (se connecter) dans le système mais ses droits (autorisation) sont différents, de sorte que le blogueur ne peut pas valider le contenu alors que le gestionnaire le peut.

Pourquoi Satellizer

Vous pouvez créer votre propre système d'authentification dans AngularJS en suivant quelques tutoriels comme celui-ci très détaillé : JSON Web Token Tutorial : An Example in Laravel and AngularJS. Je suggère de lire cet article car il explique très bien JWT (JSON Web Token) et montre un moyen simple d'implémenter l'authentification dans AngularJS en utilisant directement le stockage local et les intercepteurs HTTP.

Alors pourquoi Satellizer ? La principale raison est qu'il prend en charge une poignée de connexions de réseaux sociaux tels que Facebook, Twitter, etc. en utilisant des connexions sociales. Comme intégrer le SDK de chaque réseau social et suivre leurs documentations est assez répétitif, il serait bien de supporter ces logins sociaux avec un minimum d'effort.

De plus Satellizer est un projet actif sur Github. Active est la clé ici car ces SDK changent assez fréquemment et vous ne voulez pas lire leur documentation de temps en temps (toute personne travaillant avec Facebook SDK sait à quel point c'est ennuyeux)

Application AngularJS avec connexion Facebook

C'est là que les choses commencent à devenir intéressantes.

Nous allons créer une application Web dotée d'un mécanisme de connexion/enregistrement régulier (c'est-à-dire en utilisant un nom d'utilisateur, un mot de passe) et prenant également en charge les connexions sociales. Cette webapp est très simple puisqu'elle ne comporte que 3 pages :

  • Page d'accueil : tout le monde peut voir
  • Page de connexion : pour saisir le nom d'utilisateur/mot de passe
  • Page secrète : que seuls les utilisateurs connectés peuvent voir

Pour le backend, nous utiliserons Python et Flask. Python et le framework Flask sont assez expressifs donc j'espère que le portage du code vers d'autres langages/frameworks ne sera pas très difficile. Nous utiliserons bien sûr AngularJS pour le front-end. Et pour les connexions sociales, nous nous intégrerons uniquement à Facebook car il s'agit du réseau social le plus populaire à l'heure actuelle.

Commençons!

Étape 1 : projet d'amorçage

Voici comment nous allons structurer notre code :

 - app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html

Tout le code back-end est dans app.py . Le code frontal est placé dans le dossier static/. Par défaut, Flask servira automatiquement le contenu du dossier static/. Toutes les vues partielles sont dans static/partials/ et gérées par le module ui.router.

Pour commencer à coder le back-end, nous aurons besoin de Python 2.7.* et installerons les bibliothèques requises à l'aide de pip. Vous pouvez bien sûr utiliser virtualenv pour isoler un environnement Python. Vous trouverez ci-dessous la liste des modules Python requis à mettre dans requirements.txt :

 Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0

Pour installer toutes ces dépendances :

 pip install -r requirements.txt

Dans app.py, nous avons un code initial pour amorcer Flask (les instructions d'importation sont omises par souci de brièveté) :

 app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)

Ensuite, nous initions bower et installons AngularJS et ui.router :

 bower init # here you will need to answer some question. when in doubt, just hit enter :) bower install angular angular-ui-router --save # install and save these dependencies into bower.json

Une fois ces bibliothèques installées, nous devons inclure AngularJS et ui-router dans index.html et créer des routages pour 3 pages : home, login et secret.

 <body ng-app="DemoApp"> <a ui-sref="home">Home</a> <a ui-sref="login">Login</a> <a ui-sref="secret">Secret</a> <div ui-view></div> <script src="bower_components/angular/angular.min.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="main.js"></script> </body>

Vous trouverez ci-dessous le code dont nous avons besoin dans main.js pour configurer le routage :

 var app = angular.module('DemoApp', ['ui.router']); app.config(function ($stateProvider, $urlRouterProvider) { $stateProvider .state('home', { url: '/home', templateUrl: 'partials/home.tpl.html' }) .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', }) .state('login', { url: '/login', templateUrl: 'partials/login.tpl.html' }); $urlRouterProvider.otherwise('/home'); });

À ce stade, si vous exécutez le serveur python app.py , vous devriez avoir cette interface de base à http://localhost:5000

Interface de connexion de base

Les liens Accueil, Connexion et Secret devraient fonctionner à ce stade et afficher le contenu des modèles correspondants.

Félicitations, vous venez de terminer la mise en place du squelette ! Si vous rencontrez une erreur, veuillez consulter le code sur GitHub

Étape #2 : Connectez-vous et inscrivez-vous

À la fin de cette étape, vous aurez une application Web sur laquelle vous pourrez vous inscrire/vous connecter à l'aide d'un e-mail et d'un mot de passe.

La première étape consiste à configurer le backend. Nous avons besoin d'un modèle d'utilisateur et d'un moyen de générer le jeton JWT pour un utilisateur donné. Le modèle utilisateur présenté ci-dessous est vraiment simplifié et n'effectue même aucune vérification de base, par exemple si le champ e- mail contient "@", ou si le champ mot de passe contient au moins 6 caractères, etc.

 class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(100), nullable=False) password = db.Column(db.String(100)) def token(self): payload = { 'sub': self.id, 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(days=14) } token = jwt.encode(payload, app.config['TOKEN_SECRET']) return token.decode('unicode_escape')

Nous utilisons le module jwt en python pour générer la partie payload en JWT. Les parties iat et exp correspondent à l'horodatage de création et d'expiration du jeton. Dans ce code, le jeton expirera dans 2 semaines.

Une fois le modèle User créé, nous pouvons ajouter les points de terminaison "login" et "register". Le code pour les deux est assez similaire, donc ici je vais juste montrer la partie "registre". Veuillez noter que par défaut, Satellizer appellera les points de terminaison /auth/login et /auth/signup pour le "login" et le "register" respectivement.

 @app.route('/auth/signup', methods=['POST']) def signup(): data = request.json email = data["email"] password = data["password"] user = User(email=email, password=password) db.session.add(user) db.session.commit() return jsonify(token=user.token())

Vérifions d'abord le point de terminaison en utilisant curl :

 curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'

Le résultat devrait ressembler à ceci :

 { "token": "very long string…." }

Maintenant que la partie back-end est prête, attaquons-nous au front-end ! Tout d'abord, nous devons installer satellizer et l'ajouter en tant que dépendance dans main.js :

 bower install satellizer --save

Ajoutez le satelliseur en tant que dépendance :

 var app = angular.module('DemoApp', ['ui.router', 'satellizer']);

La connexion et l'inscription dans satellizer sont en fait assez simples par rapport à toute la configuration jusqu'à présent :

 $scope.signUp = function () { $auth .signup({email: $scope.email, password: $scope.password}) .then(function (response) { // set the token received from server $auth.setToken(response); // go to secret page $state.go('secret'); }) .catch(function (response) { console.log("error response", response); }) };

Si vous rencontrez des difficultés pour configurer le code, vous pouvez consulter le code sur GitHub.

Étape #3 : Mais la vue secrète n'est pas vraiment secrète, car tout le monde peut la voir !

Oui c'est correct! Jusqu'à présent, n'importe qui pouvait accéder à la page secrète sans se connecter.

Il est temps d'ajouter un intercepteur dans AngularJS pour s'assurer que si quelqu'un va sur la page secrète et si cet utilisateur n'est pas connecté, il sera redirigé vers la page de connexion.

Tout d'abord, nous devons ajouter un drapeau requiredLogin pour distinguer la page secrète des autres.

 .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })

La partie "data" sera utilisée dans l'événement $stateChangeStart qui est déclenché à chaque changement de routage :

 app.run(function ($rootScope, $state, $auth) { $rootScope.$on('$stateChangeStart', function (event, toState) { var requiredLogin = false; // check if this state need login if (toState.data && toState.data.requiredLogin) requiredLogin = true; // if yes and if this user is not logged in, redirect him to login page if (requiredLogin && !$auth.isAuthenticated()) { event.preventDefault(); $state.go('login'); } }); });

Désormais, l'utilisateur ne peut pas accéder directement à la page secrète sans se connecter. Hourra !

Comme d'habitude, le code de cette étape se trouve ici.

Étape #4 : Il est temps d'obtenir quelque chose de vraiment secret !

En ce moment, il n'y a rien de vraiment secret dans la page secrète. Mettons-y quelque chose de personnel.

Cette étape commence par créer un point de terminaison dans le back-end qui n'est accessible qu'à un utilisateur authentifié, tel qu'un jeton valide. Le point de terminaison /user ci-dessous renvoie le user_id et l'email de l'utilisateur correspondant au jeton.

 @app.route('/user') def user_info(): # the token is put in the Authorization header if not request.headers.get('Authorization'): return jsonify(error='Authorization header missing'), 401 # this header looks like this: “Authorization: Bearer {token}” token = request.headers.get('Authorization').split()[1] try: payload = jwt.decode(token, app.config['TOKEN_SECRET']) except DecodeError: return jsonify(error='Invalid token'), 401 except ExpiredSignature: return jsonify(error='Expired token'), 401 else: user_id = payload['sub'] user = User.query.filter_by(id=user_id).first() if user is None: return jsonify(error='Should not happen ...'), 500 return jsonify(id=user.id, email=user.email), 200 return jsonify(error="never reach here..."), 500

Encore une fois, nous utilisons le module jwt pour décoder le jeton JWT inclus dans l'en-tête 'Authorization' et pour gérer le cas où le jeton est expiré ou non valide.

Testons ce point de terminaison en utilisant curl. Tout d'abord, nous devons obtenir un jeton valide :

 curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'

Puis avec ce jeton :

 curl localhost:5000/user -H "Authorization: Bearer {put the token here}"

Ce qui donne ce résultat :

 { "email": "[email protected]", "id": 1 }

Nous devons maintenant inclure ce point de terminaison dans le contrôleur secret. C'est assez simple car nous avons juste besoin d'appeler le point de terminaison en utilisant le module $http normal. Le jeton est automatiquement inséré dans l'en-tête par Satellizer, nous n'avons donc pas besoin de nous soucier de tous les détails de l'enregistrement du jeton, puis de le placer dans le bon en-tête.

 getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }

Enfin, nous avons quelque chose de vraiment personnel dans la page secrète !

La page secrète, montrant un email et un identifiant d'utilisateur.

Le code de cette étape est sur GitHub.

Étape #5 : Connexion Facebook avec Satellizer

Une bonne chose à propos de Satellizer, comme mentionné au début, c'est qu'il facilite beaucoup l'intégration de la connexion sociale. A la fin de cette étape, les utilisateurs peuvent se connecter en utilisant leur compte Facebook !

Authentification Facebook OAuth.

La première chose à faire est de créer une application sur la page des développeurs Facebook afin d'avoir un application_id et un code secret. Veuillez suivre developers.facebook.com/docs/apps/register pour créer un compte de développeur Facebook si vous n'en avez pas déjà un et créer une application de site Web. Après cela, vous aurez l'ID de l'application et le secret de l'application comme dans la capture d'écran ci-dessous.

Obtenir le secret de l'application.

Une fois que l'utilisateur a choisi de se connecter à Facebook, Satellizer enverra un code d'autorisation au point de terminaison /auth/facebook . Avec ce code d'autorisation, le back-end peut récupérer un jeton d'accès à partir du point de terminaison Facebook /oauth qui permet à l'appel à l'API Facebook Graph d'obtenir des informations sur l'utilisateur telles que l'emplacement, les user_friends, l'e-mail de l'utilisateur, etc.

Nous devons également savoir si un compte utilisateur est créé avec Facebook ou via une inscription régulière. Pour ce faire, nous ajoutons facebook_id à notre modèle User.

 facebook_id = db.Column(db.String(100))

Le secret facebook est configuré via les variables d'environnement FACEBOOK_SECRET que nous ajoutons à app.config .

 app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')

Donc, pour lancer le app.py , vous devez définir cette variable env :

 FACEBOOK_SECRET={your secret} python app.py

Voici la méthode qui gère les connexions Facebook. Par défaut, Satellizer appellera le point de terminaison /auth/facebook .

 @app.route('/auth/facebook', methods=['POST']) def auth_facebook(): access_token_url = 'https://graph.facebook.com/v2.3/oauth/access_token' graph_api_url = 'https://graph.facebook.com/v2.5/me?fields=id,email' params = { 'client_id': request.json['clientId'], 'redirect_uri': request.json['redirectUri'], 'client_secret': app.config['FACEBOOK_SECRET'], 'code': request.json['code'] } # Exchange authorization code for access token. r = requests.get(access_token_url, params=params) # use json.loads instead of urlparse.parse_qsl access_token = json.loads(r.text) # Step 2. Retrieve information about the current user. r = requests.get(graph_api_url, params=access_token) profile = json.loads(r.text) # Step 3. Create a new account or return an existing one. user = User.query.filter_by(facebook_id=profile['id']).first() if user: return jsonify(token=user.token()) u = User(facebook_id=profile['id'], email=profile['email']) db.session.add(u) db.session.commit() return jsonify(token=u.token())

Pour envoyer une demande au serveur Facebook, nous utilisons le module pratique demandes. Maintenant, la partie difficile du back-end est terminée. Sur le front-end, ajouter une connexion Facebook est assez simple. Tout d'abord, nous devons indiquer à Satellizer notre facebook_id en ajoutant ce code dans la fonction app.config :

 $authProvider.facebook({ clientId: {your facebook app id}, // by default, the redirect URI is http://localhost:5000 redirectUri: 'http://localhost:5000/static/index.html' });

Pour vous connecter en utilisant Facebook, nous pouvons simplement appeler :

 $auth.authenticate(“facebook”)

Comme d'habitude, vous pouvez vérifier le code sur GitHub

À l'heure actuelle, la webapp est complète en termes de fonctionnalités. L'utilisateur peut se connecter/s'inscrire en utilisant un e-mail et un mot de passe habituels ou en utilisant Facebook. Une fois connecté, l'utilisateur peut voir sa page secrète.

Faire une jolie interface

L'interface n'est pas très jolie à ce stade, alors ajoutons un peu de Bootstrap pour la mise en page et le module grille-pain angulaire pour bien gérer un message d'erreur, comme lorsque la connexion échoue.

Le code de cette partie d'embellissement peut être trouvé ici.

Conclusion

Cet article montre une intégration pas à pas de Satellizer dans une (simple) webapp AngularJS. Avec Satellizer, nous pouvons facilement ajouter d'autres connexions sociales telles que Twitter, Linkedin, etc. Le code sur le front-end est à peu près le même que dans l'article. Cependant, le back-end varie car les SDK de réseaux sociaux ont différents points de terminaison avec différents protocoles. Vous pouvez consulter https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py qui contient des exemples pour Facebook, Github, Google, Linkedin, Twiter et Bitbucket. En cas de doute, vous devriez jeter un œil à la documentation sur https://github.com/sahat/satellizer.

Connexes : Connexion en un clic avec Blockchain : un didacticiel MetaMask