I've faced similar challenges with AngularJS Apps.
Rather than perform an extra service request, I like to pass logged-in status and role in Model or ViewBag
.
For simplicity, let's assume we're using ViewBag
.
1) In the Action Method, set
ViewBag.loggedIn = User.Identity.IsAuthenticated;
ViewBag.userId = User.Identity.GetUserId();
var identity = (System.Security.Claims.ClaimsIdentity)User.Identity;
ViewBag.roles = identity.Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value);
2) In a JS File, globally define UserModel
with bindings and function to check if user has correct role-name:
var UserModel = function () {
var self = this;
self.isLoggedIn = ko.observable("");
self.userId = ko.observable("");
self.roles = ko.observableArray([]);
// function to check if role passed is in array
self.hasRole = function (roleName) {
for (i = 0; 1 < self.roles.length ; i++ ) {
if (self.roles[i] == roleName)
return true
}
return false;
}
};
var UserData = new UserModel();
In .cshtml View:
3) bind Role and Logged in data
<script type="text/javascript">
UserData.isLoggedIn("@ViewBag.isLoggedIn");
UserData.userId("@ViewBag.userId");
UserData.roles(@Html.Raw(Json.Encode(ViewBag.roles));
</script>
4) Show Partial if Role
is correct:
<div data-bind="visible: UserData.hasRole("Admin")>
....
</div>
Ways to hide Admin Components:
The Razor Ways:
Rather than hide components with Knockout, there are C#/Razor ways
-> Nested if clause
@((List<String>) ViewBag.roles.contains("Admin"))
{
...
}
The advantage of Razor Conditions over Knockout is the component will not render when server creates page, leaving no trace for client to see
-> Conditional sections in _Layout
@if ((List<String>) ViewBag.roles.contains("Admin"))
{
@RenderSection("Admin Section")
}
Cleaner than simple Razor Condition and automatically applied throughout application
-> Partial with Child Action
Called (Action, Controller, Parameter)
:
@Html.Action("AdminConsole", "Admin", new { Role = "Admin"})
public ActionResult AdminComponent(string Role)
{
If (List<String>) ViewBag.roles.contains("Admin")
return PartialView(
return View(products);
}
The cleanest of all approaches. Can be combined in a section for greater convenience.
Conclusion on how to hide Admin Components:
How you choose to hide Admin Components depends on your needs. The Razor/C# approach is more convenient and feels more secure.
On the other hand, if you are interested in giving the user the SPA experience, C#
and server methods fall short. Do you allow users to Authenticate without refreshing pages? If so, an Admin may Authenticate and change from anonymous user to Admin.
If you are comfortable with refreshing the screen to show the changed content, C#/Razor
is your approach. If your priority is to give the user the "responsive experience", You will want to implement a Knockout solution.