DEV Community

Eric
Eric

Posted on • Originally published at attempts.space

Simple Password Management with React and NextJS

Originally posted on Attempts.space


Here I will show you how you can add simple password management to your React application in minutes using paassword.now.sh. In this article I will be using React and the NextJS framework! I recorded a live stream doing this exact same thing for a personal project of my own, you can see it here

First we are going to create our sign up / log in page by creating a file in the pages directory of our project, something like: pages/sign-up.js. Using NextJS this will now allow you to navigate to the route /sign-up in your browser. In that file we can add our form:

// pages/sign-up.js

export default () => {
    const handleSubmit = async event => {
        event.preventDefault();
    }

    return (
        <>
            <h1>Log In</h1>
            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="Enter email"
                />
                <input
                    type="password"
                    name="password"
                    placeholder="Enter password"
                />
                <button type="submit">Let's go!</button>
            </form>
        </>
    )
}

Now we want to handle the submission of that form to create a new user or login a current user. For that we will need an api route, which I will call /api/user/auth. Here is the structure for that file:

// pages/api/user/auth.js
// A service to connect to Mongo DB
import connectToDb from './services/connect-to-db';
// A Mongoose model for a user that contains an email and a password Id
import User from './models/User';

export default async (req, res) => {
    // Make sure we initiate our database connection
    connectToDb();

    // our plain text email and password from the form
    const { email, password } = req.body;

    // Send a 200 OK
    res.end();
}

To store our email and password we will need to create a fetch request to our api route.

// pages/sign-up.js
import fetch from 'fetch';

export default () => {
    const handleSubmit = async event => {
        event.preventDefault();

        const {
            email: emailElement,
            password: passwordElement
        } = event.target.elements;

        const email = emailElement.value;
        const password = passwordElement.value;

        const response = await fetch('/api/user/auth', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ email, password })
        })

        if (response.ok) {
            // successfully created a new user
            // OR logged in!
        }
    }

    return (
        <>
            <h1>Log In</h1>
            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="Enter email"
                />
                <input
                    type="password"
                    name="password"
                    placeholder="Enter password"
                />
                <button type="submit">Let's go!</button>
            </form>
        </>
    )
}

In that handler we will want to create a new user! First we need to store and encrypt our password in paassword.now.sh. Then we can store the id that paassword returns in our own database to use later to verify password attempts.

// pages/api/user/auth.js
import fetch from 'isomorphic-unfetch';
import connectToDb from './services/connect-to-db';
import User from './models/User';

export default async (req, res) => {
    connectToDb();
    const { email, password } = req.body;

    // Store the password in paassword.now.sh
    const paasswordResponse = await fetch(
        'https://paassword.now.sh/api/create',
        {
            method: 'POST',
            headers: { 'Content-Type': 'application-json' },
            body: JSON.stringify({ pwd: password })
        }
    );

    if (paasswordRresponse.ok) {
        // get the id from the response
        const { id } = await paasswordResponse.json();

        // store the id and the email so we can log in later
        const user = new User({
            email,
            passwordId: id
        });

        await user.save();
    }

    res.end();
}

Paassword uses Airtable to store encrypted strings that are only reference-able by the id that is returned. You can learn more about how it works here and see the open source code here. Storing a secure password is as simple as one request like this:

GitHub logo ericadamski / serverless-password

A simple, secure way to create a password checker without needing a server

serverless-password

A simple set of routes for securly storing and testing ad-hoc passwords:

How to use

  1. Go enter the password you want to use here: https://paassword.now.sh

  2. Copy the URL given to you from step 1

  3. Make a POST request to the URL you receive

import fetch from "unfetch"
const response = await fetch("<YOUR_URL>", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ pwd: 'your password attempt' })
})
if (response.ok) {
    const { valid } = await response.json()
    console.log(valid) // true || false
}

Live example

You can run this in a modern browser console.

be careful of CORS 😡

async function validatePassword(pwd) {
    const response = await fetch("https://svrlss.now.sh/api/get/rec3T73O3WNZk3IZj", {
        method: "POST",
await fetch(
    'https://paassword.now.sh/api/create',
    {
        method: 'POST',
        headers: { 'Content-Type': 'application-json' },
        body: JSON.stringify({ pwd: password })
    }
);

That request returns us an id we can then validate a password against. Once it is stored in our database, using MongoDB in the example above, we can then reference by email and compare passwords with our passwordId.

Now if we want to check if someone has logged in, we can:

  1. find their user record by looking up their email
  2. use their passwordId to request a comparison from paassword
// pages/api/user/auth.js
import fetch from 'isomorphic-unfetch';
import connectToDb from './services/connect-to-db';
import User from './models/User';

export default async (req, res) => {
    connectToDb();
    const { email, password } = req.body;

    // Attempt to find a user with that email
    let user = await User.findOne({ email });

    if (user) {
        // We have found a user that matches the input email,
        // now we have to validate that the password entered
        // matches what we originally saved
        const validateResponse = await fetch(
            `https://paassword.now.sh/api/get/${user.passwordId}`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ pwd: password })
            }
        );

        if (validateResponse.ok) {
            const { valid } = await validateResponse.json();

            if (valid) {
                // The passwords match! send a 200 OK
                return res.end();
            }
        }

        // The passwords don't match or there has been
        // a network failure trying to reach paasswords
        // send unauthorized.
        return res.status(401).end();
    }

    const paasswordResponse = await fetch(
        'https://paassword.now.sh/api/create',
        {
            method: 'POST',
            headers: { 'Content-Type': 'application-json' },
            body: JSON.stringify({ pwd: password })
        }
    );

    if (paasswordRresponse.ok) {
        const { id } = await paasswordResponse.json();

        user = new User({
            email,
            passwordId: id
        });

        await user.save();
    }

    res.end();
}

There it is!

A relatively simple way to integrate password authentication in your React app. This does not cover handling front end tokens like JWTs or cookies but they can easily be added on now that verifying passwords is complete. Let me know if you want a more concrete example of this working or want to me write a follow up about JWT and cookies.

Thanks for reading!

Top comments (2)

Collapse
 
yo profile image
Yogi

Getting 405!

Collapse
 
ericadamski profile image
Eric

Oh no! Make sure you are using the POST method. If your code is open source I'd be happy to take a look 😃