{"id":1507,"date":"2020-11-24T02:04:44","date_gmt":"2020-11-24T02:04:44","guid":{"rendered":"http:\/\/10.0.0.14\/?p=1507"},"modified":"2021-09-11T22:06:35","modified_gmt":"2021-09-11T22:06:35","slug":"tdd-with-gatsby-django-docker-part-2-chapter-13-authentication","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/11\/24\/tdd-with-gatsby-django-docker-part-2-chapter-13-authentication\/","title":{"rendered":"TDD with Gatsby, Django &#038; Docker Part 2, Chapter 13 &#8212; Authentication"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Front End Authentication with Gatsby &amp; JWT<\/h1>\n\n\n\n<p>Now we are at the point where we want to password protect our Gatsby Todo App. Remember way way back in <a rel=\"noreferrer noopener\" href=\"http:\/\/tdd-with-django-graphene-docker-part-5-jwt\" target=\"_blank\">Part 1 of this tutorial where we implemented JWT authentication for our Django backend<\/a>. Implementing JWT authentication for Gatsby is a little different.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Client-only Routes<\/h4>\n\n\n\n<p>To create pages with gated content that are restricted to only authenticated users, we will use Gatsby&#8217;s client-only routes to tell Gatsby which pages we want to password protect. To keep it simple we will create a password protected user profile page which displays the logged in user&#8217;s name, email, etc.<\/p>\n\n\n\n<p>We start by breaking out the main menu into its own component.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">NavBar.js<\/h4>\n\n\n\n<p>Create a new navbar.js file in the components folder:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/src\/components\/navbar.js\n<\/pre><\/div>\n\n\n<p>Add the following to the navbar.js file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/navbar.js\n\nimport { Link } from &quot;gatsby&quot;\nimport React from &quot;react&quot;\nimport styled from &quot;styled-components&quot;\n\nconst Nav = styled.nav`\n  a {\n    padding: 0 1.0rem 0 0;\n  }\n`\n\nexport const Navbar = () =&gt; {\n  return(\n    &amp;lt;div&gt;\n      &amp;lt;Nav&gt;\n        &amp;lt;Link to=&quot;\/&quot;&gt;Home&amp;lt;\/Link&gt;\n        {\/* More to come here *\/}\n      &amp;lt;\/Nav&gt;\n    &amp;lt;\/div&gt;\n  )\n}\n\nexport default Navbar\n<\/pre><\/div>\n\n\n<p>&#8230; and refactor the header.js file to include our Navbar component:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [8,42]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/header.js\n\nimport { Link } from &quot;gatsby&quot;\nimport PropTypes from &quot;prop-types&quot;\nimport React from &quot;react&quot;\nimport { useStaticQuery, graphql } from &quot;gatsby&quot;\nimport styled from &quot;styled-components&quot;\nimport Navbar from &quot;.\/navbar&quot;\n\nconst HeaderWrapper = styled.div`\n  padding: 2.0rem;\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: space-between;\n  background-color: rebeccapurple;\n  a {\n    color: white;\n    text-decoration: none;\n  }\n`\nconst SiteTitle = styled.div`\n  font-family: Arial, Helvetica, sans-serif;\n`\nconst HeaderH1 = styled.h1`\n  font-size: 2.6rem;\n`\n\nexport const PureHeader = ({ data }) =&gt; (\n  &amp;lt;HeaderWrapper&gt;\n    &amp;lt;SiteTitle&gt;\n      &amp;lt;HeaderH1&gt;\n        &amp;lt;Link\n          to=&quot;\/&quot;\n          \n        &gt;\n          {data.site.siteMetadata.title}\n        &amp;lt;\/Link&gt;\n      &amp;lt;\/HeaderH1&gt;\n    &amp;lt;\/SiteTitle&gt;\n\n    &amp;lt;Navbar \/&gt;\n\n  &amp;lt;\/HeaderWrapper&gt;\n)\n\nexport const Header = props =&gt; {\n  const data = useStaticQuery(graphql`\n    query {\n      site {\n        siteMetadata {\n          title\n        }\n      }\n    }\n  `)\n\n  return &amp;lt;PureHeader {...props} data={data}&gt;&amp;lt;\/PureHeader&gt;\n}\n\nHeader.propTypes = {\n  siteTitle: PropTypes.string,\n}\n\nHeader.defaultProps = {\n  siteTitle: ``,\n}\n\nexport default Header\n<\/pre><\/div>\n\n\n<p>In the refactoring of the Header component we imported the Navbar component and replaced the MainMenu styled-component wrapper with the Navbar component. As well, the Main Menu styled component definition was removed since we are no longer using it.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Authentication Helpers<\/h4>\n\n\n\n<p>It would be nice to have a couple of helper functions for:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Telling us if a user is logged in<\/li><li>Get the logged in user&#8217;s info<\/li><\/ul>\n\n\n\n<p>Lets create a new folder for services with a new file auth.js:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmkdir frontend\/src\/services\ntouch frontend\/src\/services\/auth.js\n<\/pre><\/div>\n\n\n<p>In the auth.js file we&#8217;ll create getUser() and isLoggedIn() functions:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/services\/auth.js\n\nexport const isBrowser = () =&gt; typeof window !== &quot;undefined&quot;;\n\nexport const setUser = (username, token) =&gt; {\n  window.localStorage.setItem(&quot;user&quot;, JSON.stringify({\n    &quot;username&quot;: username,\n    &quot;token&quot;: token\n  }))\n}\n\nexport const getUser = () =&gt; {\n  let user = JSON.parse(window.localStorage.getItem(&quot;user&quot;))\n  return user\n}\n\nexport const getToken = () =&gt; {\n  if (isBrowser() &amp;amp;&amp;amp; window.localStorage.getItem(&quot;token&quot;)) {\n    return window.localStorage.getItem(&quot;token&quot;)\n  } else {\n    return {}\n  }\n}\n\nexport const isLoggedIn = () =&gt; {\n  try {\n    var userObj = getUser()\n    var userToken = userObj.token.tokenAuth.token\n    userToken = &quot;JWT &quot; + userToken\n\n    \/\/ token that Django set\n    var setToken = getToken()\n\n    \/\/ compare tokens as security caution\n    if (userToken == setToken) {\n      return true\n    }\n  } catch (error) {\n    return false\n  }\n}\n\nexport const logout = () =&gt; {\n  window.localStorage.clear()\n}\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Gatsby Client-Only Routes<\/h4>\n\n\n\n<p>We will be using <a rel=\"noreferrer noopener\" href=\"https:\/\/reach.tech\/router\/\" target=\"_blank\">@reach\/router<\/a> to create our password pages. The @reach\/router library comes with Gatsby so we don&#8217;t need to install it.<\/p>\n\n\n\n<p>First we create gatsby-node.js file in the root of the frontend and we will define routes that start with \/app\/ as our password protected pages.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/gatsby-node.js\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/gatsby-node.js\n\n\/\/ Implement the Gatsby API \u201conCreatePage\u201d. This is\n\/\/ called after every page is created.\nexports.onCreatePage = async ({ page, actions }) =&gt; {\n  const { createPage } = actions\n\n  \/\/ page.matchPath is a special key that's used for matching pages\n  \/\/ only on the client.\n  if (page.path.match(\/^\\\/app\/)) {\n    page.matchPath = &quot;\/app\/*&quot;\n\n    \/\/ Update the page.\n    createPage(page)\n  }\n}\n<\/pre><\/div>\n\n\n<p>Now we need to create an app.js to generate the restricted access pages.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/src\/pages\/app.js\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/pages\/app.js\n\nimport React from &quot;react&quot;\nimport { Router } from &quot;@reach\/router&quot;\nimport Layout from &quot;..\/components\/layout&quot;\nimport Profile from &quot;..\/components\/profile&quot;\nimport Login from &quot;..\/components\/login&quot;\n\nconst App = () =&gt; (\n  &amp;lt;Layout&gt;\n    &amp;lt;Router&gt;\n      &amp;lt;Profile path=&quot;\/app\/profile&quot; \/&gt;\n      &amp;lt;Login path=&quot;\/app\/login&quot; \/&gt;\n    &amp;lt;\/Router&gt;\n  &amp;lt;\/Layout&gt;\n)\n\nexport default App\n<\/pre><\/div>\n\n\n<p>Next, lets create the components for these two routes:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/src\/components\/profile.js\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/profile.js\n\nimport React from &quot;react&quot;\n\nconst Profile = () =&gt; (\n  &amp;lt;&gt;\n    &amp;lt;h1&gt;Your profile&amp;lt;\/h1&gt;\n    &amp;lt;ul&gt;\n      &amp;lt;li&gt;Name: Your name will appear here&amp;lt;\/li&gt;\n      &amp;lt;li&gt;E-mail: And here goes the mail&amp;lt;\/li&gt;\n    &amp;lt;\/ul&gt;\n  &amp;lt;\/&gt;\n)\n\nexport default Profile\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/src\/components\/login.js\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Connecting to Django as the Authentication Service<\/h4>\n\n\n\n<p>This is where we diverge from the Gatsby authentication tutorial in order for us to connect to our Django backend which will handle user authentication leveraging JWT.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/login.js\n\nimport React from &quot;react&quot;\nimport { navigate } from &quot;gatsby&quot;\nimport { useMutation } from &quot;@apollo\/client&quot;\nimport gql from &quot;graphql-tag&quot;\nimport styled from &quot;styled-components&quot;\n\n\/\/ Request token from Django JWT\nconst LOGIN_MUTATION = gql`\n  mutation tokenAuth($username: String!, $password: String!) {\n    tokenAuth(username: $username, password: $password) {\n      token\n    }\n  }\n`\n\/*\n * Begin Styled Components\n*\/\nconst FormHeader = styled.div`\n  font-size: 2.0rem;\n`\n\nconst LoginErrorWrapper = styled.div`\n  color: red;\n  padding: 0.2rem;\n  font-size: 1.4rem;\n  font-weight: bold;\n`\nconst FormWrapper = styled.div`\n  div {\n      margin-bottom: 1.0rem;\n  }\n  label {\n      text-align: right;\n      width: 300px;\n  }\n  input {\n      float: right;\n      margin-left: 1.0rem;\n  }\n`\nconst ButtonWrapper = styled.div`\n  width: 100%;\n  text-align: center;\n  button {\n    margin-top: 0.5rem;\n    width: 100px;\n    background-color: red;\n    color: white;\n    padding: 0.2rem 1.0rem;\n    font-weight: bold;\n    cursor: pointer;\n  }\n`\n\nconst Login = (props) =&gt; {\n  let username = ''\n  let password = ''\n\n  const &#x5B;tokenAuth, { data, error }] = useMutation(LOGIN_MUTATION, {\n\n    onCompleted({ tokenAuth }) {\n      let jwtToken = &quot;JWT&quot; + tokenAuth.token\n\n      localStorage.setItem(&quot;token&quot;, jwtToken)\n      navigate(`\/app\/profile`)\n    }\n  })\n\n  let loginError = ''\n  if (error) {\n    loginError = 'Incorrect credentials'\n  }\n\n  return (\n    &amp;lt;FormWrapper&gt;\n      &amp;lt;LoginErrorWrapper&gt;{loginError}&amp;lt;\/LoginErrorWrapper&gt;\n\n      &amp;lt;FormHeader&gt;Log in&amp;lt;\/FormHeader&gt;\n\n      &amp;lt;form\n        method = &quot;post&quot;\n        onSubmit = {e =&gt; {\n          e.preventDefault()\n          tokenAuth({ variables: {\n            username: username.value,\n            password: password.value,\n          }})\n        }}\n      &gt;\n        &amp;lt;div&gt;\n          &amp;lt;label&gt;\n            Username:\n            &amp;lt;input \n              ref = { node =&gt; {\n                username = node\n              }}\n              type = &quot;text&quot; name=&quot;username&quot;\n            \/&gt; \n          &amp;lt;\/label&gt;\n        &amp;lt;\/div&gt;\n\n        &amp;lt;div&gt;\n          &amp;lt;label&gt;\n            Password:\n            &amp;lt;input \n              ref = { node =&gt; {\n                password = node\n              }}\n              type = &quot;password&quot;\n              name = &quot;password&quot;\n            \/&gt;\n          &amp;lt;\/label&gt;\n        &amp;lt;\/div&gt;\n\n        &amp;lt;ButtonWrapper&gt;\n          &amp;lt;button type = &quot;submit&quot;&gt;Log in&amp;lt;\/button&gt;\n        &amp;lt;\/ButtonWrapper&gt;\n      &amp;lt;\/form&gt;\n    &amp;lt;\/FormWrapper&gt;\n  )\n}\n\nexport default Login\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Controlling Private Routes<\/h4>\n\n\n\n<p>Next, we wrap our restricted routes inside a PrivateRoute component:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ntouch frontend\/src\/components\/privateRoute.js\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/privateRoute.js\n\nimport React from &quot;react&quot;\nimport { navigate } from &quot;gatsby&quot;\nimport { isLoggedIn } from &quot;..\/services\/auth&quot;\n\nconst PrivateRoute = ({ component: Component, location, ...rest }) =&gt; {\n  if (!isLoggedIn() &amp;amp;&amp;amp; location.pathname !== `\/app\/login`) {\n    navigate(&quot;\/app\/login&quot;)\n    return null\n  }\n\n  return &amp;lt;Component {...rest} \/&gt;\n}\n\nexport default PrivateRoute\n<\/pre><\/div>\n\n\n<p>Refactor the app.js page to use the PrivateRoute component:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [6,13]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/pages\/app.js\n\nimport React from &quot;react&quot;\nimport { Router } from &quot;@reach\/router&quot;\nimport Layout from &quot;..\/components\/layout&quot;\nimport PrivateRoute from &quot;..\/components\/privateRoute&quot;\nimport Profile from &quot;..\/components\/profile&quot;\nimport Login from &quot;..\/components\/login&quot;\n\nconst App = () =&gt; (\n  &amp;lt;Layout&gt;\n    &amp;lt;Router&gt;\n      &amp;lt;PrivateRoute path=&quot;\/app\/profile&quot; component={Profile} \/&gt;\n      &amp;lt;Login path=&quot;\/app\/login&quot; \/&gt;\n    &amp;lt;\/Router&gt;\n  &amp;lt;\/Layout&gt;\n)\n\nexport default App\n<\/pre><\/div>\n\n\n<p>Since we&#8217;ve added the gatsby-node.js file we will need to rebuild our Docker frontend container:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose build frontend\n\n# boot up dev env\ndocker-compose -f docker-compose.yml -f docker-compose-dev.yml up\n\n# if you have a problem booting up the Django server run:\ndocker-compose up -d database\ndocker-compose up -d server\ndocker-compose -f docker-compose.yml -f docker-compose-dev.yml up\n\n# or\nmake dev\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Refining Authentication UI<\/h4>\n\n\n\n<p>You should now be able to hit http:\/\/localhost:8000 and be able to login which should redirect you to the profile page. <\/p>\n\n\n\n<p>Now that we can login we need a way of logging out which is essentially clearing the token from localStorage. So to add a functional logout link to the navbar when the user is logged in,  update the Navbar component with the following additions:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [6,13,14,15,21,22]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/navbar.js\n\nimport { Link } from &quot;gatsby&quot;\nimport React from &quot;react&quot;\nimport styled from &quot;styled-components&quot;\nimport { isLoggedIn, logout } from &quot;..\/services\/auth&quot;\n\nconst Nav = styled.nav`\n  a {\n    padding: 0 1.0rem 0 0;\n  }\n`\nconst handleLogout = () =&gt; {\n  logout()\n}\n\nexport const Navbar = () =&gt; {\n  return(\n    &amp;lt;div&gt;\n      &amp;lt;Nav&gt;\n        &amp;lt;Link to=&quot;\/&quot;&gt;Home&amp;lt;\/Link&gt;\n        {!isLoggedIn() ? &amp;lt;Link to=&quot;\/app\/login&quot;&gt;Login&amp;lt;\/Link&gt; : ''}\n        {isLoggedIn() ? &amp;lt;Link href=&quot;#&quot; onClick={handleLogout}&gt;Logout&amp;lt;\/Link&gt; : ''}\n      &amp;lt;\/Nav&gt;\n    &amp;lt;\/div&gt;\n  )\n}\n\nexport default Navbar\n<\/pre><\/div>\n\n\n<p>If you are not logged in a Log In link should appear in the navbar which will take you to your log in page.<\/p>\n\n\n\n<p>This completes our infrastructure allowing users to authenticate with our Django back end from our Gatsby front end.<\/p>\n\n\n\n<p>What&#8217;s next? For fun, we will leverage the power of styled components to build a fly-in mobile menu in our next chapter.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-gatsby-django-docker-part-2-chapter-12-using-theme-ui\">&lt; Previous:  Chapter 12<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-gatsby-django-docker-part-2-chapter-14-mobile-menu\">Next: Chapter 14 &gt;<\/a><\/div>\n<\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Front End Authentication with Gatsby &amp; JWT Now we are at the point where we want to password protect our Gatsby Todo App. Remember way way back in Part 1 of this tutorial where we implemented JWT authentication for our&#8230; <a class=\"more-link\" href=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/11\/24\/tdd-with-gatsby-django-docker-part-2-chapter-13-authentication\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":277,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,12],"tags":[10,30,32,31],"class_list":["post-1507","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured-tutorial","category-gatsby","tag-gatsby","tag-jwt","tag-private-routing","tag-reach-router"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1507","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1507"}],"version-history":[{"count":63,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1507\/revisions"}],"predecessor-version":[{"id":2554,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1507\/revisions\/2554"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/277"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1507"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}