---
author: Mike Conrad
categories:
- Software Engineering
date: "2024-01-10T20:41:00Z"
tags:
- Blog Post
- GraphQL
- KeystoneJS
- NodeJS
- Prisma
- TypeScript
title: Roll your own authenticator app with KeystoneJS and React - pt 2
---

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’s get started. Make sure you are in the `authenticator` folder and run the following:

```shell
$ yarn create vite@latest
yarn create v1.22.21
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...

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

Scaffolding project in /home/Mike Conrad/projects/authenticator/frontend...

Done. Now run:

  cd frontend
  yarn
  yarn dev

Done in 10.20s.
```

Let’s go ahead and go into our frontend directory and get started:

```shell
$ cd frontend
$ yarn
yarn install v1.22.21
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[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

```

Next go ahead and open the project up in your IDE of choice. I prefer [VSCodium](https://vscodium.com/):

``` shell
codium frontend
```

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

```typescript
import './App.css'

function App() {

  return (
    <>
    </>
  )
}

export default App
```

Let’s start by building a card component that will display an individual token. Our goal is something that looks like this:

![](/images/ente-auth.webp)We will start by creating a Components folder with a Card component:

```shell
$ mkdir src/Components
$ touch src/Components/Card.tsx
```

Let’s go ahead and make a couple updates, we will create this simple card component, add some dummy tokens and some basic styling.

```typescript
// 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[] = [
    {
      account: 'enxoco@github.com',
      issuer: 'Github',
      token: 'AJFDLDAJKFK'
    },
    {
      account: 'Mike Conrad@example.com',
      issuer: 'Example.com',
      token: 'KAJLFDJLKAFD'
    }
  ]
  return (
    <>
      <div className='cardWrapper'>
        {tokens.map(token => <Card token={token} />)}
      </div>
    </>
  )
}

export default App

```

```typescript
// src/Components/Card.tsx
import { IToken } from "../App"

function Card({ token }: { token: IToken }) {
  return (
    <>
      <div className='card'>
        <span>{token.issuer}</span>
        <span>{token.account}</span>
        <span>{token.token}</span>
      </div>
    </>

  )
}
export default Card
```

```typescript
// 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;
}
```

Now you should have something that looks like this:

![](/images/initial-token-frontend.webp)Alright, we have some of the boring stuff out of the way, now let’s start making some magic. If you aren’t familiar with how TOTP tokens work, basically there is an Algorithm that generates them. I would encourage you to read the [RFC](https://datatracker.ietf.org/doc/html/rfc6238) 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.

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 [totp-generator](https://github.com/bellstrand/totp-generator). Let’s go ahead and install it and check it out:

```shell
$ yarn add totp-generator
```

Now let’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 `TokenGenerator` and pass it our Secret key:

```typescript
// src/Components/card.tsx
import { TOTP } from 'totp-generator';
---
function Card({ token }: { token: IToken }) {
  const { otp, expires } = TOTP.generate(token.token)
  return (
    <>
      <div className='card'>
        <span>{token.issuer}</span>
        <span>{token.account}</span>
        <span>{otp} - {expires}</span>
      </div>
    </>
  )
}
```

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

![](/images/token-display-1.png)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.