Usage Guide A comprehensive guide to using NextRoleManager for role-based access control in next.js applications.
The NextRoleManager
is a powerful tool for implementing role-based access control (RBAC) in Next.js applications. It integrates seamlessly with the @ iamjs / core
package to provide robust authorization capabilities. This guide will walk you through setting up, configuring, and using the NextRoleManager
in various scenarios.
Here's a basic setup to get you started with @iamjs/next:
import { Role, Schema } from ' @iamjs/core ' ;
import { NextRoleManager } from ' @iamjs/next ' ;
import { NextApiRequest, NextApiResponse } from ' next ' ;
// Define roles
const userRole = new Role ( {
name : ' user ' ,
config : {
posts : {
base : ' cr--l ' ,
custom : {
like : true ,
comment : true
}
},
profile : {
base : ' crud- ' ,
custom : {
changePassword : true
}
}
}
} );
// Create schema
const schema = new Schema ( {
roles : { user : userRole }
} );
// Initialize NextRoleManager
const roleManager = new NextRoleManager ( {
schema ,
onSuccess : ( req : NextApiRequest , res : NextApiResponse ) => {
res . status ( 200 ) . json ( { message : ' Access granted ' } ) ;
},
onError : ( err : Error , req : NextApiRequest , res : NextApiResponse ) => {
console . error ( ' Authorization error: ' , err ) ;
res . status ( 403 ) . json ( { error : ' Access denied ' } ) ;
}
} );
// Example API route
export default roleManager . check (
( req : NextApiRequest , res : NextApiResponse ) => {
res . status ( 200 ) . json ({ message: ' Protected resource accessed successfully ' });
},
{
resources: ' posts ' ,
actions: [ ' read ' , ' list ' ],
role: ' user '
}
);
This setup demonstrates how to define roles, create a schema, initialize the NextRoleManager, and use it in a Next.js API route.
For more complex scenarios, you might need to dynamically determine the role or perform additional checks:
import { Role, Schema } from ' @iamjs/core ' ;
import { NextRoleManager } from ' @iamjs/next ' ;
import { NextApiRequest, NextApiResponse } from ' next ' ;
// ... (previous role and schema setup)
const roleManager = new NextRoleManager ( { schema } );
// Middleware to fetch user permissions
const withAuth = ( handler ) => async ( req : NextApiRequest , res : NextApiResponse ) => {
// In a real app, you'd fetch this from a database or authentication service
req . user = { id : 1 , role : ' user ' };
req . permissions = userRole . toObject () ;
return handler ( req , res ) ;
} ;
const handler = roleManager . check (
( req : NextApiRequest , res : NextApiResponse ) => {
res . status ( 200 ) . json ( { message : ' Post created successfully ' } ) ;
},
{
resources : ' posts ' ,
actions : [ ' create ' ] ,
strict : true ,
construct : true ,
data : async ( req ) => req . permissions
}
);
export default withAuth (handler);
This advanced example shows how to use middleware for authentication and fetching user permissions, and how to use the construct
and data
options for dynamic role checking.
Customize success and error handling to tailor the behavior of your application:
const roleManager = new NextRoleManager ( {
schema ,
onSuccess : ( req : NextApiRequest , res : NextApiResponse ) => {
console . log ( ' Authorization successful ' ) ;
res . setHeader ( ' X-Auth-Status ' , ' success ' ) ;
res . status ( 200 ) . json ( { message : ' Access granted ' } ) ;
},
onError : ( err : Error , req : NextApiRequest , res : NextApiResponse ) => {
console . error ( ' Authorization error: ' , err ) ;
res . status ( 403 ) . json ( {
error : ' Access denied ' ,
details : err . message
} ) ;
}
} );
Use the onActivity
handler to log or process user activities:
const roleManager = new NextRoleManager ( {
schema ,
onSuccess : ( req , res ) => { /* ... */ },
onError : ( err , req , res ) => { /* ... */ },
async onActivity ( data ) {
console . log ( ' User activity: ' , {
timestamp : new Date () ,
userId : data . req ?. user ?. id ,
role : data . role ,
resources : data . resources ,
actions : data . actions ,
success : data . success
} ) ;
// In a real app, you might save this to a database or send to a logging service
}
} );
@iamjs/next provides full TypeScript support. You can create custom interfaces to extend the Next.js request and response types:
import { NextApiRequest, NextApiResponse } from ' next ' ;
import { Role, Schema } from ' @iamjs/core ' ;
import { NextRoleManager } from ' @iamjs/next ' ;
interface CustomRequest extends NextApiRequest {
user ?: { id : number ; role : string };
permissions ?: any ;
}
interface CustomResponse extends NextApiResponse {}
const roleManager = new NextRoleManager ( {
schema ,
onSuccess : ( req : CustomRequest , res : CustomResponse ) => {
console . log ( ' Authorized user: ' , req . user ?. id ) ;
res . status ( 200 ) . json ( { message : ' Access granted ' } ) ;
},
onError : ( err : Error , req : CustomRequest , res : CustomResponse ) => {
console . error ( ' Authorization failed for user: ' , req . user ?. id ) ;
res . status ( 403 ) . json ( { error : ' Access denied ' } ) ;
},
} );
const handler = roleManager . check < CustomRequest , CustomResponse > (
( req , res ) => {
res . status ( 200 ) . json ( { message : ' Protected resource accessed ' , userId : req . user ?. id } ) ;
},
{
resources : ' protectedResource ' ,
actions : [ ' read ' ] ,
construct : true ,
data : async ( req ) => req . permissions
}
);
export default handler;
For Next.js 13's App Router, which uses the Web Request and Response APIs, you can use the checkFn
method:
import { NextResponse } from ' next/server ' ;
import { roleManager } from ' @/lib/roleManager ' ;
import { getUserPermissions } from ' @/lib/auth ' ;
export async function GET ( request : Request ) {
const authorized = await roleManager . checkFn ( {
resources : ' posts ' ,
actions : [ ' read ' ] ,
strict : true ,
construct : true ,
data : await getUserPermissions ( request ) ;
} );
if ( ! authorized) {
return new NextResponse ( ' Unauthorized ' , { status: 401 });
}
// Fetch and return posts
const posts = await fetchPosts ();
return NextResponse . json (posts);
}
Granular Permissions : Define roles with specific, granular permissions for flexible access control.
Dynamic Role Assignment : Utilize the construct
and data
options for dynamic role assignment based on user data.
Error Handling : Provide clear error messages in your onError
handler for better debugging and user experience.
Logging : Use the onActivity
handler for comprehensive audit trails of authorization attempts.
Performance : Consider caching role data or authorization results for improved performance with complex permission structures.
Security : Always validate and sanitize user input, especially when constructing roles dynamically.
Testing : Implement thorough unit and integration tests for your authorization logic.
Separation of Concerns : Keep your authorization logic separate from your business logic for better maintainability.