{"componentChunkName":"component---src-templates-blog-post-js","path":"/intro-to-nodejs-part-7/","result":{"data":{"site":{"siteMetadata":{"title":"tech-blog","author":"Alamgir Qazi"}},"markdownRemark":{"id":"c614c2bd-eeba-5a47-91e6-091c03e4f2f9","excerpt":"This is the part 7 of post Intro to Nodejs - part 6. So lets get started. In the next part, we will cover Guards and Authentication. Only…","html":"<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/d9199/node-js.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 590px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACKElEQVQoz32Sz2vTYBjHnzfVHedglzpRUUSTTE3UJq9jS9I0s/h7BwXdZbUyRUS9+A8sWZqijrGDN0+iB0U8CP4Nw7Vqi9OLStllIHrx5kWI3ydpvWngy/O27/N+np9E//m8sk2ua5ED4ayUfUkD/fMbOAQnJdXCI7SwqlPc0ShqaVSdscgtWzTllDL4wNevSJo+ZVM9MSlc06nR1WjhjUpLX8YzoJJFr0hlfvkwLb7TlLClKotvNcU+OAFQBhmBbkIFSMC/gP+VNCWK32uCgZBY+poD8+iIeqVhEEAUtlWKYE9fKgm+v3/3Hvt9h1+RA0wck2iFpEe/D1Ccw4YYCqDCjgH02g9kpZ4YFLU1B1k+gfXrTYP8aWnxPQPLFXsYQMuZlC9hq5UTNleko0UeNI6StzHwF/TM8+S3WmgW4476A9mlsD+vrxhjyKSH7B+XfXsT572AbpyfK7XxZvXGQ2M0WVdnUPZVAO8kH/RhBm6cnbW3Ati9eOvorrir9vrAzWvLZtFz5Wf4vIB6AI71gc+Dqtz+Kt29BRkybB66DeAIA/fjESH9fUQXlOYnVYXTA0zZqDdMLtmEz1NIIigP6RBKXoHdeWbWomhN34H+TUJ7eDvyoQCIhzREASUfeSAq95JqkSnQ278r47q24KFMHcfZyVcoauk86Ww4zfUcKPgSD8Uo+QwUUVstoGwxF5m8c9ma9Ncr80c/+TdVz9kMFBmwo4k0vUx/ANst4OwYAzGqAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"Intro To Node.js\"\n        title=\"\"\n        src=\"/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/fcda8/node-js.png\"\n        srcset=\"/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/12f09/node-js.png 148w,\n/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/e4a3f/node-js.png 295w,\n/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/fcda8/node-js.png 590w,\n/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/efc66/node-js.png 885w,\n/tech-blog/static/a717eafa290bf333c4dd1c86076c5b9e/d9199/node-js.png 960w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>This is the part 7 of post <a href=\"https://alamgirqazi.github.io/tech-blog/intro-to-nodejs-part-6/\">Intro to Nodejs - part 6</a>.</p>\n<p>So lets get started.</p>\n<p>In the next part, we will cover Guards and Authentication. Only the user who are logged in should see the books list and should only be able to add/edit/delete. We will discuss concepts like Authentication, Authorization, Guards and Interceptors.</p>\n<p>First, we need to protect our backend.</p>\n<p>Only authorized users should have access to Books List right. Only authorized users can add/update/delete books from the database.</p>\n<p>How do we do it?</p>\n<p>Remember, we created a JWT Token when we login? we will use that token and save in our “local Storage”.</p>\n<p>Once token is saved, every request is sent with an authorization header. That includes our credentials.</p>\n<p>Here is an example</p>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/7ca1f/authorization.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 590px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 46.621621621621614%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABJ0AAASdAHeZh94AAABoElEQVQoz5WSv24UMRDG9y2oaHkYGireI0K0SHmDQEFBQ0e4ipZISDwAl6MCkSDlcmFz2dyu927/eNf2+vZ+GfuADqSM9Glm7PHnb8ZOHjx8xI+znwQbt1t248goGIYB51zE4Ad077iYzzlfrDi78WQVLBZXnH6dcTqbMZ1+Ib1OSR4/ecr8Ys7oPbrVdF1H32lyVbDK8whVKs4vl7x5d8zrt8ccTWZ8+PydyeQ9z54fcHj4gpevjjj59JHE6ZZNVXNbKH4tM66ub8hyIZN8WQhhWbKW/aZpsNbincHbFtvv83G778hLF8b0JFprejfQWYc2js5Y2t5gneeyVUzVim/KkjaWqrc0glrab6Q2xE3byjmLE3InPEkhKuqqwhpDlt2K2rW03GH6Pq714kMcfFC0lTn/G55EKUUpbYXiNE0p12U87GWmwXa7HfexJJBkWUYlKvN8FWdlRNl9if4S1nUtLxva0SyXJ0I+pyjKeEHYq+oqxpvNJl4WfkGA1h1tmJ/Wv3MdkYzjn1fy5Cv5IqqMBUYGHZQGhHHsvYt1/8Mdb1CocmeyhMQAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"authorization\"\n        title=\"\"\n        src=\"/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/fcda8/authorization.png\"\n        srcset=\"/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/12f09/authorization.png 148w,\n/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/e4a3f/authorization.png 295w,\n/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/fcda8/authorization.png 590w,\n/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/efc66/authorization.png 885w,\n/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/c83ae/authorization.png 1180w,\n/tech-blog/static/c16e41e2ffeb67f2d100fb6bba285cab/7ca1f/authorization.png 1890w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>do you see the highlighted part? thats the JWT Token. every request includes that token. Without that token, that server will throw a 401 or 403 HTTP Error stating you do not have access to this resource.</p>\n<p>We need to incorporate that in our app.</p>\n<p>lets first setup our application. we will use that same Ionic and Node app from previous part.</p>\n<p>We need to add a check in our Node.js / Express app. That check will check if the request that Authorization token in it. If it doesnot, then it will throw an error.</p>\n<p>We use <em>middlewares</em> for that.</p>\n<p>The main idea of middlewares is that every request goes through middleware before executing our method.</p>\n<p>Lets see some code to get a better idea.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">const checkAuth = require('../middleware/check-auth');\n\nrouter.get(\"/\",checkAuth, bookController.getAll);</code></pre></div>\n<p>When we go to localhost:3000/books</p>\n<p>then first <code class=\"language-text\">checkAuth</code> will run. After that, <code class=\"language-text\">UserController.getAll</code> will run.</p>\n<p>If any error then checkAuth will throw the error and it won’t touch <code class=\"language-text\">UserController.getAll</code>. That’s it. that’s all the logic.</p>\n<p>lets also make one change in our code.</p>\n<p>in users.controller.js</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"> const token = jsonwebtoken.sign({\n               data: result,\n               role: 'User'\n            }, process.env.JWT_KEY, { expiresIn: '7d' });</code></pre></div>\n<p>make sure we use <code class=\"language-text\">process.env.JWT_KEY</code> instead of previously <code class=\"language-text\">supersecretToken</code></p>\n<p>Lets take a look at <code class=\"language-text\">checkAuth</code> middleware.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">const jwt = require('jsonwebtoken');\n\nmodule.exports = (req, res, next) => {\n    try {\n        const token = req.headers.authorization;\n        const decoded = jwt.verify(token, process.env.JWT_KEY);\n        req.userData = decoded;\n        next();\n    } catch (error) {\n        next({\n            message: 'Auth failed',\n            status: 401\n        });\n    }\n};</code></pre></div>\n<p>we get that authorization from <code class=\"language-text\">req.headers</code>.</p>\n<p>we decode it using the jwt library.</p>\n<p>if there is no token or it has expired, then it will throw the error.</p>\n<p>Lets see this in action.</p>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/e0124299570cadd27de6f76a09b41805/8b70b/unauth.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 590px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 63.51351351351351%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABJ0AAASdAHeZh94AAACAUlEQVQ4y41TS5LTMBD1xbgCVLFkyQ24B8WODSw4B1eYJVQBmWSSyST2xIktWWpZtiTLj7bymSkGalDV8+tWW62WXivrug6WIYREVQsI2YDIwLTtE7TWorMGrdHoO5NgW4I1xHMEMgaZ4c+EdlqQcPQNEc4xSjah0QbLXGF+J5k1VoXGsmgwy/dYlyL9kznn8Bz63gGjx/dbh1cfIt58jHj7ecDr98DLLxu8uPqEd9++AiEi897jOTjnEZiVcZgVEfP7iMUu4roYMdt5/KgkVkLD8+b/VeEZwXOlGHEc4yP76Pd9jyyEYxVTBSGEo/0Hn23HPI4PqR6nDMOQNs002cvln8VJfPbTnEkXPvlTFakzJsUvbNO8tS2yTV6iqirsDgJ3RYW8uIfk1lGaIBsFpXSyJz5UNceLh/kTGgaZqRBOaKqfaOUNqJolqMOvxK1YgOoZjJhDT7H6OtlUz2GbGzhaw5k7dGrJ/gKD3QK+RNbtc4hig/1mhXK7gs5v0W6WoO0anRRwWjJqhKbCoJhJIMhD8oMo4eUefSPgudkjN3oW+UKptmi4MVVZgg4VFL+YRvCLkRqGj2f5yB0fq2fEMGCMI8YhYjzZbCZMAmWTQl3TQ+0JkXgBXzxYMcR45CHwQkYcThzxdJz05hZICZ31nJSVFPx8WKDAyv2tPS7g4L/wG/gt5kbM3PsMAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"unauth\"\n        title=\"\"\n        src=\"/tech-blog/static/e0124299570cadd27de6f76a09b41805/fcda8/unauth.png\"\n        srcset=\"/tech-blog/static/e0124299570cadd27de6f76a09b41805/12f09/unauth.png 148w,\n/tech-blog/static/e0124299570cadd27de6f76a09b41805/e4a3f/unauth.png 295w,\n/tech-blog/static/e0124299570cadd27de6f76a09b41805/fcda8/unauth.png 590w,\n/tech-blog/static/e0124299570cadd27de6f76a09b41805/efc66/unauth.png 885w,\n/tech-blog/static/e0124299570cadd27de6f76a09b41805/c83ae/unauth.png 1180w,\n/tech-blog/static/e0124299570cadd27de6f76a09b41805/8b70b/unauth.png 1266w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>we get a 401 error. We don’t see our books list anymore.</p>\n<p>For that to happen, we first need to save our JWT token after we login into local Storage or ionic Storage.</p>\n<p>lets do that.</p>\n<p>lets create an <code class=\"language-text\">auth.service.ts</code> file in sdk -> core folder.</p>\n<p>lets install these to access ionic storage.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">ionic cordova plugin add cordova-sqlite-storage\n\nnpm install @ionic/storage</code></pre></div>\n<p>to use it in our service file, we need to import it in app.module.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">imports: [\n    BrowserModule,\n    IonicModule.forRoot(),\n    IonicStorageModule.forRoot(),\n    HttpClientModule,\n    AppRoutingModule\n  ]</code></pre></div>\n<p>lets write code in our Auth.service.ts file</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import { Injectable } from '@angular/core';\nimport { Storage } from '@ionic/storage';\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class AuthService {\n  constructor(private storage: Storage) {}\n\n  public saveTokenToStorage(token: string) {\n    this.storage.set('token', token);\n  }\n\n  public async getTokenFromStorage() {\n    return await this.storage.get('token');\n  }\n}</code></pre></div>\n<p>we will use this in login.page.ts</p>\n<p>lets update our code. we just add one line. we get the token in data.token so</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  data => {\n        console.log('got response from server', data);\n        this.loading = false;\n        this.authService.saveTokenToStorage(data.token);\n        this.router.navigateByUrl('/home');\n      },</code></pre></div>\n<p>Nice. we still however need to add this token to our books request. Lets do that.</p>\n<p>We will be changing our methods a little bit.</p>\n<p>We first need to update our GET /books request.</p>\n<p>lets go to <code class=\"language-text\">books.service.ts</code></p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  public async getAllBooks(): Promise&lt;any> {\n    const url = BookyConfig.getPath() + '/books';\n    const token = await this.authService.getTokenFromStorage();\n    return this.http.get(url, {\n      headers: new HttpHeaders().set('Authorization', token)\n    });\n  }</code></pre></div>\n<p>Few things we changed. We converted our Observable to Promise (because we had to use async/await for getting token from Ionic Storage).</p>\n<p>we append that Token in our headers.</p>\n<p>remember our Node.js middleware where we read <code class=\"language-text\">req.headers.authorization</code> ? that’s what we are adding here.</p>\n<p>We also updated the component method. Lets take a look.</p>\n<p>books.page.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">async getAll() {\n    this.loading = true;\n\n    const observable = await this.booksService.getAllBooks();\n    observable.subscribe(\n      data => {\n        this.books = data.data.docs;\n        this.loading = false;\n        console.log('data', data);\n      },\n      err => {\n        console.log('err', err);\n      }\n    );\n  }</code></pre></div>\n<p>Now, our books should return.</p>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/79c520a1eb7e063690c2a9a4b0077b1f/2108e/booksback.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 453px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 106.08108108108108%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAABJ0AAASdAHeZh94AAADDklEQVQ4y51UTU8TURTt1p3+BKNGE1DjgoiJC3cmbnRn/EwkwRCXukE0qCQu/EjckBBBtsZIDKiEIJGQaNAElUSgtKWlWFrotLSl3zOdefOO975pSwssgJucvpl7J+e9e+55dY3PGLjZI9DWL3D7jYPWPoFbrwVaGL0b+Wq9v4Qbgz5c+TiLq0NuXP/kxqWBOQz8jcP19ruBQ/eAUw+Akx1AQztw7inQ0gdc6wEuvgIa7wMnOhgSJ6l+7EkS+z2taNTu4EikDU3Ru9j37zIeLn2A6/2kjuPtNs48FjjdSXhk42yXjQsvHJx/Zqtcs4JAcyfQ1JXDwaFeHB3txuGRbjSM9uDA0Es8n56CS0sW4Q5bWIwBAQ3wREyChbnlDXgjDjwKVKfvvUH6dhFqZcwHgFXNhMsyDQA2tGgYyUQUTlgqtxUSG7G1JoRBhJZAKLKKd0PDGPg8ikQqjWBwCR6vTyEQCCJC9YWFAOJrCUUphFQEa1mJnC6xFJeIpukYdDiXYRgwSiWkMxlCFqZpqv2FEFXYtq1WKWUdbAJ4taXaiLlcuq5jJ6EIiLgWkDblN1rWdSIs0en4YXpmDrMeHyzLQi6XQ6FQQD6fRzabVbkKKWqUNCltCiBvSBj8XCJCISxEVqMYHhvHyNcJpNbTiMVicLvdWF5eht/vRzqdricsM6by1CYpFE5KJHIkk1XWkPXhkzJUK9tEbauSIZ3BoLJS6LvRcLsNZEVDWdFQh4unytP9MvENE5M/lZ7JZBKJRALxeLwOnItGo0hQnYkKJToVtcz2yepl27DgWiyOyanf+PHrD9bZPuvrVYJQKKQ0TaVSal1ZWVE1aVvIFnkojh8zxTLhXlt2NNzGNhXCPNmEE7sjrB+KMja3HKMrNTgypqxTKBYR0zTVGuvF1gmHw9Ao5/V66VoGkSN/cqQLEkXScSUlkaSU4JZ5KOy9+QU/5n1+Rcg7sffY1KwdD4mfM6Qvv7O9OIolvj1Q95mHo4y9Fw25XSEcu1SuYNU2/LP50u8EFeLa97o/h83F3U/cIfwPHdsbXCStN5AAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"booksback\"\n        title=\"\"\n        src=\"/tech-blog/static/79c520a1eb7e063690c2a9a4b0077b1f/2108e/booksback.png\"\n        srcset=\"/tech-blog/static/79c520a1eb7e063690c2a9a4b0077b1f/12f09/booksback.png 148w,\n/tech-blog/static/79c520a1eb7e063690c2a9a4b0077b1f/e4a3f/booksback.png 295w,\n/tech-blog/static/79c520a1eb7e063690c2a9a4b0077b1f/2108e/booksback.png 453w\"\n        sizes=\"(max-width: 453px) 100vw, 453px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>Now, unless you login properly, the GET request won’t work.</p>\n<p>we need to add this to other methods as well in Node.js server.</p>\n<p><code class=\"language-text\">books.routes.js</code></p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">const checkAuth = require('../middleware/check-auth');\n\nconst bookController = require('../controllers/books.controllers');\n\nrouter.get(\"/\",checkAuth,bookController.getAll);\nrouter.post(\"/add\",checkAuth,bookController.addBook);\nrouter.put(\"/:_id\",checkAuth, bookController.updateBook);\nrouter.delete(\"/:_id\",checkAuth, bookController.deleteBook);</code></pre></div>\n<p>Now it will work for all.</p>\n<p>we need to update our methods in Angular/Ionic as well.</p>\n<p>books.service.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import { HttpClient, HttpHeaders } from '@angular/common/http';\n\nimport { AuthService } from './../core/auth.service';\nimport { BookyConfig } from '../booky.config';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class BooksService {\n  constructor(private http: HttpClient, private authService: AuthService) {}\n\n  public async getAllBooks(): Promise&lt;any> {\n    const url = BookyConfig.getPath() + '/books';\n    const token = await this.authService.getTokenFromStorage();\n    return this.http.get(url, {\n      headers: new HttpHeaders().set('Authorization', token)\n    });\n  }\n\n  public async addNewBook(data: object): Promise&lt;any> {\n    const url = BookyConfig.getPath() + '/books/add';\n    const token = await this.authService.getTokenFromStorage();\n\n    return this.http.post(url, data, {\n      headers: new HttpHeaders().set('Authorization', token)\n    });\n  }\n  public async updateBook(data): Promise&lt;any> {\n    const url = BookyConfig.getPath() + `/books/${data._id}`;\n    const token = await this.authService.getTokenFromStorage();\n\n    return this.http.put(url, data, {\n      headers: new HttpHeaders().set('Authorization', token)\n    });\n  }\n  public async deleteBook(id: string): Promise&lt;any> {\n    const url = BookyConfig.getPath() + `/books/${id}`;\n    const token = await this.authService.getTokenFromStorage();\n\n    return this.http.delete(url, {\n      headers: new HttpHeaders().set('Authorization', token)\n    });\n  }\n}</code></pre></div>\n<p>Lets update the methods as well</p>\n<p>books.page.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  async deleteBook() {\n    this.deleteLoading = true;\n    const observable = await this.booksService.deleteBook(\n      this.selectedBook._id\n    );\n\n    observable.subscribe(\n      data => {\n        console.log('got response from server', data);\n        this.deleteLoading = false;\n        this.getAll();\n      },\n      error => {\n        this.deleteLoading = false;\n        console.log('error', error);\n      }\n    );\n  }\n  }</code></pre></div>\n<p>addnewbook.component.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  async addNew() {\n    const observable = await this.booksService.addNewBook(\n      this.addNewBookForm.value\n    );\n    observable.subscribe(\n      async data => {\n        console.log('got response from server', data);\n        const name = this.addNewBookForm.controls['name'].value;\n        const toast = await this.toastController.create({\n          message: `${name} has been added successfully.`,\n          duration: 3500\n        });\n        toast.present();\n        this.loading = false;\n        this.addNewBookForm.reset();\n        //optional\n\n        this.modalCtrl.dismiss();\n      },\n      error => {\n        this.loading = false;\n        console.log('error', error);\n      }\n    );\n  }\n  async updateBook() {\n    const observable = await this.booksService.updateBook(\n      this.addNewBookForm.value\n    );\n\n    observable.subscribe(\n      async data => {\n        console.log('got response from server', data);\n        const name = this.addNewBookForm.controls['name'].value;\n        const toast = await this.toastController.create({\n          message: `${name} has been updated successfully.`,\n          duration: 3500\n        });\n        toast.present();\n        this.loading = false;\n        this.addNewBookForm.reset();\n        //optional\n\n        this.modalCtrl.dismiss();\n      },\n      error => {\n        this.loading = false;\n        console.log('error', error);\n      }\n    );\n  }</code></pre></div>\n<p>Now our flow is working.</p>\n<p>Now we’re successfully restricting our user access to the API without our secure token.</p>\n<p>What we need to do is prevent the user even going to books page if he doesnot have a token.</p>\n<p>In Angular, we do that using <strong><em>Guards</em></strong></p>\n<p>Lets create a folder inside sdk -> custom -> guards</p>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/3983283215db8b77e9ecbd51ea6cbf54/89aeb/newfiles.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 323px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 77.70270270270271%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABJ0AAASdAHeZh94AAACmUlEQVQ4y41TyXLTQBT0F3DkQmJFlrXOaJuRZrRYkq3ESxYgFaiioIriwoVv4MiNn26epCRFKBM4tP1G9rT6db83WzgcTpCg7i/RX79DLgX6rsTcCmDa7J84WwawXA4hxFjPBkLbiyCKDuVmjziKoPMMxsJ/cuk5woXD4LNoIjTpwyaFjhvg5PYHgotPsPMYrClhkMoHsr+Rjs9JoSPlPSE9tNwQSy+EzFqwRMHmEdwoIeXhfykcvp3gQaEdwCWFVpDi53WF93UJrTIcNjW2bQnPD0elx0h+91CmAubkIf1AcDxqO0mQXzXwYjHWHnlpk9JjCp9YQTksQxLlxVMo5miqJB8SbL/eIG4bsK4A7yq45M3c8GCY/iP+VDgQDi9e+snU8uQjx/zUg6UYvJ7UphJekcMvc7j5VAeVorMaBQxEIwY76GxTR0vqcmYT6wCPC1h2jKDJELQZfKkR9y3CtkbYTGBVCaY1XYxhOdGIoR6U+ZJezDPMQkF/FiskitrkVB9aiFtCewH99hLqao/8cg/9+oD8sJvqmwPKu2vkuy14WiGUK6RdjzjrMHOZIGbyjzxkocD6XIBlGZjQiHSBINPghLiqwBX5WlakRo3w0pzGJaX7gsg0/EhPHg5Jn5oB0jTAt48uSiWxo1D2mwq1zrCpFfqVxpqgaDVPDBdntEnDNhmWT/PKcUHrupxSpkAoJcNi4BHHhzcBfJ4gkQVYLBELhZjqMFXjeRyzIwNuUED3KTMMozMnwoQt8eXWGi836y2EXqFqehSrDWENqepR0dHteSQcB5tjsXDw4u47XjUfIEWOrOwgMko1zqHrNVTVgdNaPrfTjwoHQtPy8LL/DKvYQRcrItmgIlWZbkay9vwAkdc0d/6zhL8AiozqUM6afHYAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"newfiles\"\n        title=\"\"\n        src=\"/tech-blog/static/3983283215db8b77e9ecbd51ea6cbf54/89aeb/newfiles.png\"\n        srcset=\"/tech-blog/static/3983283215db8b77e9ecbd51ea6cbf54/12f09/newfiles.png 148w,\n/tech-blog/static/3983283215db8b77e9ecbd51ea6cbf54/e4a3f/newfiles.png 295w,\n/tech-blog/static/3983283215db8b77e9ecbd51ea6cbf54/89aeb/newfiles.png 323w\"\n        sizes=\"(max-width: 323px) 100vw, 323px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>we create two files.</p>\n<p>islogin.guard.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import { CanActivate, Router } from '@angular/router';\n\nimport { AuthService } from 'src/sdk/core/auth.service';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class IsLoginGuard implements CanActivate {\n  constructor(private router: Router, private authService: AuthService) {}\n\n  async canActivate() {\n    const token = await this.authService.getTokenFromStorage();\n    if (!token) {\n      this.router.navigateByUrl('/login');\n    } else {\n      return true;\n    }\n  }\n}</code></pre></div>\n<p>we will come back to redirect.login.ts in a while.</p>\n<p>In the meantime, lets use <code class=\"language-text\">IsLoginGuard</code> service/guard we created.</p>\n<p>lets go to app-routing.module.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"> {\n    path: 'books',\n    canActivate: [IsLoginGuard],\n    loadChildren: () =>\n      import('./books/books.module').then(m => m.BooksPageModule)\n  }</code></pre></div>\n<p>Now if there is no token, it won’t let us go to books page.</p>\n<p>To test it, lets change app.component.html and .ts a little bit</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  &lt;ion-list>\n          &lt;ion-menu-toggle auto-hide=\"false\" *ngFor=\"let p of appPages\">\n            &lt;ion-item *ngIf=\"p?.title!=='Logout'\" [routerDirection]=\"'root'\" [routerLink]=\"[p.url]\">\n              &lt;ion-icon *ngIf=\"p?.icon\" slot=\"start\" [name]=\"p.icon\">&lt;/ion-icon>\n              &lt;ion-label>\n                {{p.title}}\n              &lt;/ion-label>\n            &lt;/ion-item>\n            &lt;ion-item (click)=\"logout()\" *ngIf=\"p?.title==='Logout'\">\n              &lt;ion-icon *ngIf=\"p?.icon\" slot=\"start\" [name]=\"p.icon\">&lt;/ion-icon>\n              &lt;ion-label>\n                {{p.title}}\n              &lt;/ion-label>\n            &lt;/ion-item>\n          &lt;/ion-menu-toggle>\n        &lt;/ion-list></code></pre></div>\n<p>app.component.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">public appPages = [\n    {\n      title: 'Home',\n      url: '/home',\n      icon: 'home'\n    },\n    {\n      title: 'Books',\n      url: '/books',\n      icon: 'book'\n    },\n    {\n      title: 'Logout',\n      icon: 'power'\n    }\n  ];\n\n  logout() {\n    this.authService.logout();\n  }</code></pre></div>\n<p>Click on logout on the drawer will log you out of the app (It will delete the token from storage).</p>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/tech-blog/static/2a36a8632891237871053554071845fc/29114/tokensaved.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 590px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 43.24324324324324%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABJ0AAASdAHeZh94AAABC0lEQVQoz6WSuU7FMBBF/f8tv0FDAQ2voKFAFDzEDyChhCzet2wkl7GThxAoBTxLR3c0cz1jy2bDMGAP733WZZkpDjg+vWAcR8qNu3tYMnwnJU8aQkDXdUhLSY+ryzt8zDvNtv3sZyE3ngiK+75HjCE39LbC/eGCagumafrF6UBMKgVjDHRCa9R1jfeqQllzKKopKSlvIARHWbzCkIc3DQTnkGKDYlFX2cM4F7DWZRSZi6JYzUrnAWYblNQ6D9G2kDRUUlPZNqtupGFMS44+WPTRITgN7xzOWezm4Q3XjxVujwKHZ4myUbkwzzO97vJnWNFolK3NpDjE7rwTKrpy8BbRu1Xpq3y99j/4BED6uMCCm/UfAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"token saved\"\n        title=\"\"\n        src=\"/tech-blog/static/2a36a8632891237871053554071845fc/fcda8/tokensaved.png\"\n        srcset=\"/tech-blog/static/2a36a8632891237871053554071845fc/12f09/tokensaved.png 148w,\n/tech-blog/static/2a36a8632891237871053554071845fc/e4a3f/tokensaved.png 295w,\n/tech-blog/static/2a36a8632891237871053554071845fc/fcda8/tokensaved.png 590w,\n/tech-blog/static/2a36a8632891237871053554071845fc/efc66/tokensaved.png 885w,\n/tech-blog/static/2a36a8632891237871053554071845fc/c83ae/tokensaved.png 1180w,\n/tech-blog/static/2a36a8632891237871053554071845fc/29114/tokensaved.png 1920w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<p>We can see our token being saved in IndexedDB (Ionic Storage handles this for us).</p>\n<p>Lets see what redirectLogin is supposed to do.</p>\n<p>Imagine if Im already logged in, I should never go to login page right? right now I can do that. which is wrong.</p>\n<p>so lets add redirectLogin.guard.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import { CanActivate, Router } from '@angular/router';\n\nimport { AuthService } from 'src/sdk/core/auth.service';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class RedirectLoginGuard implements CanActivate {\n  constructor(private router: Router, private authService: AuthService) {}\n\n  async canActivate() {\n    const token = await this.authService.getTokenFromStorage();\n    if (token) {\n      this.router.navigateByUrl('/books');\n    } else {\n      return true;\n    }\n  }\n}</code></pre></div>\n<p>lets use it in app-routing.module.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  {\n    path: 'login',\n    canActivate: [RedirectLoginGuard],\n    loadChildren: () =>\n      import('./login/login.module').then(m => m.LoginPageModule)\n  },\n  {\n    path: 'register',\n    canActivate: [RedirectLoginGuard],\n    loadChildren: () =>\n      import('./register/register.module').then(m => m.RegisterPageModule)\n  },</code></pre></div>\n<p>Now if I go to login page, it takes me to /books as long as I’m logged in.</p>\n<p>We have covered almost all scenarios. However, one is missing.</p>\n<p>Lets say your token has expired and server send a 401 error. What do you do now? if we get a 401 or 403 error from server, we need to quickly send it back to login page. don’t allow access to Books api.</p>\n<p>For that, we use <em>Interceptors</em></p>\n<p>Interceptors in Angular catch requests before and after. All HTTP Requests in Angular go through the interceptor.</p>\n<p>lets create an httpinterceptor.service.ts in sdk -> core -> httpinterceptor.service.ts</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import {\n  HTTP_INTERCEPTORS,\n  HttpErrorResponse,\n  HttpEvent,\n  HttpHandler,\n  HttpInterceptor,\n  HttpRequest\n} from '@angular/common/http';\n\nimport { AuthService } from './auth.service';\nimport { Injectable } from '@angular/core';\nimport { Observable } from 'rxjs/internal/Observable';\nimport { Router } from '@angular/router';\nimport { tap } from 'rxjs/operators';\n\n@Injectable()\nexport class ErrorInterceptor implements HttpInterceptor {\n  constructor(private router: Router, private authService: AuthService) {}\n\n  intercept(\n    req: HttpRequest&lt;any>,\n    next: HttpHandler\n  ): Observable&lt;HttpEvent&lt;any>> {\n    return next.handle(req).pipe(\n      tap(\n        event => {},\n        err => {\n          if (\n            err instanceof HttpErrorResponse &amp;&amp;\n            (err.status === 403 || err.status === 401)\n          ) {\n            this.authService.logout();\n          }\n        }\n      )\n    );\n  }\n}</code></pre></div>\n<p>To use it, lets go to app.module.ts</p>\n<p>import these</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';\n\nimport { ErrorInterceptor } from 'src/sdk/core/httpinterceptor.service';</code></pre></div>\n<p>and update providers</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">providers: [\n    StatusBar,\n    SplashScreen,\n    {\n      provide: HTTP_INTERCEPTORS,\n      useClass: ErrorInterceptor,\n      multi: true\n    },\n    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }\n  ],</code></pre></div>\n<p>and Boom. its set up.</p>\n<p>We covered quite a few things things here</p>\n<h3>Authentication</h3>\n<p>Check if the user is login or not.</p>\n<h3>Authorization</h3>\n<p>Check if the user has to roles to perform certain tasks. For e.g is it admin or user ? We didn’t cover this in here.</p>\n<h3>Guards</h3>\n<p>Angular services that prevent going to a page/route under some condition.</p>\n<h3>Interceptor</h3>\n<p>Catch HTTP Requests and give us control. These are like middlewares for angular.</p>\n<p>Here’s <a href=\"https://github.com/alamgirqazi/intro-to-nodejs-part7\">link</a> to the node.js app code.</p>\n<p>Here’s <a href=\"https://github.com/alamgirqazi/booky-ionic/tree/part7\">link</a> to the ionic app code. (<em>branch part7</em>)</p>\n<p>In the next part, we will deploy our application to production.</p>\n<p>we will use mlab / mongodb Atlas for online database.</p>\n<p>we will use heroku platform to deploy our backend.</p>\n<p>we will create an APK app using Capacitor.</p>","frontmatter":{"title":"A very basic introduction to Node.js - Part 7","date":"December 15, 2019"}}},"pageContext":{"slug":"/intro-to-nodejs-part-7/","previous":{"fields":{"slug":"/intro-to-nodejs-part-6/"},"frontmatter":{"title":"A very basic introduction to Node.js - Part 6"}},"next":{"fields":{"slug":"/intro-to-nodejs-part-8/"},"frontmatter":{"title":"A very basic introduction to Node.js - Part 8"}}}},"staticQueryHashes":[]}