Web App Roles - ob1dev/Auth0 GitHub Wiki
The last step to configure the Web App is adding authorization. After Auth0 checks a user identity and returns the token with claims, a user gets authenticated in the app. But what can s\he do inside the app? This is what authorization can answer on.
The idea here is that different users or better to say group of users do different stuff inside the app. For this purpose, you need to define roles. Where each role will grand different access rights to users.
I used a hardcoded list of the roles viewer
, editor
and admin
. And I assigned one of these roles to the following users, which I defined in Auth0.
User | Role |
---|---|
[email protected] | viewer |
[email protected] | editor |
[email protected] | admin |
To keep information which role(s) is assigned to the above users, you will use Auth0 Metadata.
IMPORTANT
You don't have to hardcode users and/or roles. In Auth0 you can dynamically create custom roles and assign them to users using Auth0 Rules or Management API.
Log in to Auth0 Portal, and then go to the section Users at the right menu. Click the button + Create Your First User or + Create User if you have already created some user(s).
After you're done with creating three users, go through each of them and add a custom metadata.
So, click on a user, then switch to the tab Details and scroll down to the section Metadata. Add a new entry roles
and assigned one of the roles (viewer
, editor
and admin
).
Now you have users with assigned roles, but how to push this information back to Web App? The app_metadata
that includes roles
is not a standard claim of the token openid
. Well, the token supports standard claims as well as custom ones. You need Auth0 Rules to add a custom claim to your ID Token.
In the Auth0 Portal, go to the section Rules at the right menu. Click the button _+ Create your First Rule _ or + Create Rule if you have already created some rule(s).
Use the template empty rule
and add the following code.
function (user, context, callback)
{
var metadata = user.app_metadata || {};
console.log("Setting role(s) for user \'" + user.email + "\' (" + metadata.roles + ")");
if (!metadata.roles)
{
console.log("The user \'" + user.email + "\' has no role. Access denied.");
return callback(new UnauthorizedError("No roles defined. Access denied."));
}
context.idToken["https://olegburov.com/roles"] = metadata.roles;
callback(null, user, context);
}
NOTE
Thehttps://olegburov.com/roles
is just a namespace for a custom claim. It could be whatever you want, unless it intersects with existent claim.
IMPORTANT
If something doesn't work the way you expected, use the extensionReal-time Webtask Logs
to debug Auth0 Rules. Click the button Install Real-Time Logs or go the section Extensions and search for it there.
In the file Startup.cs
, modify the method ConfigureServices
as shown below to tell the OpenID Connect middleware, which claim has the roles.
public void ConfigureServices(IServiceCollection services)
{
...
options.TokenValidationParameters = new TokenValidationParameters
{
...
RoleClaimType = "https://olegburov.com/roles"
};
...
}
Create a new view AccessDenied
under the folder Views\Account
, to render it for a user, which isn't authorized or don't have a required role (access right).
@{
ViewData["Title"] = "Access Denied";
}
<h2>Access</h2>
<h4>Denied</h4>
<hr />
<p class="text-danger">You do not have access to this resource.</p>
Modify the class AccountController
under the folder Controllers
, by adding a new action AccessDenied
and decorating the actions Signout
and Profile
with attribute Authorize
. The Authorize will restrict access to these actions unless a user is both authenticated and authorized. In this case, it doesn't matter which roles a user is authorized.
[Route("[controller]/[action]")]
public class AccountController : Controller
{
...
[Authorize]
public async Task Signout()
{
...
}
[Authorize]
public async Task<IActionResult> Profile()
{
...
}
public IActionResult AccessDenied()
{
return View();
}
}
NOTE
For more information about the attributeAuthorize
read here.
Modify the class HomeController
under the folder Controllers
, by decorating the actions with attribute Authorize
. Just like you did before for in the class AccountController
, but this time using specific roles.
public class HomeController : Controller
{
...
[HttpGet]
[Authorize(Roles = "admin, editor")]
public IActionResult New()
{
...
}
[HttpPost]
[Authorize(Roles = "admin, editor")]
public async Task<IActionResult> New(RepositoryModel repository)
{
...
}
[HttpGet]
[Authorize(Roles = "admin, editor")]
public async Task<IActionResult> Edit(Guid id)
{
...
}
[HttpPost]
[Authorize(Roles = "admin, editor")]
public async Task<IActionResult> Edit(RepositoryModel repository)
{
...
}
[Authorize(Roles = "admin")]
public async Task<IActionResult> Delete(Guid id)
{
...
}
...
}
Let's say you want to show some menu items or buttons or simple a piece of HTML to a user with specific role. You still can do this even with Razor Views.
...
@if (!User.Identity.IsAuthenticated)
{
<div class="row">
<div class="col-md-12">
<h2>Please log in to get started</h2>
...
</div>
</div>
}
else
{
...
@if (User.IsInRole("admin") || User.IsInRole("editor"))
{
<a class="btn btn-success" asp-action="New">
<span class="glyphicon glyphicon-book" aria-hidden="true"></span>
New
</a>
}
<table class="table">
...
<td>
<div class="btn-group" role="group" aria-label="Operations">
@if (User.IsInRole("admin") || User.IsInRole("editor"))
{
<a class="btn btn-default" asp-action="Edit" asp-route-id="@item.Id">Edit</a>
}
@if (User.IsInRole("admin"))
{
<a class="btn btn-danger" asp-action="Delete" asp-route-id="@item.Id">Delete</a>
}
</div>
</td>
...
</table>
}
You've now used data to protect pages based on user's role, which Web App obtains from Auth0. In the following tutorial, you'll learn more about accessing a user's related data.