premieroctet / next-crud

Full-featured CRUD routes for Next.js

Home Page:https://next-crud.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Protecting routes: authentication & authorization

franky47 opened this issue · comments

At the moment, it's possible to use the onRequest callback to implement access control to protect routes.

Authentication and authorization both fall out of the scope of this library, but having a nice and developer-friendly way to implement protected routes would be great.

Blocking access to a route is already possible by throwing HttpError in onRequest, with status codes:

  • 401 Unauthorized for missing or invalid authentication
  • 403 Forbidden for insufficient or invalid authorization

Proposal

In the following example, I'll be using next-auth for both authentication (via JWT) and authorization (via claims in the JWT).

Since onRequest is called right after invoking the internal getRouteType method, its result could be passed to onRequest to help developers switch over the CRUD operations rather than having to parse URLs and HTTP methods:

import NextCrud, {
  HttpError,
  PrismaAdapter,
  RouteType
} from '@premieroctet/next-crud'
import { getSession } from 'next-auth/client'

export default NextCrud({
  resourceName: 'users',
  adapter: new PrismaAdapter({
    modelName: 'user'
  }),
  onRequest: async (req, _res, { routeType, resourceId }: GetRouteType) => {
    //                         ^--- New argument added
    // Read authentication from next-auth
    const session = await getSession({ req })
    switch (routeType) {
      case RouteType.CREATE:
        // No authentication or authorization required, keep going
        break
      case RouteType.READ_ALL:
      case RouteType.READ_ONE:
        // Authentication required
        if (!session) {
          throw new HttpError(401, 'your session has expired')
        }
        break
      case RouteType.UPDATE:
      case RouteType.DELETE:
        // Authentication required
        if (!session) {
          throw new HttpError(401, 'your session has expired')
        }
        // Authorization
        if (
          session.user.id !== resourceId ||
          session.user.roles.include('admin') === false
        ) {
          throw new HttpError(403, 'you cannot modify this user')
        }
        break
    }
  }
})

I agree this would solve the issue of authorization outright on the route which would work well on hiding admin functionality. But another common scenario most apps will need is filtering.

Example

For an email client, you would want to request emails but only for the logged in user.

The proposed solution doesn't allow you to read only your emails without either granting access to all emails or requesting the entire DB and then using a middleware to do the filtering before resolving.

I believe we need a way to mutate the query to safeguard data before anything is requested.

Just made a PR that suits your proposal, feel free to make some feedback