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 authentication403 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