Authentification Firebase et Angular avec Auth0: Partie 1 –
   
Cet article a été publié à l'origine sur le blog Auth0.com et est republié ici avec sa permission.
Dans cette série de didacticiels en deux parties, nous apprendrons comment créer une application qui sécurise une extrémité arrière de nœud et une extrémité avant angulaire avec authentification Auth0 . Notre serveur et notre application authentifient également une base de données Cloud Firestore Firebase avec des jetons personnalisés afin que les utilisateurs puissent laisser des commentaires en temps réel de manière sécurisée après s'être authentifiés avec Auth0.
Le code d'application Angular peut être trouvé dans le repo GitHub angulaire-firebase et l'API Node dans le repo firebase-auth0-nodeserver .
 Authentification de Firebase et Angular avec Auth0: Part 1 [19659006] La première partie de notre tutoriel couvrira:-  Firebase et Auth0 
-  Ce que nous allons construire 
-  Angular CLI 
-  Client Auth0 et API 
-  Projet Firebase avec compte de service 
-  API nœud 
-  Configuration de l'application angulaire 
-  Architecture de l'application angulaire 
-  Implémentation des modules partagés 
-  Implémentation des modules de routage et chargement par chargement 
-  Chargement et erreur des composants 
-  Logique d'authentification 
-  Logique du noyau 
-  Prochaines étapes [19659021] Firebase et Auth0
Fireba se est une plateforme de développement d'applications mobiles et Web. Firebase a été acquise par Google en 2014 et continue d'être développée sous l'égide de Google. Firebase fournit des bases de données NoSQL ( RTDB, ou Realtime Database et Cloud Firestore, en version bêta au moment de la rédaction ) hébergées dans le cloud et connectées via des sockets Web pour fournir des fonctionnalités en temps réel aux applications.
Auth0 est une plate-forme basée sur le cloud qui fournit l'authentification et l'autorisation en tant que service. En tant que fournisseur d'authentification, Auth0 permet aux développeurs d'implémenter et de personnaliser facilement la sécurité de connexion et d'autorisation pour leurs applications.
Choisir Auth0 + Firebase Authentication
Si vous connaissez déjà les offres de Firebase, demandez-vous: implémenter Auth0 avec des jetons personnalisés dans Firebase au lieu de coller avec l'authentification intégrée de Firebase par lui-même?
Tout d'abord, il y a une distinction importante à faire ici. L'utilisation de Auth0 pour sécuriser Firebase ne signifie pas que vous n'êtes pas en utilisant Firebase auth. Firebase a une approche d'authentification personnalisée qui permet aux développeurs d'intégrer leur solution d'identité préférée à Firebase auth. Cette approche permet aux développeurs d'implémenter l'authentification Firebase pour qu'elle fonctionne de manière transparente avec les systèmes propriétaires ou d'autres fournisseurs d'authentification.
Il existe de nombreuses raisons potentielles pour lesquelles nous souhaitons intégrer Auth0 à l'authentification Firebase. Alternativement, il existe des scénarios où l'utilisation de base Firebase auth par elle-même pourrait suffire. Explorons.
Vous pouvez utiliser l'authentification intégrée de Firebase par elle-même si vous:
- Vous ne voulez authentifier que Firebase RTDB ou Firestore et n'avez pas besoin d'authentifier les backend supplémentaires
- une petite poignée d'options de connexion et pas besoin de fournisseurs d'identité d'entreprise, intégration avec vos propres bases de données de stockage, etc.
- Pas besoin d'une gestion étendue des utilisateurs, enrichissement de profil, etc. API
- Pas besoin de personnaliser les flux d'authentification
- Vous n'avez pas besoin d'adhérer aux règles de conformité concernant le stockage des données utilisateur.
Vous devriez considérer Auth0 avec un jeton Firebase personnalisé si vous:
- avez déjà implémenté Auth0 et souhaitez ajouter des fonctionnalités en temps réel à votre application
- Besoin d'utiliser facilement des jetons émis pour sécuriser un backend qui n'est pas fourni par Firebas
- Nécessité d'intégrer les fournisseurs d'identité sociale au-delà de Google, Facebook, Twitter et GitHub
- Besoin d'intégrer les fournisseurs d'identité d'entreprise tels que Active Directory, LDAP, ADFS , SAMLP, etc.
- Besoin d'un flux d'authentification personnalisé
- Besoin d'une gestion des utilisateurs robuste avec des API et un tableau de bord convivial
- pour pouvoir dynamiquement enrichir les profils d'utilisateurs
- Vous voulez des fonctionnalités comme personnalisable sans-mot de passe authentification multifactorielle violation de mot de passe détection d'anomalie etc.
- Doit adhérer à règlements de conformité tels que HIPAA, GDPR, SOC2, etc
Essentiellement, les fournisseurs d'authentification de base de Firebase devraient suffire si vous avez un application simple avec des besoins d'authentification bare-bones et sont seulement en utilisant des bases de données Firebase. Cependant, si vous avez besoin de plus que cela, Firebase offre un excellent moyen d'utiliser leurs services avec autres solutions d'authentification . C'est un scénario beaucoup plus réaliste auquel de nombreux développeurs seront confrontés, nous allons donc l'explorer en détail ici.
Ce que nous allons construire
Nous allons construire une API Node.js sécurisée avec Auth0 Mets des jetons Firebase personnalisés et renvoie également des données sur dix races de chiens différentes.
Nous allons également construire une application frontale Angular appelée "Dogs populaires" qui affiche des informations sur les dix chiens les plus populaires en 2016, classés par popularité publique par l'American Kennel Club (AKC). Notre application sera sécurisée par Auth0, appelez l'API Node pour récupérer les données du chien et appelez l'API pour acquérir des jetons Firebase afin d'autoriser les utilisateurs à ajouter et supprimer des commentaires en temps réel avec Cloud Firestore. L'application utilisera des modules partagés et implémentera un chargement paresseux.
 
Pour implémenter l'application, vous aurez besoin des éléments suivants:
- CLI angulaire
- ] Un compte Auth0 gratuit avec un client et une API configurée
- Un projet Firebase gratuit avec un compte de service
Commençons!
Angular CLI
Assurez-vous d'avoir Node.js avec NPM installé sur votre machine locale. Exécutez la commande suivante pour installer l'interface CLI Angular globalement:
 $ npm install -g @ angular / cli @ latest
Nous allons générer notre application Angular et presque toute son architecture en utilisant la CLI.
Auth0 Client et API
Vous aurez besoin d'un compte Auth0 pour gérer l'authentification. Vous pouvez créer un compte gratuit ici .
 
Ensuite, configurez une application client et une API Auth0 afin que Auth0 puisse s'interfacer avec l'application Angular et API de noeud
Configurer un client Auth0
- Accédez à votre tableau de bord Auth0 et cliquez sur le bouton Créer un nouveau client .
-  Nommez votre nouvelle application (quelque chose comme Angular Firebase) et sélectionnez Applications Web page unique .
-  Dans les  paramètres  pour votre nouvelle application client Auth0, ajoutez http: // localhost : 4200 / callbackaux URL de rappel autorisées .
- Activer le bouton de basculement pour Utilisez Auth0 au lieu de l'IdP pour activer l'authentification unique . de la section Paramètres cliquez sur "Afficher les paramètres avancés". Choisissez l'onglet OAuth et vérifiez que l'algorithme de signature JsonWebToken est défini sur "RS256".
- Si vous le souhaitez, vous pouvez établir des connexions sociales . Vous pouvez ensuite les activer pour votre application dans les options Client sous l'onglet Connexions . L'exemple montré dans la capture d'écran ci-dessus utilise le nom d'utilisateur / mot de passe, Facebook, Google et Twitter.
Note: Pour la production, assurez-vous de créer vos propres clés sociales et ne laissez pas les connexions sociales utilisées.
Configuration d'une API Auth0
-  Accédez aux API   dans votre tableau de bord Auth0 et cliquez sur le bouton "Créer une API". Entrez un nom pour l'API, par exemple API Firebase Dogs. Définissez l'identificateur sur l'URL de votre point de terminaison API. Dans ce tutoriel, notre identifiant d'API esthttp: // localhost: 1337 /. L'algorithme de signature doit être "RS256".
-  Vous pouvez consulter l'exemple Node.js sous l'onglet  Quick Start  dans les paramètres de votre nouvelle API. Dans les prochaines étapes, nous allons implémenter notre API Node de cette manière en utilisant  Express  express-jwt et  jwks-rsa . Nous sommes maintenant prêts à implémenter l'authentification Auth0 à la fois sur notre client angulaire et notre API back-end de nœudProjet Firebase avec compte de serviceEnsuite, vous aurez besoin d'un projet gratuit Firebase . Projet Firebase - Accédez à la Firebase Console et connectez-vous avec votre compte Google
- Cliquez sur Ajouter un projet .
-  Dans la boîte de dialogue qui s'affiche, donnez votre projeter un nom (tel que Angular Firebase Auth0). Un identifiant de projet sera généré en fonction du nom que vous avez choisi. Vous pouvez ensuite sélectionner votre pays / région.
- Cliquez sur le bouton Créer un projet
 Générer une clé SDK administrateurPour créer des jetons Firebase personnalisés vous Vous devez accéder au SDK Firebase Admin . Pour obtenir l'accès, vous devez créer un compte de service dans votre nouveau projet Firebase. Cliquez sur l'icône en forme de roue dentée à côté de la vue d'ensemble du projet dans la barre latérale de la console Firebase et sélectionnez Paramètres du projet . :  Dans la vue des paramètres, cliquez sur l'onglet Comptes de service . L'interface utilisateur Firebase Admin SDK apparaît, affichant un extrait de code de configuration. Node.js est sélectionné par défaut. C'est la technologie que nous voulons, et nous l'implémenterons dans notre API Node. Cliquez sur le bouton Générer une nouvelle clé privée Une boîte de dialogue s'affiche pour vous avertir de conserver votre clé privée confidentiellement. Nous veillerons à ne jamais vérifier cette clé dans un dépôt public. Cliquez sur le bouton Generate Key pour télécharger la clé sous la forme d'un fichier .json. Nous allons bientôt ajouter ce fichier à notre API Node.API NodeL'API Node.js terminée pour ce tutoriel se trouve sur le firebase-auth0-nodeserver GitHub repo . Apprenons à créer cette API. Structure du fichier de l'API de noeudNous allons configurer la structure de fichier suivante: firebase-auth0-nodeserver / | --firebase / | -. gitignore | -.json | -. gitignore | --config.js | --dogs.json | --package.json | --routes.js | --server.js Vous pouvez générer les dossiers et les fichiers nécessaires avec la ligne de commande comme suit: $ mkdir firebase-auth0-nodeserver $ cd firebase-auth0-nodeserver $ mkdir firebase $ touch firebase / .gitignore $ touch .gitignore $ touch config.js $ touch dogs.json $ touch package.json $ touch routes.js $ touch server.jsClé SDK Admin Firebase et Ignorer GitMaintenant, déplacez le fichier de clé Firebase Admin SDK .jsonque vous avez téléchargé précédemment dans le dossierfirebase. Nous veillerons à ce que le dossier soit archivé, mais son contenu ne sera jamais envoyé à un repo en utilisant lefirebase / .gitignorecomme ceci:# firebase /. gitignore * * / ! .gitignoreCette configuration .gitignoregarantit que Git ignorera tous les fichiers et dossiers du répertoirefirebasesauf pour le fichier.gitignorelui-même. Cela nous permet de valider un dossier (essentiellement) vide. Notre clé SDK.jsonFirebase Admin peut vivre dans ce dossier et nous n'aurons pas à nous inquiéter du gitignoring par nom de fichier .Note: Ceci est particulièrement utile si le projet est arrêté sur plusieurs machines et que des clés différentes (avec des noms de fichiers différents) sont générées. Ajoutons maintenant le code du répertoire racine .gitignore:#. gitignore config.js node_modulesDogs JSON DataNous allons maintenant ajouter les données pour dix races de chiens. Par souci de concision, vous pouvez simplement copier et coller ces données dans votre fichier dogs.json.DépendancesAjoutons notre fichier package.jsoncomme ça:{ "name": "firebase-auth0-nodeserver", "version": "0.1.0", "description": "serveur Node.js qui s'authentifie avec un jeton d'accès Auth0 et renvoie un jeton d'authentification Firebase.", "référentiel": "https://github.com/auth0-blog/firebase-auth0-nodeserver", "main": "server.js", "scripts": { "démarrer": "serveur de noeud" }, "auteur": "Auth0", "licence": "MIT", "dépendances": {}, "devDependencies": {} }Nous installerons les dépendances avec la ligne de commande et les dernières versions seront sauvegardées automatiquement dans le fichier package.json:$ npm install --save body-parser cors express express-jwt jwks-rsa firebase-adminNous aurons besoin de body-parsercorsetexpresspour servir nos points de terminaison API. L'authentification s'appuiera surexpress-jwtetjwks-rsatandis que le jeton de jetons Firebase est implémenté avec le SDKfirebase-admin(auquel nous aurons accès) en utilisant la clé que nous avons générée.ConfigurationDans le fichier config.jsajoutez le code suivant et remplacez les valeurs de l'espace réservé par vos propres paramètres:// config.js module.exports = { AUTH0_DOMAIN: '', // par exemple, you.auth0.com AUTH0_API_AUDIENCE: ' ', // par exemple, http: // localhost: 1337 / FIREBASE_KEY: './firebase/ ', // par exemple, votre-projet-firebase-adminsdk-xxxxx-xxxxxxxxxx.json FIREBASE_DB: ' ' // par exemple, https://your-project.firebaseio.com } ServerAvec nos données, notre configuration et nos dépendances en place, nous pouvons maintenant implémenter notre serveur Node. Ouvrez le fichier server.jset ajoutez:// server.js // Modules const express = require ('express'); const bodyParser = require ('body-parser'); const cors = require ('cors'); // App const app = express (); app.use (bodyParser.json ()); app.use (bodyParser.urlencoded ({extended: false})); app.use (cors ()); // Définir le port port const = process.env.PORT || «1337»; app.set ('port', port); // Routes require ('./ routes') (app); // Serveur app.listen (port, () => console.log (`Serveur s'exécutant sur localhost: $ {port}`));Ceci lancera notre serveur Node avec Express à http: // localhost: 1337 /.Note: Notez qu'il s'agit de l'identifiant API que nous avons configuré dans Auth0. [19659121] API Routes Ensuite, ouvrez le fichier routes.js. C'est ici que nous définirons nos extrémités d'API, les sécuriserons et frapperons les jetons Firebase personnalisés. Ajoutez le code suivant:// routes.js // Dépendances const jwt = require ('express-jwt'); const jwks = require ('jwks-rsa'); const firebaseAdmin = require ('firebase-admin'); // Config const config = require ('./ config'); module.exports = function (app) { // middleware d'authentification Auth0 const jwtCheck = jwt ({ secret: jwks.expressJwtSecret ({ cache: vrai, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: `https: // $ {config.AUTH0_DOMAIN} / .well-known / jwks.json` }), audience: config.AUTH0_API_AUDIENCE, émetteur: `https: // $ {config.AUTH0_DOMAIN} /`, algorithme: 'RS256' }); // Initialise Firebase Admin avec un compte de service const serviceAccount = require (config.FIREBASE_KEY); firebaseAdmin.initializeApp ({ informations d'identification: firebaseAdmin.credential.cert (serviceAccount), databaseURL: config.FIREBASE_DB }); // Objet GET contenant un jeton personnalisé Firebase app.get ('/ auth / firebase', jwtCheck, (req, res) => { // Crée un UID à partir de l'utilisateur Auth0 authentifié const uid = req.user.sub; // Jeton de menthe utilisant Firebase Admin SDK firebaseAdmin.auth (). createCustomToken (uid) .then (customToken => // La réponse doit être un objet ou des erreurs Firebase res.json ({firebaseToken: customToken}) ) .catch (err => res.status (500) .send ({ message: 'Quelque chose s'est mal passé lors de l'acquisition d'un jeton Firebase.', erreur: err }) ) }); // Configurer les données JSON des chiens pour l'API const chiens = require ('./ dogs.json'); const getDogsBasic = () => { const chiensBasicArr = dogs.map (chien => { revenir { classement: dog.rank, race: dog.breed, image: dog.image } }); retour chiensBasicArr; } // GET chiens (public) app.get ('/ api / dogs', (req, res) => { res.send (getDogsBasic ()); }); // Obtenir les détails du chien par rang (privé) app.get ('/ api / chien /: rang', jwtCheck, (req, res) => { const rank = req.params.rank * 1; const thisDog = dogs.find (chien => dog.rank === rang); res.send (thisDog); }); }À un niveau élevé, notre fichier d'itinéraires effectue les opérations suivantes: -  Configure la vérification d'authentification pour s'assurer que seuls les utilisateurs connectés peuvent accéder aux routes avec middleware jwtCheck
- Initialise le SDK Admin Firebase avec la clé privée générée à partir du compte de service de projet Firebase
-  Fournit un point de terminaison sécurisé GETqui renvoie un jeton Firebase personnalisé
-  Fournit un point de terminaison public GET* qui renvoie une version courte des données de chiens
-  Fournit un point de terminaison sécurisé GET* qui renvoie les données détaillées d'un chien spécifique, demandées par rang.
 * Les points de terminaison utilisent des variations du même ensemble de données de base pour simuler un Vous pouvez lire les commentaires de code pour plus de détails. Servir l'APIVous pouvez servir l'API Node en exécutant: $ node serverL'API sera alors disponible à http: // localhost: 1337 . Note: Si vous essayez d'accéder à des routes sécurisées dans le navigateur, vous devriez recevoir un 401 Erreur non autorisée.C'est tout pour notre serveur! Laissez l'API s'exécuter pour qu'elle soit accessible à l'application Angular, que nous installerons ensuite Set Up Angular AppIl est maintenant temps de créer notre application Angular et de configurer des dépendances supplémentaires. [19659140] Créer une nouvelle application angulaire Vous devriez déjà avoir installé la CLI angulaire plus tôt. Nous pouvons maintenant utiliser la CLI pour générer notre projet et son architecture. Pour créer une nouvelle application, choisissez un dossier contenant, puis exécutez la commande suivante: $ ng new angular-firebase --routing --skip-testsL'indicateur - routinggénère une application avec un module de routage et- skip-testsgénère le composant racine sans fichier.spec.tsNote: Par souci de concision, nous n'allons pas couvrir les tests dans cet article. Si vous souhaitez en savoir plus sur les tests dans Angular, consultez la conclusion du didacticiel pour plus de ressources. Installer les dépendances frontalesMaintenant, installons nos dépendances frontales: $ cd angular-firebase $ npm installer --save auth0-js @ dernière firebase @ dernière angularfire2 @ dernièreNous aurons besoin de la bibliothèque auth0-jspour implémenter l'authentification Auth0 dans notre application Angular. Nous aurons aussi besoin de la bibliothèquefirebaseJS SDK etangularfire2Angular Firebase pour implémenter nos commentaires en temps réel avec Firebase.Ajouter Bootstrap CSSPour simplifier le style, nous allons ajouter le lien Bootstrap CSS CDN au de notre fichierindex.htmlcomme suit:... ...Les 10 meilleurs chiens ... ...Servir l'application AngularVous pouvez servir l'application Angular avec la commande suivante: $ ng serveL'application tournera dans le navigateur à http: // localhost: 4200 . Angular App ArchitectureNous allons utiliser l'interface CLI angulaire pour générer l'architecture complète de notre application à l'avant. De cette façon, nous pouvons nous assurer que nos modules fonctionnent correctement avant d'implémenter notre logique et nos templates. Notre application va utiliser une approche modulaire avec chargement paresseux . L'exemple d'application dans ce didacticiel est petit, mais nous souhaitons le construire de façon évolutive et réelle . Root ModuleLe module racine a déjà été créé lorsque l'application Angular a été créée. généré avec la commande ng new. Le module racine réside àsrc / app / app.module.ts. Tous les composants que nous générons dans notre application Angular sans le sous-répertoire d'un autre module spécifié seront automatiquement importés et déclarés dans notre module racine.Générons maintenant un composant avec la CLI: # create CallbackComponent: $ ng g callback de composant --is --it --flat --no-specCette commande est composée des éléments suivants: -  ng g composant: génère un fichier de composantcallbackavec:
-  - isinline styles
-  - ilmodèle en ligne
-  - platsans dossier contenant
-  - no-specno.specfichier de test
 Nous utiliserons le composant callback pour gérer la redirection après que l'utilisateur se connecte à notre application. C'est un composant très simple Note: gest un raccourci pourgénérer. Nous pourrions aussi utiliserccomme raccourci pour le composantng g c. Cependant, ce tutoriel n'utilisera pas de raccourcis pour le type de fichiers générés, dans un souci de clartéCore Module ArchitectureEnsuite, nous allons créer le CoreModuleet ses composants et services. C'est un module partagé . À partir de la racine de votre dossier de projet Angular, exécutez les commandes CLI suivantes. Assurez-vous d'exécuter la commandeng g coreen premier comme suit:# create Module de base: $ ng g noyau de module # créer un service d'API sans fichier .spec: $ ng g noyau de service / api --no-spec # crée HeaderComponent avec des styles inline, pas de fichier .spec, et exporte dans le module: $ ng g core / header du composant --is --no-spec --export = true # create LoadingComponent avec les styles inline, le template inline, pas de dossier, pas de fichier .spec, et l'export dans le module: $ ng g core / chargement --is --it --flat --no-spec --export = true # create ErrorComponent avec les styles en ligne, le modèle en ligne, aucun dossier, aucun fichier .spec et l'exportation en module: $ ng g core / erreur de composant --is --it --flat --no-spec --export = true # créer une interface de type chien: $ ng g noyau d'interface / chien # créer une interface de type DogDetail: $ ng g noyau d'interface / chien-détailLa création du module garantit que les composants créés dans le dossier de ce module seront importés et déclarés automatiquement dans ce module parent au lieu du module racine de l'application. Remarque: Si vous souhaitez utiliser les composants d'un module partagé Dans un autre module, vous devez exporter les composantset les déclarer. Nous pouvons le faire automatiquement avec la CLI en utilisant le drapeau--export = trueVoici l'architecture de base pour les services de base partagés, les composants et les modèles auxquels notre application aura besoin d'accéder Auth Module ArchitectureEnsuite, nous allons créer notre AuthModule. Exécutez les commandes CLI suivantes (encore une fois, assurez-vous de générer le module en premier):# create Module Auth: $ ng g module auth # crée AuthService sans fichier .spec: $ ng g service auth / auth --no-spec # crée un chemin d'accès Auth sans fichier .spec: $ ng g de garde auth / auth --no-specNotre module Authfournit le service et la protection d'itinéraire dont nous avons besoin pour gérer l'authentification, mais n'a aucun composant. C'est aussi un module partagéDogs Module ArchitectureNotre page d'accueil sera fournie par le DogsModule. Ce sera la liste des dix chiens les plus populaires en 2016, classés par l'AKC. Utilisez les commandes CLI suivantes pour générer la structure de ce module de page chargé par défaut:# create Dogs module: $ ng g module chiens # create DogsComponent avec des styles en ligne et pas de fichier .spec: $ ng g composants chiens / chiens --is --no-specDog Module ArchitectureNotre application contiendra également des pages détaillées pour chaque chien listé dans le composant Chiens afin que les utilisateurs puissent en apprendre plus sur chaque race. Utilisez les commandes CLI suivantes pour générer la structure du module DogModule DogModule:# paresseux: $ ng g module chien # create DogComponent avec des styles inline et aucun fichier .spec: $ ng g composant chien / chien --is --no-specEnfin, nous devons implémenter l'architecture nécessaire pour nos commentaires en temps réel Firebase. Utilisez les commandes CLI suivantes pour générer la structure pour le module CommentsModule:# create Comments: $ ng g commentaires du module # create classe de modèle de commentaire: $ ng g classe commentaires / commentaires # create CommentsComponent sans fichier .spec: $ ng g commentaires / commentaires sur les composants --no-spec --export = true # create CommentFormComponent avec des styles inline et aucun fichier .spec: $ ng g commentaires / commentaires / commentaire -formulaire --is --no-specConfiguration de l'environnementAjoutons nos informations de configuration pour Auth0 et Firebase à notre front angulaire. Ouvrez le fichier environment.tset ajoutez:// src / environments / environment.ts const FB_PROJECT_ID = ''; export const environnement = { production: faux, auth: { clientId: ' ', clientDomain: ' ', // par exemple, you.auth0.com public: ' ', // par exemple, http: // localhost: 1337 / redirection: 'http: // localhost: 4200 / callback', scope: 'email de profil openid' }, Firebase: { apiKey: ' ', authDomain: `$ {FB_PROJECT_ID} .firebaseapp.com`, databaseURL: `https: // $ {FB_PROJECT_ID} .firebaseio.com`, ID de projet: FB_PROJECT_ID, storageBucket: `$ {FB_PROJECT_ID} .appspot.com`, messagingSenderId: ' ' }, apiRoot: ' ' // par exemple, http: // localhost: 1337 / (DO inclut une barre oblique) } Remplacez les espaces réservés dans Vous pouvez trouver votre configuration Auth0 dans votre Auth0 Dashboard dans les paramètres du client et de l'API que vous avez créés Vous pouvez trouver votre configuration Firebase dans la vue d'ensemble du projet de console Firebase après avoir cliqué sur la grande icône Ajouter Firebase à votre application web comme indiqué ci-dessous:  Ajouter Chargement ImageLa dernière chose que nous allons faire avant de commencer à implémenter des fonctionnalités dans notre application Angular est d'ajouter une image de chargement. Créez le dossier suivant: src / assets / images.Puis enregistrez en chargeant l'image SVG dans ce dossier: Implémenter les modules partagésMettons en place nos modules. Nous allons importer les modules partagés ( CoreModuleetAuthModule) dans notre racineAppModule.Module de baseNous allons d'abord implémenter notre CoreModule. Ouvrez le fichiercore.module.tset mettez à jour le code suivant:// src / app / core / core.module.ts importer {NgModule, ModuleWithProviders} à partir de '@ angular / core'; importer {CommonModule} à partir de '@ angular / common'; importer {HttpClientModule} à partir de '@ angular / common / http'; importer {FormsModule} à partir de '@ angular / forms'; importer {RouterModule} à partir de '@ angular / router'; importer {Title} à partir de '@ angular / platform-browser'; importer {DatePipe} à partir de '@ angular / common'; importer {HeaderComponent} à partir de './header/header.component'; importer {ApiService} à partir de './api.service'; importer {LoadingComponent} à partir de './loading.component'; importer {ErrorComponent} à partir de './error.component'; @NgModule ({ importations: [ CommonModule, RouterModule, HttpClientModule, // AuthModule is a sibling and can use this without us exporting it FormsModule ], déclarations: [ HeaderComponent, LoadingComponent, ErrorComponent ], exportations: [ FormsModule, // Export FormsModule so CommentsModule can use it HeaderComponent, LoadingComponent, ErrorComponent ] }) classe d'exportation CoreModule { static forRoot (): ModuleWithProviders { revenir { ngModule: CoreModule, fournisseurs: [ Title, DatePipe, ApiService ] } } }Comme il s'agit d'un module partagé, nous allons importer les autres modules, services et composants auxquels nous aurons besoin tout au long de notre application. Note: ] CommonModuleest importé dans tous les modules qui sont et non le module racine.Dans notre tableau importsnous ajoutons tous les modules qui peuvent être requis par les services ou composants dans leCoreModuleou qui doivent être disponibles pour autres modules dans notre application. L'interface de ligne de commande doit avoir ajouté automatiquement tous les composants générés au tableaudéclarations. Le tableauexportsdoit contenir tous les modules ou composants que nous voulons mettre à la disposition des autres modules.Notez que nous avons importé ModuleWithProvidersde@ angular / coreÀ l'aide de ce module, nous pouvons créer une méthodeforRoot ()qui peut être appelée à l'importation dans la racineapp.module.tslorsqueCoreModuleest importé. De cette façon, nous pouvons nous assurer que tous les services que nous ajoutons à un tableauprovidersrenvoyé par la méthodeforRoot ()restent singletons dans notre application. De cette manière, nous pouvons éviter plusieurs instances involontaires si d'autres modules de notre application doivent également importer leCoreModule.Auth ModuleEnsuite, ajoutons du code à notre AuthModule auth.module.ts:// src / app / auth / auth.module.ts importer {NgModule, ModuleWithProviders} à partir de '@ angular / core'; importer {CommonModule} à partir de '@ angular / common'; importer {AuthService} à partir de './auth.service'; importer {AuthGuard} à partir de './auth.guard'; importer {AngularFireAuthModule} à partir de 'angularfire2 / auth'; @NgModule ({ importations: [ CommonModule, AngularFireAuthModule ] }) classe d'exportation AuthModule { static forRoot (): ModuleWithProviders { revenir { ngModule: AuthModule, fournisseurs: [ AuthService, AuthGuard ] } } }We’ll import ModuleWithProvidersto implement aforRoot()method like we did with ourCoreModule. Then we’ll import ourAuthServiceandAuthGuard. We also need to importAngularFireAuthModulefromangularfire2/authso we can secure our Firebase connections in ourAuthService. The service and guard should then be returned in theprovidersarray in theforRoot()method.Open the comments.module.tsfile to implement theCommentsModulelike so:// src/app/comments/comments.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreModule } from '../core/core.module'; import { environment } from './../../environments/environment'; import { AngularFireModule } from 'angularfire2'; import { AngularFirestoreModule } from 'angularfire2/firestore'; import { CommentsComponent } from './comments/comments.component'; import { CommentFormComponent } from './comments/comment-form/comment-form.component'; @NgModule({ imports: [ CommonModule, CoreModule, // Access FormsModule, Loading, and Error components AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule ], declarations: [ CommentsComponent, CommentFormComponent ], exports: [ CommentsComponent ] }) export class CommentsModule { }We’ll need to import the CoreModuleso we can utilize its exportedFormsModuleLoadingComponentandErrorComponent. We also need to access our configuration from theenvironment.tsfile. Comments use Firebase’s Cloud Firestore database, so let’s import theAngularFireModuleandAngularFirestoreModuleas well as our two components:CommentsComponentandCommentFormComponent.When we add AngularFireModuleto the @NgModule’simportsarray, we’ll call itsinitializeApp()method, passing in our Firebase configuration. Both of our components should already be in thedeclarationsarray, and theCommentsComponentshould already be added to theexportsarray so that other components from other modules can use it.Note: We don’t need to export CommentsFormComponentbecause it’s a child ofCommentsComponent.The CommentsModuledoes not provide any services, so there’s no need to implement aforRoot()method.App ModuleNow that our CoreModuleAuthModuleandCommentsModulehave been implemented, we need to import them in our root module, theAppModulelocated in theapp.module.tsfile:// src/app/app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { CoreModule } from './core/core.module'; import { AuthModule } from './auth/auth.module'; import { CommentsModule } from './comments/comments.module'; import { AppComponent } from './app.component'; import { CallbackComponent } from './callback.component'; @NgModule({ declarations: [ AppComponent, CallbackComponent ], imports: [ BrowserModule, AppRoutingModule, CoreModule.forRoot(), AuthModule.forRoot(), CommentsModule ], bootstrap: [AppComponent] }) export class AppModule { }The AppComponentandCallbackComponenthave already been added automatically by the CLI. When we add ourCoreModuleandAuthModuleto theimportsarray, we’ll call theforRoot()method to ensure no extra instances are created for their services. TheCommentsModuledoesn’t provide any services, so this is not a concern for that module.Implement Routing and Lazy Loaded ModulesWe have two modules that require routing: the DogsModulefor the main listing of dogs, and theDogModulewhich contains the component showing a dog breed’s detail page.App RoutingFirst let’s implement our app’s routing. Open the app-routing.module.tsfile and add this code:// src/app/app-routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { CallbackComponent } from './callback.component'; import { AuthGuard } from './auth/auth.guard'; const routes: Routes = [ { path: '', loadChildren: './dogs/dogs.module#DogsModule', pathMatch: 'full' }, { path: 'dog', loadChildren: './dog/dog.module#DogModule', canActivate: [ AuthGuard ] }, { path: 'callback', component: CallbackComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }We’ll import our CallbackComponentandAuthGuard. The remaining routes will be string references to modules rather than imported components using theloadChildrenproperty.We will set the default ''path to load route children from theDogsModuleand the'dog'path to load route children from theDogModule. The'dog'path should also be protected by theAuthGuardwhich we declare using thecanActivateproperty. This can hold an array of route guards should we require more than one. Finally, the'callback'route should simply point to theCallbackComponent.Dogs ModuleLet’s add some code to the dogs.module.tsfile:// src/app/dogs/dogs.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { CoreModule } from '../core/core.module'; import { CommentsModule } from '../comments/comments.module'; import { DogsComponent } from './dogs/dogs.component'; const DOGS_ROUTES: Routes = [ { path: '', component: DogsComponent } ]; @NgModule({ imports: [ CommonModule, CoreModule, RouterModule.forChild(DOGS_ROUTES), CommentsModule ], declarations: [ DogsComponent ] }) export class DogsModule { }We’ll import RoutesandRouterModulein addition to ourCoreModuleandCommentsModule(comments will appear on the main dogs listing page).This module has a child route, so we’ll create a constant that contains an array to hold our route object. The only child route we’ll need inherits the ''path fromapp-routing.module.tsso its path should also be''. It will load theDogsComponent. In ourimportsarray, we’ll pass ourDOGS_ROUTESconstant to theRouterModule‘sforChild()method.Dog ModuleThe DogModuleworks similarly to theDogsModuleabove. Opendog.module.tsand add the following:// src/app/dog/dog.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { CoreModule } from '../core/core.module'; import { DogComponent } from './dog/dog.component'; const DOG_ROUTES: Routes = [ { path: ':rank', component: DogComponent } ]; @NgModule({ imports: [ CommonModule, CoreModule, RouterModule.forChild(DOG_ROUTES) ], declarations: [ DogComponent ] }) export class DogModule { }One difference between this module and the DogsModuleis that ourDOG_ROUTEShas a path of:rank. This way, the route for any specific dog’s details is passed as a URL segment matching the dog’s rank in our list of top ten dog breeds, like so:http://localhost:4200/dog/3Another difference is that we will not import the CommentsModule. However, we could add comments to dog details in the future if we wished.Our app’s architecture and routing are now complete! The app should successfully compile and display in the browser, with lazy loading functioning properly to load shared code and the code for the specific route requested. We’re now ready to implement our application’s logic. Loading and Error ComponentsThe loading and error components are basic, core UI elements that can be used in many different places in our app. Let’s set them up now. Loading ComponentThe LoadingComponentshould simply show a loading image. (Recall that we already saved one when we set up the architecture of our app.) However, it should be capable of displaying the image large and centered, or small and inline.Open the loading.component.tsfile and add:// src/app/core/loading.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'app-loading', template: ``, styles: [` .inline { display: inline-block; } img { height: 80px; width: 80px; } .inline img { height: 24px; width: 24px; } `] }) export class LoadingComponent { @Input() inline: boolean; }Using the @Input()decoratorwe can pass information into the component from its parent, telling it whether we should display the component inline or not. We’ll use the NgClass directive ([ngClass]) in our template to conditionally add the appropriate styles for the display we want. Displaying this component in another template will look like this:Error ComponentNext let’s quickly implement our ErrorComponent. This component will display a simple error message if shown. Open theerror.component.tsfile and add:// src/app/core/error.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-error', template: `Error: There was an error retrieving data. ` }) export class ErrorComponent { }Authentication LogicNow let’s implement the code necessary to get our AuthModule‘s features working. We’ll need the authentication service in order to build out the header in theCoreModuleso it makes sense to start here. We’ve already installed the necessary dependencies (Auth0 and FirebaseAuth), so let’s begin.Authentication ServiceBefore we write any code, we’ll determine what the requirements are for this service. We need to: - Create a login()method that will allow users to authenticate using Auth0
- If user was prompted to log in by attempting to access a protected route, make sure they can be redirected to that route after successful authentication
- Get the user’s profile information and set up their session
- Establish a way for the app to know whether the user is logged in or not
- Request a Firebase custom token from the API with authorization from the Auth0 access token
- If successful in acquiring a Firebase token, sign into Firebase using the returned token and establish a way for the app to know whether the user is logged into Firebase or not
- Custom tokens minted by Firebase expire after an hour, so we should set up a way to automatically renew tokens that expire
- Create a logout()method to clear session and sign out of Firebase.
 Open the auth.service.tsfile that we generated earlier.For tutorial brevity, please check out the full code in the GitHub repo’s auth.service.tsfile here.There’s a lot going on, so let’s go through it step by step. First, as always, we’ll import our dependencies. This includes our environmentconfiguration we set up earlier to provide our Auth0, Firebase, and API settings, as well asauth0andfirebaselibraries,AngularFireAuthHttpClientto call the API to get a custom Firebase token, and the necessary RxJS imports.You can refer to the code comments for descriptions of the private and public members of our AuthServiceclass.Next is our constructor function, where we’ll make RouterAngularFireAuthandHttpClientavailable for use in our class.The login()method looks like this:login(redirect?: string) { // Set redirect after login const _redirect = redirect ? redirect : this.router.url; localStorage.setItem('auth_redirect', _redirect); // Auth0 authorize request this._auth0.authorize(); }If a redirectURL segment is passed into the method, we’ll save it in local storage. If no redirect is passed, we’ll simply store the current URL. We’ll then use the_auth0instance we created in our members and call Auth0’sauthorize()method to go to the Auth0 login page so our user can authenticate.The next three methods are handleLoginCallback()getUserInfo()and_setSession():handleLoginCallback() { this.loading = true; // When Auth0 hash parsed, get profile this._auth0.parseHash((err, authResult) => { if (authResult && authResult.accessToken) { window.location.hash = ''; // Store access token this.accessToken = authResult.accessToken; // Get user info: set up session, get Firebase token this.getUserInfo(authResult); } else if (err) { this.router.navigate(['/']); this.loading = false; console.error(`Error authenticating: ${err.error}`); } }); } getUserInfo(authResult) { // Use access token to retrieve user's profile and set session this._auth0.client.userInfo(this.accessToken, (err, profile) => { if (profile) { this._setSession(authResult, profile); } else if (err) { console.warn(`Error retrieving profile: ${err.error}`); } }); } private _setSession(authResult, profile) { // Set tokens and expiration in localStorage const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + Date.now()); localStorage.setItem('expires_at', expiresAt); this.userProfile = profile; // Session set; set loggedIn and loading this.loggedIn = true; this.loading = false; // Get Firebase token this._getFirebaseToken(); // Redirect to desired route this.router.navigateByUrl(localStorage.getItem('auth_redirect'));These methods are fairly self-explanatory: they use Auth0 methods parseHash()anduserInfo()to extract authentication results and get the user’s profile. We’ll also set our service’s properties to store necessary state (such as whether the user’s authentication state is loading and if they’re logged in or not), handle errors, save data to our service and local storage, and redirect to the appropriate route.We are also going to use the authentication result’s access token to authorize an HTTP request to our API to get a Firebase token. This is done with the _getFirebaseToken()and_firebaseAuth()methods:private _getFirebaseToken() { // Prompt for login if no access token if (!this.accessToken) { this.login(); } const getToken$ = () => { return this.http .get(`${environment.apiRoot}auth/firebase`, { headers: new HttpHeaders().set('Authorization', `Bearer ${this.accessToken}`) }); }; this.firebaseSub = getToken$().subscribe( res => this._firebaseAuth(res), err => console.error(`An error occurred fetching Firebase token: ${err.message}`) ); } private _firebaseAuth(tokenObj) { this.afAuth.auth.signInWithCustomToken(tokenObj.firebaseToken) .then(res => { this.loggedInFirebase = true; // Schedule token renewal this.scheduleFirebaseRenewal(); console.log('Successfully authenticated with Firebase!'); }) .catch(err => { const errorCode = err.code; const errorMessage = err.message; console.error(`${errorCode} Could not log into Firebase: ${errorMessage}`); this.loggedInFirebase = false; }); }We’ll create a getToken$observable from theGETrequest to our API’s/auth/firebaseendpoint and subscribe to it. If successful, we’ll pass the returned object with the custom Firebase token to the_firebaseAuth()method, which will authenticate with Firebase using Firebase’ssignInWithCustomToken()method. This method returns a promise, and when the promise is resolved, we can tell our app that Firebase login was successful. We can also schedule Firebase token renewal (we’ll look at this shortly). We’ll handle any errors appropriately.Our custom Firebase token will expire in 3600seconds (1 hour). This is only half as long as our default Auth0 access token lifetime (which is7200seconds, or 2 hours). To avoid having our users lose access to Firebase unexpectedly in the middle of a session, we’ll set up automatic Firebase token renewal with two methods:scheduleFirebaseRenewal()andunscheduleFirebaseRenewal().Note: You can also implement automatic session renewal with Auth0 in a similar manner using the checkSession()method. In addition, you could usecheckSession()to restore an unexpired authentication session in the constructor if a user navigates away from the app and then returns later. We won’t cover that in this tutorial, but this is something you should try on your own!scheduleFirebaseRenewal() { // If user isn't authenticated, check for Firebase subscription // and unsubscribe, then return (don't schedule renewal) if (!this.loggedInFirebase) { if (this.firebaseSub) { this.firebaseSub.unsubscribe(); } return; } // Unsubscribe from previous expiration observable this.unscheduleFirebaseRenewal(); // Create and subscribe to expiration observable // Custom Firebase tokens minted by Firebase // expire after 3600 seconds (1 hour) const expiresAt = new Date().getTime() + (3600 * 1000); const expiresIn$ = Observable.of(expiresAt) .pipe( mergeMap( expires => { const now = Date.now(); // Use timer to track delay until expiration // to run the refresh at the proper time return Observable.timer(Math.max(1, expires - now)); } ) ); this.refreshFirebaseSub = expiresIn$ .subscribe( () => { console.log('Firebase token expired; fetching a new one'); this._getFirebaseToken(); } ); } unscheduleFirebaseRenewal() { if (this.refreshFirebaseSub) { this.refreshFirebaseSub.unsubscribe(); } }To schedule automatic token renewal, we’ll create a timer observable that counts down to the token’s expiration time. We can subscribe to the expiresIn$observable and then call our_getFirebaseToken()method again to acquire a new token. ThesignInWithCustomToken()angularfire2 auth method returns a promise. When the promise resolves,scheduleFirebaseRenewal()is called, which in turn ensures that the token will continue to be renewed as long as the user is logged into our app.We’ll also need to be able to unsubscribe from token renewal, so we’ll create a method for that as well. Finally, the last two methods in our authentication service are logout()andtokenValid():logout() { // Ensure all auth items removed localStorage.removeItem('expires_at'); localStorage.removeItem('auth_redirect'); this.accessToken = undefined; this.userProfile = undefined; this.loggedIn = false; // Sign out of Firebase this.loggedInFirebase = false; this.afAuth.auth.signOut(); // Return to homepage this.router.navigate(['/']); } get tokenValid(): boolean { // Check if current time is past access token's expiration const expiresAt = JSON.parse(localStorage.getItem('expires_at')); return Date.now() < expiresAt; }The logout()method removes all session information from local storage and from our service, signs out of Firebase Auth, and redirects the user back to the homepage (the only public route in our app).The tokenValidaccessor method checks whether the Auth0 access token is expired or not by comparing its expiration to the current datetime. This can be useful for determining if the user needs a new access token; we won’t cover that in this tutorial, but you may want to explore Auth0 session renewal further on your own.That’s it for our AuthService!Callback ComponentRecall that we created a CallbackComponentin our root module. In addition, we set ourenvironment‘s Auth0redirectto the callback component’s route. That means that when the user logs in with Auth0, they will return to our app at the/callbackroute with the authentication hash appended to the URI.We created our AuthServicewith methods to handle authentication and set sessions, but currently these methods aren’t being called from anywhere. The callback component is the appropriate place for this code to execute.Open the callback.component.tsfile and add:// src/app/callback.component.ts import { Component, OnInit } from '@angular/core'; import { AuthService } from './auth/auth.service'; @Component({ selector: 'app-callback', template: `` }) export class CallbackComponent implements OnInit { constructor(private auth: AuthService) { } ngOnInit() { this.auth.handleLoginCallback(); } } All our callback component needs to do is show the LoadingComponentwhile theAuthService‘shandleAuth()method executes. ThehandleLoginCallback()method will parse the authentication hash, get the user’s profile info, set their session, and redirect to the appropriate route in the app.Auth GuardNow that we’ve implemented the authentication service, we have access to the properties and methods necessary to effectively use authentication state throughout our Angular application. Let’s use this logic to implement our AuthGuardfor protecting routes.Using the Angular CLI should have generated some helpful boilerplate code, and we only have to make a few minor changes to ensure that our guarded routes are only accessible to authenticated users. Note: It’s important to note that route guards on their own do not confer sufficient security. You should always secure your API endpoints, as we have done in this tutorial, and never rely solely on the client side to authorize access to protected data. Open the auth.guard.tsfile and make the following changes:// src/app/auth/auth.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { AuthService } from './auth.service'; @Injectable() export class AuthGuard implements CanActivate { constructor(private auth: AuthService) { } canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable| Promise | boolean { if (this.auth.loggedIn) { return true; } else { // Send guarded route to redirect after logging in this.auth.login(state.url); return false; } } } We’ll import AuthServiceadd aconstructor()function to make the service available in our route guard. ThecanActivate()method should returntrueif conditions are met to grant access to a route, andfalseif not. In our case, the user should be able to access the guarded route if they are authenticated. TheloggedInproperty from ourAuthServiceprovides this information.If the user does not have a valid token, we’ll prompt them to log in. We want them to be redirected back to the guarded route after they authenticate, so we’ll call the login()method and pass the guarded route (state.url) as the redirect parameter.Note: Remember that we set up our entire app’s architecture and routing earlier. We already added AuthGuardto our dog details route, so it should be protected now that we’ve implemented the guard.Core LogicThe last thing we’ll do in this section of our tutorial is build out the remaining components and services that belong to our CoreModule. We’ve already taken care of theLoadingComponentandErrorComponentso let’s move on to the header.The header will use methods and logic from our authentication service to show login and logout buttons as well as display the user’s name and picture if they’re authenticated. Open the header.component.tsfile and add:// src/app/core/header/header.component.ts import { Component } from '@angular/core'; import { AuthService } from '../../auth/auth.service'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styles: [` img { border-radius: 100px; width: 30px; } .loading { line-height: 31px; } .home-link { color: #212529; } .home-link:hover { text-decoration: none; } `] }) export class HeaderComponent { constructor(public auth: AuthService) {} }We’ll add a few simple styles and import our AuthServiceto make its members publicly available to our header component’s template.Next open the header.component.htmlfile and add:The header now shows: - The name of our app (“Popular Dogs”) with a link to the /route
- A login button if the user is not authenticated
- A “Logging in…” message if the user is currently authenticating
- The user’s picture, name, and a logout button if the user is authenticated
 Now that we have our header component built, we need to display it in our app. Open the app.component.htmlfile and add:The header component will now be displayed in our app with the current routed component showing beneath it. Check it out in the browser and try logging in! Dog and DogDetail ModelsLet’s implement our dog.tsanddog-detail.tsinterfaces. These are models that specify types for the shape of values that we’ll use in our app. Using models ensures that our data has the structure that we expect.We’ll start with the dog.tsinterface:// src/app/core/dog.ts export interface Dog { breed: string; rank: number; image: string; }Next let’s implement the dog-detail.tsinterface:// src/app/core/dog-detail.ts export interface DogDetail { breed: string; rank: number; description: string; personality: string; energy: string; group: string; image: string; link: string; }API ServiceWith our Node API and models in place, we’re ready to implement the service that will call our API in the Angular front end. Open the api.service.tsfile and add this code:// src/app/core/api.service.ts import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { environment } from './../../environments/environment'; import { AuthService } from './../auth/auth.service'; import { Observable } from 'rxjs/Observable'; import { catchError } from 'rxjs/operators'; import 'rxjs/add/observable/throw'; import { Dog } from './../core/dog'; import { DogDetail } from './../core/dog-detail'; @Injectable() export class ApiService { private _API = `${environment.apiRoot}api`; constructor( private http: HttpClient, private auth: AuthService) { } getDogs$(): Observable{ return this.http .get(`${this._API}/dogs`) .pipe( catchError((err, caught) => this._onError(err, caught)) ); } getDogByRank$(rank: number): Observable { return this.http .get(`${this._API}/dog/${rank}`, { headers: new HttpHeaders().set('Authorization', `Bearer ${this.auth.accessToken}`) }) .pipe( catchError((err, caught) => this._onError(err, caught)) ); } private _onError(err, caught) { let errorMsg = 'Error: Unable to complete request.'; if (err instanceof HttpErrorResponse) { errorMsg = err.message; if (err.status === 401 || errorMsg.indexOf('No JWT') > -1 || errorMsg.indexOf('Unauthorized') > -1) { this.auth.login(); } } return Observable.throw(errorMsg); } } We’ll add the necessary imports to handle HTTP in Angular along with the environment configuration, AuthServiceRxJS imports, andDogandDogDetailmodels we just created. We’ll set up private members for the_APIand to store the_accessTokenthen make theHttpClientandAuthServiceavailable privately to our API service.Our API methods will return observables that emit one value when the API is either called successfully or an error is thrown. The getDogs$()stream returns an observable with an array of objects that areDog-shaped. ThegetDogByRank$(rank)stream requires a numeric rank to be passed in, and will then call the API to retrieve the requestedDog‘s data. This API call will send anAuthorizationheader containing the authenticated user’s access token.Finally, we’ll create an error handler that checks for errors and assesses if the user is not authenticated and prompts for login if so. The observable will then terminate with an error. Note: We are using arrow functions to pass parameters to our handler functions for RxJS pipeable operators (such as catchError). This is done to preserve the scope of thethiskeyword (see the “No separatethis” section of the MDN arrow functions documentation).Next StepsWe’ve already accomplished a lot in the first part of our tutorial series. In the next part, we’ll finish our Popular Dogs application. In the meantime, here are some additional resources that you may want to check out: Angular Testing ResourcesIf you’re interested in learning more about testing in Angular, which we did not cover in this tutorial, please check out some of the following resources: Additional ResourcesYou can find more resources on Firebase, Auth0, and Angular here: In the next installment of our Auth0 + Firebase + Angular tutorial, we’ll display data from our dogs API and learn how to set up and implement realtime comments with Firebase! Check out Authenticating Firebase and Angular with Auth0: Part 2 now. 
Source link

