<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>NodeJS &#8211; hackanooga</title>
	<atom:link href="/tag/nodejs/feed/" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>Confessions of a homelab hacker</description>
	<lastBuildDate>Wed, 13 Mar 2024 12:54:04 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>/wp-content/uploads/2024/03/cropped-cropped-avatar-32x32.png</url>
	<title>NodeJS &#8211; hackanooga</title>
	<link>/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Roll your own authenticator app with KeystoneJS and React &#8211; pt 2</title>
		<link>/roll-your-own-authenticator-app-with-keystonejs-and-react-pt-2/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Thu, 11 Jan 2024 01:41:00 +0000</pubDate>
				<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Blog Post]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[KeystoneJS]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[Prisma]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">/?p=539</guid>

					<description><![CDATA[In part 1 of this series we built out a basic backend using KeystoneJS. In this part we will go ahead and start a new React frontend that will interact with our backend. We will be using Vite. Let&#8217;s get started. Make sure you are in the authenticator folder and run the following: Let&#8217;s go [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In part 1 of this series we built out a basic backend using KeystoneJS.  In this part we will go ahead and start a new React frontend that will interact with our backend.  We will be using Vite.  Let&#8217;s get started.  Make sure you are in the <code>authenticator</code> folder and run the following:</p>



<pre class="wp-block-code"><code>$ yarn create vite@latest
yarn create v1.22.21
&#91;1/4] Resolving packages...
&#91;2/4] Fetching packages...
&#91;3/4] Linking dependencies...
&#91;4/4] Building fresh packages...

success Installed "create-vite@5.2.2" with binaries:
      - create-vite
      - cva
&#x2714; Project name: … frontend
&#x2714; Select a framework: › React
&#x2714; Select a variant: › TypeScript

Scaffolding project in /home/mikeconrad/projects/authenticator/frontend...

Done. Now run:

  cd frontend
  yarn
  yarn dev

Done in 10.20s.</code></pre>



<p>Let&#8217;s go ahead and go into our frontend directory and get started:</p>



<pre class="wp-block-code"><code>$ cd frontend
$ yarn
yarn install v1.22.21
info No lockfile found.
&#91;1/4] Resolving packages...
&#91;2/4] Fetching packages...
&#91;3/4] Linking dependencies...
&#91;4/4] Building fresh packages...
success Saved lockfile.
Done in 10.21s.

$ yarn dev
yarn run v1.22.21
$ vite
Port 5173 is in use, trying another one...

  VITE v5.1.6  ready in 218 ms

  ➜  Local:   http://localhost:5174/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
</code></pre>



<p>Next go ahead and open the project up in your IDE of choice.  I prefer <a href="https://vscodium.com/">VSCodium</a>:</p>



<pre class="wp-block-code"><code>codium frontend</code></pre>



<p>Go ahead and open up <code>src/App.tsx</code> and remove all the boilerplate so it looks like this:</p>



<pre class="wp-block-code"><code>import './App.css'

function App() {

  return (
    &lt;&gt;
    &lt;/&gt;
  )
}

export default App</code></pre>



<p>Let&#8217;s start by building a card component that will display an individual token.  Our goal is something that looks like this:</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="928" height="320" src="/wp-content/uploads/2024/03/ente-auth.webp" alt="" class="wp-image-540" srcset="/wp-content/uploads/2024/03/ente-auth.webp 928w, /wp-content/uploads/2024/03/ente-auth-300x103.webp 300w, /wp-content/uploads/2024/03/ente-auth-768x265.webp 768w" sizes="(max-width: 928px) 100vw, 928px" /></figure>



<p>We will start by creating a Components folder with a Card component:</p>



<pre class="wp-block-code"><code>$ mkdir src/Components
$ touch src/Components/Card.tsx</code></pre>



<p>Let&#8217;s go ahead and make a couple updates, we will create this simple card component, add some dummy tokens and some basic styling.</p>



<pre class="wp-block-code"><code># src/App.tsx

import './App.css'
import Card from './Components/Card';
export interface IToken {
  account: string;
  issuer: string;
  token: string;
}
function App() {

  const tokens: IToken&#91;] = &#91;
    {
      account: 'enxoco@github.com',
      issuer: 'Github',
      token: 'AJFDLDAJKFK'
    },
    {
      account: 'mikeconrad@example.com',
      issuer: 'Example.com',
      token: 'KAJLFDJLKAFD'
    }
  ]
  return (
    &lt;&gt;
      &lt;div className='cardWrapper'&gt;
        {tokens.map(token =&gt; &lt;Card token={token} /&gt;)}
      &lt;/div&gt;
    &lt;/&gt;
  )
}

export default App
</code></pre>



<pre class="wp-block-code"><code># src/Components/Card.tsx
import { IToken } from "../App"

function Card({ token }: { token: IToken }) {
  return (
    &lt;&gt;
      &lt;div className='card'&gt;
        &lt;span&gt;{token.issuer}&lt;/span&gt;
        &lt;span&gt;{token.account}&lt;/span&gt;
        &lt;span&gt;{token.token}&lt;/span&gt;
      &lt;/div&gt;
    &lt;/&gt;

  )
}
export default Card</code></pre>



<pre class="wp-block-code"><code># src/index.css
:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
  background-color: #2c2c2c;

}

.cardWrapper {
  display: flex;
}

.card {
    padding: 2em;
    min-width: 250px;
    border: 1px solid;
    margin: 10px;
    background-color: #333333;
    display: flex;
    flex-direction: column;
    align-items: baseline;
}</code></pre>



<p>Now you should have something that looks like this:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="928" height="320" src="/wp-content/uploads/2024/03/initial-token-frontend.webp" alt="" class="wp-image-541" srcset="/wp-content/uploads/2024/03/initial-token-frontend.webp 928w, /wp-content/uploads/2024/03/initial-token-frontend-300x103.webp 300w, /wp-content/uploads/2024/03/initial-token-frontend-768x265.webp 768w" sizes="(max-width: 928px) 100vw, 928px" /></figure>



<p>Alright, we have some of the boring stuff out of the way, now let&#8217;s start making some magic.  If you aren&#8217;t familiar with how TOTP tokens work, basically there is an Algorithm that generates them.  I would encourage you to read the <a href="https://datatracker.ietf.org/doc/html/rfc6238">RFC</a> for a detailed explanation.  Basically it is an algorithm that generates a one time password using the current time as a source of uniqueness along with the secret key.</p>



<p>If we really wanted to we could implement this algorithm ourselves but thankfully there are some really simple libraries that do it for us.  For our project we will be using one called <code><a href="https://github.com/bellstrand/totp-generator">totp-generator</a></code>.  Let&#8217;s go ahead and install it and check it out:</p>



<pre class="wp-block-code"><code>$ yarn add totp-generator</code></pre>



<p>Now let&#8217;s add it to our card component and see what happens.  Using it is really simple.  We just need to import it, instantiate a new <code>TokenGenerator</code> and pass it our Secret key:</p>



<pre class="wp-block-code"><code># src/Components/card.tsx
import { TOTP } from 'totp-generator';
---
function Card({ token }: { token: IToken }) {
  const { otp, expires } = TOTP.generate(token.token)
  return (
    &lt;>
      &lt;div className='card'>
        &lt;span>{token.issuer}&lt;/span>
        &lt;span>{token.account}&lt;/span>
        &lt;span>{otp} - {expires}&lt;/span>
      &lt;/div>
    &lt;/>
  )
}</code></pre>



<p>Now save and go back to your browser and you should see that our secret keys are now being displayed as tokens:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="928" height="320" src="/wp-content/uploads/2024/03/token-display-1.png" alt="" class="wp-image-542" srcset="/wp-content/uploads/2024/03/token-display-1.png 928w, /wp-content/uploads/2024/03/token-display-1-300x103.png 300w, /wp-content/uploads/2024/03/token-display-1-768x265.png 768w" sizes="(max-width: 928px) 100vw, 928px" /></figure>



<p>That is pretty cool, the only problem is you need to refresh the page to refresh the token.  We will take care of that in part 3 of this series as well as handling fetching tokens from our backend.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Roll your own authenticator app with KeystoneJS and React</title>
		<link>/roll-your-own-authenticator-app-with-keystonejs-and-react/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Thu, 04 Jan 2024 00:59:49 +0000</pubDate>
				<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Blog Post]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[KeystoneJS]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[Prisma]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">/?p=533</guid>

					<description><![CDATA[In this series of articles we are going to be building an authenticator app using KeystoneJS for the backend and React for the frontend. The concept is pretty simple and yes there are a bunch out there already but I recently had a need to learn some of the ins and outs of TOTP tokens [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In this series of articles we are going to be building an authenticator app using KeystoneJS for the backend and React for the frontend.  The concept is pretty simple and yes there are a bunch out there already but I recently had a need to learn some of the ins and outs of TOTP tokens and thought this project would be a fun idea.  Let&#8217;s get started.</p>



<h5 class="wp-block-heading">Step 1: Init keystone app</h5>



<p>Open up a terminal and create a blank keystone project.  We are going to call our app authenticator to keep things simple.</p>



<pre class="wp-block-code"><code>$ yarn create keystone-app
yarn create v1.22.21
&#91;1/4] Resolving packages...
&#91;2/4] Fetching packages...
&#91;3/4] Linking dependencies...
&#91;4/4] Building fresh packages...

success Installed "create-keystone-app@9.0.1" with binaries:
      - create-keystone-app
&#91;###################################################################################################################################################################################] 273/273
&#x2728; You're about to generate a project using Keystone 6 packages.

&#x2714; What directory should create-keystone-app generate your app into? · authenticator

⠸ Installing dependencies with yarn. This may take a few minutes.
&#x26a0; Failed to install with yarn.
&#x2714; Installed dependencies with npm.


&#x1f389;  Keystone created a starter project in: authenticator

  To launch your app, run:

  - cd authenticator
  - npm run dev

  Next steps:

  - Read authenticator/README.md for additional getting started details.
  - Edit authenticator/keystone.ts to customize your app.
  - Open the Admin UI
  - Open the Graphql API
  - Read the docs
  - Star Keystone on GitHub

Done in 84.06s.
</code></pre>



<p>After a few minutes you should be ready to go.  Ignore the error about yarn not being able to install dependencies, it&#8217;s an issue with my setup.  Next go ahead and open up the project folder with your editor of choice.  I use VSCodium:</p>



<pre class="wp-block-code"><code>codium authenticator</code></pre>



<p>Let&#8217;s go ahead and remove all the comments from the <code>schema.ts</code> file and clean it up some:</p>



<pre class="wp-block-code"><code>sed -i '/\/\//d' schema.ts</code></pre>



<p>Also, go ahead and delete the Post and Tag list as we won&#8217;t be using them.  Our cleaned up <code>schema.ts</code> should look like this:</p>



<pre class="wp-block-code"><code>// schema.ts
import { list } from '@keystone-6/core';
import { allowAll } from '@keystone-6/core/access';

import {
  text,
  relationship,
  password,
  timestamp,
  select,
} from '@keystone-6/core/fields';


import type { Lists } from '.keystone/types';

export const lists: Lists = {
  User: list({
    access: allowAll,

    fields: {
      name: text({ validation: { isRequired: true } }),
      email: text({
        validation: { isRequired: true },
        isIndexed: 'unique',
      }),
      password: password({ validation: { isRequired: true } }),
      createdAt: timestamp({
        defaultValue: { kind: 'now' },
      }),
    },
  }),

};
</code></pre>



<p>Next we will define the schema for our tokens.  We will need 3 basic things to start with:</p>



<ul class="wp-block-list">
<li>Issuer</li>



<li>Secret Key</li>



<li>Account</li>
</ul>



<p>The only thing that really matters for generating a TOTP is actually the secret key.  The other two fields are mostly for identifying and differentiating tokens.  Go ahead and add the following to our <code>schema.ts</code> underneath the User list:</p>



<pre class="wp-block-code"><code>  Token: list({
    access: allowAll,
    fields: {
      secretKey: text({ validation: { isRequired: true } }),
      issuer: text({ validation: { isRequired: true }}),
      account: text({ validation: { isRequired: true }})
    }
  }),</code></pre>



<p>Now that we have defined our Token, we should probably link it to a user.  KeystoneJS makes this really easily.  We simply need to add a relationship field to our User list.  Add the following field to the user list:</p>



<pre class="wp-block-code"><code>tokens: relationship({ ref:'Token', many: true })</code></pre>



<p>We are defining a tokens field on the User list and tying it to our Token list.  We are also passing <code>many: true</code> saying that a user can have one or more tokens.  Now that we have the basics set up, let&#8217;s go ahead and spin up our app and see what we have:</p>



<pre class="wp-block-code"><code>$ yarn dev
yarn run v1.22.21
$ keystone dev
&#x2728; Starting Keystone
&#x2b50; Server listening on :3000 (http://localhost:3000/)
&#x2b50; GraphQL API available at /api/graphql
&#x2728; Generating GraphQL and Prisma schemas
&#x2728; The database is already in sync with the Prisma schema
&#x2728; Connecting to the database
&#x2728; Creating server
&#x2705; GraphQL API ready
&#x2728; Generating Admin UI code
&#x2728; Preparing Admin UI app
&#x2705; Admin UI ready
</code></pre>



<p>Our server should be running on localhost:3000 so let&#8217;s check it out!  The first time we open it up we will be greeted with the initialization screen.  Go ahead and create an account to login:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1012" height="806" src="/wp-content/uploads/2024/03/keystone-init-min.webp" alt="" class="wp-image-534" srcset="/wp-content/uploads/2024/03/keystone-init-min.webp 1012w, /wp-content/uploads/2024/03/keystone-init-min-300x239.webp 300w, /wp-content/uploads/2024/03/keystone-init-min-768x612.webp 768w" sizes="(max-width: 1012px) 100vw, 1012px" /></figure>



<p>Once you login you should see a dashboard similar to this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="607" src="/wp-content/uploads/2024/03/keystone-dashboard-min-1024x607.webp" alt="" class="wp-image-535" srcset="/wp-content/uploads/2024/03/keystone-dashboard-min-1024x607.webp 1024w, /wp-content/uploads/2024/03/keystone-dashboard-min-300x178.webp 300w, /wp-content/uploads/2024/03/keystone-dashboard-min-768x455.webp 768w, /wp-content/uploads/2024/03/keystone-dashboard-min.webp 1169w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>You can see we have Users and Tokens that we can manage.  The beauty of KeystoneJS is that you get full CRUD functionality out of the box just by defining our schema!  Go ahead and click on Tokens to add a token:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="572" src="/wp-content/uploads/2024/03/create-token-min-1024x572.webp" alt="" class="wp-image-536" srcset="/wp-content/uploads/2024/03/create-token-min-1024x572.webp 1024w, /wp-content/uploads/2024/03/create-token-min-300x167.webp 300w, /wp-content/uploads/2024/03/create-token-min-768x429.webp 768w, /wp-content/uploads/2024/03/create-token-min.webp 1134w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>For this example I just entered some random text as an example.  This is enough to start testing out our TOTP functionality.  Click &#8216;Create Token&#8217; and you should see a list displaying existing tokens:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="275" src="/wp-content/uploads/2024/03/display-token-dashboard-min-1024x275.webp" alt="" class="wp-image-537" srcset="/wp-content/uploads/2024/03/display-token-dashboard-min-1024x275.webp 1024w, /wp-content/uploads/2024/03/display-token-dashboard-min-300x81.webp 300w, /wp-content/uploads/2024/03/display-token-dashboard-min-768x206.webp 768w, /wp-content/uploads/2024/03/display-token-dashboard-min-1536x413.webp 1536w, /wp-content/uploads/2024/03/display-token-dashboard-min.webp 1786w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>We are now ready to jump into the frontend.  Stay tuned for pt 2 of this series.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
