Using Permissions and Token Verification to Secure Your Site

Article author
Josh Lopez

If you have access to a server or a serverless environment, you can use data fetching libraries like axios to make authenticated requests to your backend. You can use axios in Webflow (with custom code) or in a standard JavaScript environment, like Svelte or Vue.

On your server, you can leverage the Memberstack admin API to access admin specific data from your Memberstack account. Use this API to programmatically create members, retrieve all members, and verify a member’s access tokens.

By combining data-fetching on the client side with our admin API on the server side,

The Member Object

In the Memberstack 2.0 API , members are represented as JSON “objects” with relevant data attached.

The Member object looks like this:

{
  data: {
    id: "mem...",
    auth: {
      email: "john@doe.com"
    }
    customFields: {
      country: "Germany"
    },
    metaData: {
      avatar: "photo.png"
    },
    permissions: ["can:view-members", "is:admin"],
    planConnections: [
      {
        id: "con_...",
        status: "ACTIVE"
        planId: "pln_...",
        type: "FREE",
        payment: null
      },
    ]
  }
}

Notice the permissions field. It's an array of string values based on the permissions that were set on a member manually, or they signed up for a plan. You can do ALOT with this permissions array when it comes to gating content and authorizing requests for secure data.

Using Permissions AND Access Tokens to Secure Backend Requests

If you have access to a backend environment (server, serverless lambdas, edge workers), you can use our memberstack admin package to verify a member’s access token AND control what data get’s returned based on their permissions. Verifying tokens on the server is an industry standard approach to authentication and considered a best practice for authorizing requests.

This example below first verifies the member’s access token and returns a 401 Unauthorized error back to the client if the token is invalid or the member is not found.

const memberstackAdmin = require("@memberstack/admin");

const memberstack = memberstackAdmin.init(process.env.MEMBERSTACK_SECRET_KEY);
const REQUIRED_PERMISSIONS = ["can:view-members"];

async function handler(req, res)  {
  let member
  try {
		// get access token from the _ms-mid cookie in headers
    const token = getCookie(req.headers.get("cookie"), "_ms-mid");
    // verify the token and extract the member id from the jwt payload
    const { id } = await memberstack.verifyToken({ token })
    // use the member id to retrieve the member's info from memberstack
    let { data } = await memberstack.members.retrieve({ id })
    member = data
  } catch (error) {
    console.log(error)
		 // catch errors from above and return a 401 unauthorized error 
    return res.status(401).send("Unauthorized");
  }

....
....

Now here’s is where it gets interesting. You can create your own permission handlers using various array methods*:*

// These are the permissions we require to authorize the request and send back data
const REQUIRED_PERMISSIONS = ['can:view-members', 'is:admin'];

// This function returns true if the member has ALL permissions listed in
// REQUIRED_PERMISSIONS. 
// Note that the member can have additional permissions not listed,
// but they must have ALL of the required ones at least:
const hasRequiredPermissions = (permissions) =>
  REQUIRED_PERMISSIONS.every((permission) => permissions.includes(permission));

// this function returns true if the member has the exact combination of 
// permissions listed in REQUIRED_PERMISSIONS. No more or no less.
const hasExactPermissions = (permissions) =>
permissions.length === REQUIRED_PERMISSIONS.length &&
permissions.every(permission => REQUIRED_PERMISSIONS.includes(permission));

In the example below, we have a list of required permissions, and we’re passing in “member” permissions to the function handlers we just created.

Let’s see these functions in action:

const REQUIRED_PERMISSIONS = ['can:view-members', 'is:admin'];
...
....

console.log(hasRequiredPermissions(['can:view-members', 'is:member']));
// returns false (missing 'is:admin')
console.log(hasExactPermissions(['can:view-members', 'is:admin']));
// return true (has exact combination)

The great thing about the permission handlers is that they return Boolean values (true or false), which help us to conditionally route the flow of logic.

Now, let’s add this to the rest of our code.

...
const REQUIRED_PERMISSIONS = ['can:view-members', "is:admin"];
let permissions = member?.permissions

// if member does not have ALL of required permissions,return 401
  if (!hasExactPermissions(permissions))
    return res.status(401).json({ error: "Unauthorized" });
  
  // if member has required permissions,
  //  return a list of all members in the memberstack account
  const members = await memberstack.members.list()
  return res.status(200).json({ members });
}

The example above demonstrates that this member has admin permissions and they are authorized to retrieve a list of all members. They are required to have the exact combination of permissions to retrieve data from this endpoint.

If you wanted to to loosen restrictions, you could swap out the permission handlers:

const REQUIRED_PERMISSIONS = ['can:read-articles', "is:editor"];
let permissions = member?.permissions

// if member has at least some of required permissions, return data
  if (hasRequiredPermissions(permissions))
		const articles = await myCMS.getAllPosts()
    return res.status(200).json({ articles });
...
...

Choosing The Right Approach

As you can see, there’s a variety of ways you can manage access, gate content and secure your data with Memberstack.

Was this article helpful?

Comments

7 comments

  • Comment author
    Arnau Ros

    how can i check the user token with the rest api? i have problem sending the token in the doc is not clear how to set the params token, I'm receiving error 400 invalid token 
    also I'm not sure i get the right token from the front-end, I'm using xano thanks

    0
  • Comment author
    Duncan from Memberstack

    Josh Lopez Do you have any recommendations for Arnau Ros?

    I'm afraid this is over my head. 

    0
  • Comment author
    Duncan from Memberstack

    Arnau Ros After doing a bit more research it seems JWT verification can only be done with the Node.js version of the Admin Package. It's not possible to verify JWT with the REST version right now. 

     
    0
  • Comment author
    Eric Yen

    What could be the equivalent of that when we are using webflow?

    0
  • Comment author
    Duncan from Memberstack

    Hi Eric, I don't think there is an equivalent when using Webflow. We handle all of the verification for you 👍

    0
  • Comment author
    Deividas Kovger

    Hey, regarding this:

     it seems JWT verification can only be done with the Node.js version of the Admin Package. It's not possible to verify JWT with the REST version right now. 

    Is there still no possibility to verify the JWT with REST?

    0
  • Comment author
    Tyler Bell

    Deividas Kovger

    https://developers.memberstack.com/docs/admin-package/admin-package-rest#verify-member-token

    0

Please sign in to leave a comment.