Tech

Using Sanity.io with React Dashboard Auth Functions

by Dillon RaphaelOct 11 2019

Out of the box, React Dashboard comes with all the necessary serverless functions for authentication. It uses mongo for the database, but what's really cool is using Sanity.io as your data management. It includes it's own query language that's really easy to use, and a whole content management system built in.

Sanity is perfect for static sites built with frameworks like Next.js. We use this tech stack for the landing page and checkout process at https://reactdashboard.com. If we wouldn't use our own product, we wouldn't sell it!

React Dashboard comes with a login & signup page that already sends requests to the appropraite functions url. For this post, I'm only going to show the signup process, and how to create user accounts in Sanity.

First thing, if you haven't already - make sure Sanity is installed on your machine. You can do so by running npm install --g @sanity/cli

Run sanity init to create/login into your sanity account & create a new project.

  • Choose any informal name that suits your project.
  • The dataset is where the data is stored in Sanity. In our case we chose the default (production) value.
  • Set the visibility to Public (world readable).
  • For the output path append a /studio to whereever you want to save the project. In my case it's /Users/dillonraphael/Desktop/testtest/studio. We do this so we can separate the Sanity studio and your static application. Inside your new proejct folder's root, create another directory called web. So your project directory should look like this:

Well get to the "web" directory shortly, but first let's create the User schema inside the studio folder. Inside /studio/schemas/ create a user.js file with the following:

export default {
  name: 'user',
  title: 'User',
  type: 'document',
  fields: [
    {
      name: 'email',
      title: 'Email',
      type: 'string'
    },
    {
      name: 'password',
      title: 'Password',
      type: 'string'
    }
  ]
}

This is a sanity schema, you can get more info from here.

Now inside the /studio directory, run sanity start to start a local server at http://localhost:3333

Traverse back to /web this is where you place your React Dashboard files, with the addition of one file, client.js. This file will handle connecting to your Sanity project from the signup.js function included with React Dashboard.

const sanityClient = require('@sanity/client')

module.exports = sanityClient({
  projectId: 'o157pfj5', // you can find this in sanity.json
  dataset: 'production' // or the name you chose in step 1
})

Make sure to install npm install --save @sanity/client inside the /web directory. Set the projectId to the one that corresponds with your Sanity project. You can find it at https://manage.sanity.io. Set the dataset to production, or whatever you named it earlier.

We won't be using the actual sanity client in this tutorial, as it's only for querying. In sanity there are 2 types of transactions. A query & mutation. The mutation aspect is what writes data to Sanity. At this moment, the only way todo so is with http requests.

Goto https://manage.sanity.io and locate your project's settings page and press API. This is where you will make a new token. When you create the token, it will show only once. So write it down.

Inside the included signup.js function, replace with the following:

// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

require('dotenv').config();

const axios = require('axios')
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const validateHttpMethod = require('../utils/validateHttpMethod');

// ─────────────────────────────────────────────────────────────────────────────
// handler
// ─────────────────────────────────────────────────────────────────────────────

exports.handler = async (event, context) => {
  try {
    // check http method
    await validateHttpMethod(event, ['POST']);

    // parse request payload
    const { email, password } = JSON.parse(event.body);

		const hashedPassword = await bcrypt.hash(password, 10) 

    await axios.post('https://<project id>.api.sanity.io/v1/data/mutate/<dataset name>', { 
       "mutations": [{
         "createIfNotExists": {
           "_id": email.replace(/[^\w\s]/gi, '') + 13371337,
           "_type": "user",
           "email": email,
           "password": hashedPassword
         }
       }]
      }, {
        headers: {
          "Authorization": "Bearer <token from sanity here>"
        }
     })

    // create new user
    const token = await jwt.sign({ sub: email }, process.env.JWT_SECRET, {
      expiresIn: '1h',
    });
    // all went well, respond with JWT token
    return {
      statusCode: 201,
      body: JSON.stringify({token: token})
      headers:    {
        'Cache-Control': 'no-store', // prevent caching of response
        Pragma:          'no-cache', // prevent caching of response
        Authorization:   `Bearer: ${token}`,
      },
    };
  } catch (error) {
    // something went wrong, respond with error
    return {
      statusCode: error.statusCode || 500,
      headers:    error.headers || {},
      body:       JSON.stringify(error.message),
    };
  }
};

A lot is going on here. Let's go through it step by step. Not much is different then the included signup function, except for the axios post requests to Sanity

We make sure to require 3 packages:

const axios = require('axios')
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

Axios for the http requests, bcrypt to hash a password (you don't want to store your users passwords as plain text), and jsonwebtoken to handle creating a jwt token.

We then hash the password the user submits with the signup form

const { email, password } = JSON.parse(event.body);

const hashedPassword = await bcrypt.hash(password, 10)

Now it's time to make a POST request to the sanity api. Replace <project id> with your project id and <dataset name> to whatever you set it as, in our case - "production". There are a few mutations you can use, read about them at https://www.sanity.io/docs/http-api/http-mutations. In our case, we're using createIfNotExists.

For the user's _id we just take the supplied email, strip any special characters and add a few random digits at the end. We then set email to the same email prodived before, and the password to the hashedPassword we created with bcrypt above.

await axios.post('https://<project id>.api.sanity.io/v1/data/mutate/<dataset name>', { 
       "mutations": [{
         "createIfNotExists": {
           "_id": email.replace(/[^\w\s]/gi, '') + 13371337,
           "_type": "user",
           "email": email,
           "password": hashedPassword
         }
       }]
      }, {
        headers: {
          "Authorization": "Bearer <token from sanity here>"
        }
     })

The only way for Sanity to approve this transaction is if you supply the api token you created before. Inside the Authorization headers with your axios request, replace <token from sanity here> with the token you created.

Then we sign a jwt token, and return it when the function completes

const token = await jwt.sign({ sub: email }, process.env.JWT_SECRET, {
      expiresIn: '1h',
    });

return {
      statusCode: 201,
      body: JSON.stringify({token: token})
      headers:    {
        'Cache-Control': 'no-store', // prevent caching of response
        Pragma:          'no-cache', // prevent caching of response
        Authorization:   `Bearer: ${token}`,
      },
    };

If all is good, you should now see the new user in your sanity studio at http://localhost:3333/desk

You can get React Dashboard at https://reactdashboard.com. If you have any questions or need any help, you can tweet me at https://twitter.com/dillonraphael

Recommended Posts

signature

© 2019 Creators Never Die. All Right Reserved. Made with Magic World Wide

About Creators Never Die

Creators Never Die. Is a publication that focuses on content for creators.