Authentication and authorization overview
DotVVM mostly relies on authentication and authorization mechanisms that are available in ASP.NET Core or OWIN. Every type of authentication and authorization which is available in the ASP.NET platform, is available in DotVVM.
Configure authentication
The configuration of authentication is commonly done in Startup.cs
file, and it is not very different from how it's done in ASP.NET MVC or other frameworks.
Because of the differences between ASP.NET Core and OWIN, different NuGet packages are used:
- In OWIN, the
Microsoft.Owin.Security.*
NuGet packages are used. - In ASP.NET Core, the built-in authentication mechanism is used.
See the ASP.NET Core authentication and OWIN authentication for more info.
Restrict access to viewmodels and their methods
Once the authentication and authorization is configured, you can restrict access to viewmodels and their methods based on the user identity, role memberships, and more.
This restriction is done by calling the Context.Authorize()
method in the Init
stage of the viewmodel, or in the command and static command methods.
Prior to DotVVM 4.0, the
[DotVVM.Framework.Runtime.Filters.Authorize]
attribute was used for this. We've decided to make the attribute obsolete (even though it will remain functional and stay in the framework forever) as its usage was not intuitive in all cases. For example, if you call a method "protected" by this attribute yourself, the attribute is not respected and the method is just called. We wanted to make the authorization explicit.
The Context.Authorize()
method can be called in:
the
Init
method of the viewmodel; the entire page will be accessible only to the authorized usersspecific viewmodel methods which are called from the command binding; the commands will fail for unauthorized users
If you are still using the
[Authorize]
attribute, please note that ASP.NET MVC and other frameworks are also using theAuthorize
attribute, but they have their own implementation. When adding theusing
statement, make sure theAuthorize
attribute comes from theDotVVM.Framework.Runtime.Filters
namespace.
using System;
using System.Threading.Tasks;
using DotvvmWeb.BL.Facades;
using DotVVM.Framework.Runtime.Filters;
namespace DotvvmDemo.ViewModels
{
public class AdminViewModelBase : DotvvmViewModelBase
{
public override async Task Init()
{
await Context.Authorize();
await base.Init(); // always call base.Init() - another authorization checks can be specified in the base page
}
// The page with this viewmodel will return 403 Forbidden if the user is not authenticated.
// No commands to this page will be accepted.
// Note: In most cases, the 403 response will be caught by the authentication middleware,
// and the user will be redirected to the sign in page.
}
}
You can also restrict the access only to specific user roles.
using System;
using System.Threading.Tasks;
using DotVVM.Framework.ViewModel;
using DotVVM.Framework.Runtime.Filters;
namespace DotvvmDemo.ViewModels
{
public class AdminViewModelBase : DotvvmViewModelBase
{
public async Task DeleteUser(int id)
{
await Context.Authorize(roles: new[] { "Admin" });
// Only the users with the Admin role will be able
// to call this method from the command binding.
}
// Please note that if you call the DeleteUser method from your own code, the Authorize attribute will not be checked.
// The attribute is checked **only** if the method is called from a command binding:
// <dot:Button Text="Delete" Click="{command: DeleteUser(Id)}" />
}
}
Note that the Context.Authorize
method has several optional parameters - you can enforce the user to be a member of specific roles, or comply with specific ASP.NET Core Authorization policies, or be authenticated via specified authentication scheme.
If you want the same permission check for all pages, you can place the Authorize
method call to the master page viewmodel. If you override the Init
method in page viewmodels, make sure to properly call await base.Init(context);
so the check is applied.
If you use the same base view model for the login page, you might cause an infinite redirect loop when you call
Context.Authorize
in OnInit. To avoid it, you'll need to suppress the Authorize call, for instance by placing it into a protected virtual method which you can override to do nothing.
Obtaining the context in static commands
You should perform the authorization also in the static commands.
If you declare the static command methods in a static command service, you can use dependency injection to obtain the IDotvvmRequestContext
object. This object contains the Authorize
method.
In case your static command method is static, you need to pass IDotvvmRequestContext
to the method so it can perform the check. You can resolve the context object in the markup file using the @service
directive:
@service context = IDotvvmRequestContext
<dot:Button Text="Call" Click="{staticCommand: MyMethod(context, ...)"} />
[AllowStaticCommand]
public static async Task MyMethod(IDotvvmRequestContext context, ...)
{
await context.Authorize();
...
}
Render different content for authorized users
Often, you want to provide a different UI to the authenticated users, or to the users which belong to specific roles.
DotVVM provides the AuthenticatedView and RoleView controls which can help with this.
The AuthenticatedView
can display different content to authenticated and anonymous users:
<dot:AuthenticatedView>
<AuthenticatedTemplate>
I am authenticated.
</AuthenticatedTemplate>
<NotAuthenticatedTemplate>
I am not authenticated.
</NotAuthenticatedTemplate>
</dot:AuthenticatedView>
The RoleView
can display different content based on the user's role membership:
<dot:RoleView Roles="admin,moderator,tester" HideForAnonymousUsers="false">
<IsMemberTemplate>
I am a member.
</IsMemberTemplate>
<IsNotMemberTemplate>
I am not a member.
</IsNotMemberTemplate>
</dot:RoleView>