Next.js role based access to component

Viewed 24

I'm trying to restrict access to a admin area based on a int flag called isAdmin that is set to either 0 or 1 in my users table.

In the admin component I've made a function to fetch an API route that returns a unique user based on email, which will allow me to pass this parameter from the session to the API route - but it never returns true value

This is the code for the lookup function and how I restrict access in the component

export const getServerSideProps: GetServerSideProps = async () => {
  const dashboards = await prisma.dashboard.findMany({
    orderBy: {
      id: "asc",
    }
  })

  return {
    props: JSON.parse(JSON.stringify({ dashboards })),
  }
}

async function checkAdminUser(email: string) {
  try {
    const result = await fetch(`/api/user/${email}`, {
      method: "GET",
    })
    const user = await result.json()
    if (user.isAdmin == 1) {
      return true
    } else {
      return false
    }
  } catch (error) {
    console.error(error)
  }
}

const Dashboard: React.FC<Props> = (props) => {
  const { data: session, status } = useSession()

  if (!session || !checkAdminUser(session.user?.email)) {
    return (
      <Layout>
        <AccessDenied />
      </Layout>
    )
  }

  return (
    <Layout>
        ..Layout code
    </Layout>
  )
}

I've also tried the checkAdminUser function as a Promise without success. The API route has been checked for valid output

"{"id":1,"image":null,"name":null,"email":"censoredfor@crawlers.com","emailVerified":null,"isAdmin":1,"createdAt":"2022-09-21T07:52:20.263Z","updatedAt":"2022-09-21T10:22:39.024Z"}"

Any tips to get me rolling would be greatly appreciated!

1 Answers

This answer assumes that console.log(user) gives you: {"id":1,"image":null,"name":null,"email":"censoredfor@crawlers.com","emailVerified":null,"isAdmin":1,"createdAt":"2022-09-21T07:52:20.263Z","updatedAt":"2022-09-21T10:22:39.024Z"}

Then you should better handle how you check if the user is an Admin. checkAdminUser returns a promise that is not resolved when you're checking for the value. A better solution would be to use react state to manage access to a specific component:

const Dashboard: React.FC<Props> = (props) => {
  const { data: session, status } = useSession()

  const [isAdmin, setAdmin] = useState(false);

  const checkAdminUser = useCallback(async () => {
    try {
      const result = await fetch(`/api/user/${session?.user?.email}`, {
        method: "GET",
      })
      const user = await result.json()
      if (user.isAdmin == 1) {
        setAdmin(true)
      } else {
        setAdmin(false)
      }
    } catch (error) {
      console.error(error)
    }
  },[session?.user?.email])

  useEffect(() => {
    checkAdminUser()
  },[checkAdminUser])

  if (!session || !isAdmin) {
    return (
      <Layout>
        <AccessDenied />
      </Layout>
    )
  }

  return (
    <Layout>
        ..Layout code
    </Layout>
  )
}

Don't forget to: import {useCallback, useEffect, useState} from 'react'

Related