Step 4 - Role-Based Access Control
Setting up roles
The Kickstart from step one created two roles for our Changebank application. These are admin
and user
. It assigned these roles to the two users it created as well. The user admin@example.com
was granted the admin
role and the user richard@example.com
was granted the user
role.
Roles are the way that FusionAuth manages user authorization to applications. And authorization is the process an application determines what a user is allowed to do. Access control is the method that is used to enforce these constraints. A common form of authorization and access controls is Role-Based Access Controls (RBAC).
When a user logs into an application, the user’s roles for that application are included in their access token. Since FusionAuth access tokens are JWTs, it is simple to decode the JWT and analyze the roles a user has been granted.
Let’s take a look at how our example application uses roles to verify the user’s access.
If you open the src/sdk.ts
file and scroll to the userHasAccess
function, you’ll see that the final parameter of this function is an array. This array contains the names of the roles that are allowed to access a specific page in the application. For example, the /account
route calls this method like this:
app.get("/account", async (req, res) => {
if (!await sdk.userHasAccess(req, res, ["admin", "user"])) {
res.redirect(302, '/login');
return;
}
res.sendFile(path.join(__dirname, '../templates/account.html'));
});
This means that the user must have either the admin
or the user
role in order to access the /account
page.
Similarly, the /admin
route makes a similar check like this:
app.get("/admin", async (req, res) => {
if (!await sdk.userHasAccess(req, res, ["admin"])) {
res.redirect(302, '/account');
return;
}
res.sendFile(path.join(__dirname, '../templates/admin.html'));
});
However, this route requires that the user has the admin
role.
The check for the roles is located in the userHasAccess
function of the src/sdk.ts
file. If you jump over to that function, you can see that the function confirms that the user has one of the specified roles like this:
async userHasAccess(req: Request, res: Response, roles: Array<string>): Promise<boolean> {
const jwt = await this.getUser(req, res);
// @ts-ignore
if (!jwt || !jwt.roles || jwt.roles.length === 0) {
return false;
}
// @ts-ignore
return jwt.roles.some(role => roles.includes(role));
}
You can write a similar function to verify roles and in some languages you can leverage security frameworks and libraries as well. The general rule is to ensure that every protected route has an access control check before it renders the response.
Next steps
< Go back to step 3 - Protected page Ready for the next step? Step 5 - Session management >