Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Blazor authentication and authorization

Authentication and Authorization

Authentication means determining who a particular user is. Authorization means applying rules about what they can do. Blazor contains features for handling both aspects of this.

It worth remembering how the overall goals differ between server-side Blazor and client-side Blazor:

  • Server-side Blazor applications run on the server. As such, correctly-implemented authorization checks are both how you determine which UI options to show (e.g., which menu entries are available to a certain user) and where you actually enforce access rules.
  • Client-side Blazor applications run on the client. As such, authorization is only used as a way of determining what UI options to show (e.g., which menu entries). The actual enforcement of authorization rules must be implemented on whatever backend server your application operates on, since any client-side checks can be modified or bypassed.

Authentication-enabled templates for Server-Side Blazor

If you're creating a new server-side Blazor application, the project template can set up an authentication mechanism for you. During project creation, click on Change under Authentication.

image

This will open a dialog that offers the same set of authentication mechanisms available for other ASP.NET Core projects, i.e.:

The actual mechanism of authenticating the user, i.e., determining their identity using cookies or other information, is the same in Blazor as in any other ASP.NET Core application. So to control and customize any aspect of it, see documentation about authentication in ASP.NET Core.

The result is that your project will track the identity of the logged-in user. You can then apply authorization rules to users as described below.

image

Accessing information about the current user

There are three ways to access information about the current user. Each is useful in different cases.

1. Using <AuthorizeView> to display user information

This is the simplest and most high-level way to access authentication data, and is useful when you only need to display the data, and don't need to use it in procedural logic.

The <AuthorizeView> component exposes a context variable of type AuthenticationState, which you can use to display information about the logged-in user:

<AuthorizeView>
    <h1>Hello, @context.User.Identity.Name!</h1>
    You can only see this content if you're authenticated.
</AuthorizeView>

You can also supply different content to be displayed if the user isn't authenticated:

<AuthorizeView>
    <Authorized>
        <h1>Hello, @context.User.Identity.Name!</h1>
        You can only see this if you're authenticated.
    </Authorized>
    <NotAuthorized>
        You're not logged in.
    </NotAuthorized>
</AuthorizeView>

The content doesn't have to be static HTML. It can include arbitrary items, such as other interactive components.

It's also possible to specify authorization conditions such as roles or policies. This is covered later in this document. If no authorization conditions are specified, then <AuthorizeView> treats all authenticated (logged-in) users as authorized, and unauthenticated (logged-out) users as unauthorized.

Troubleshooting: If you receive an error saying Authorization requires a cascading parameter of type Task. Consider using CascadingAuthenticationState to supply this., then it's likely you didn't create your project using one of the authentication templates. In this case, you need to wrap a <CascadingAuthenticationState> around some part of your UI tree, for example in App.razor as follows:

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Startup).Assembly">
        ...
    </Router>
</CascadingAuthenticationState>

2. Using a cascaded Task<AuthenticationState>

If you need to use authentication state data as part of procedural logic, such as when performing an action triggered by the user, then you should obtain it by receiving a cascaded parameter of type Task<AuthenticationState>.

@page "/"

<button @onclick="@LogUsername">Log username</button>

@code {
    [CascadingParameter] Task<AuthenticationState> authenticationStateTask { get; set; }

    async Task LogUsername()
    {
        var authState = await authenticationStateTask;
        var user = authState.User;
        if (user.Identity.IsAuthenticated)
        {
            // Since the user is a ClaimsPrincipal, you can also enumerate claims,
            // evaluate membership in roles, etc.
            Console.WriteLine($"Hello, {user.Identity.Name}");
        }
        else
        {
            Console.WriteLine("You're not logged in.");
        }
    }
}

Troubleshooting: If you receive a null value for authenticationStateTask, then then it's likely you didn't create your project using one of the authentication templates. In this case, you need to wrap a <CascadingAuthenticationState> around some part of your UI tree, for example in App.razor as follows:

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Startup).Assembly">
        ...
    </Router>
</CascadingAuthenticationState>

The <CascadingAuthenticationState> supplies the Task<AuthenticationState> cascading parameter, which in turn it gets from the underlying AuthenticationStateProvider DI service.

3. Using the AuthenticationStateProvider DI service

This is the lowest-level way to access authentication state. In most cases you won't want to use this directly, as there are more convenient alternatives as described previously. However it's worth understanding that this exists, because it's the underlying feature that supports more high-level alternatives.

The AuthenticationStateProvider is a DI service that can give you the current user's ClaimsPrincipal data.

@page "/"
@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="@LogUsername">Write user info to console</button>

@code {
    async Task LogUsername()
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        if (user.Identity.IsAuthenticated)
        {
            // Since the user is a ClaimsPrincipal, you can also enumerate claims,
            // evaluate membership in roles, etc.
            Console.WriteLine($"Hello, {user.Identity.Name}!");
        }
        else
        {
            Console.WriteLine("You're not logged in.");
        }
    }
}

The main drawback to using AuthenticationStateProvider directly is that your component won't be notified automatically if the underlying authentication state data changes. That's why it's normally preferable to use the cascaded Task<AuthenticationState> instead.

Implementing a custom AuthenticationStateProvider

Server-side Blazor has a built-in AuthenticationStateProvider DI service that obtains authentication state data from ASP.NET Core's server-side HttpContext.User. This is how it integrates with all the existing server-side authentication mechanisms.

For server-side Blazor, it is very unlikely that you should implement a custom AuthenticationStateProvider. The built-in implementation already integrates with ASP.NET Core's built-in authentication mechanisms. If you implement a custom one, you may introduce security vulnerabilities.

The only common scenario for a custom AuthenticationStateProvider is client-side Blazor, because in that case you may want to integrate with any number of external authentication systems independently of your server-side code. Also, in client-side Blazor, authentication only exists to present a convenient UI to well-behaved users - it's not actually the place where security is enforced, since client-side rules can always be bypassed.

So, if you're building a client-side Blazor application, or if your requirements are different in some other way, you could choose to implement your own AuthenticationStateProvider that gets data from some other source. For example,

class FakeAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var identity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, "Some fake user"),
        }, "Fake authentication type");

        var user = new ClaimsPrincipal(identity);
        return Task.FromResult(new AuthenticationState(user));
    }
}

You can register this with DI in your Startup class as follows:

public void ConfigureServices(IServiceCollection services)
{
    // ... other services added here ...

    services.AddScoped<AuthenticationStateProvider, FakeAuthenticationStateProvider>();
}

With this custom AuthenticationStateProvider, all users will now be treated as authenticated with the username Some fake user.

Note that if you want to use <AuthorizeView> or a cascaded parameter of type Task<AuthenticationState>, then you still also need to ensure you have wrapped a <CascadingAuthenticationState> around the relevant part of your UI hierarchy, for example in App.razor.

Notifying about authentication state changes

If you determine that your underlying authentication state data has changed (e.g., because the user logged out, or another user has changed their roles), then your custom authentication state provider can optionally invoke the method NotifyAuthenticationStateChanged on the AuthenticationStateProvider base class. This notifies consumers of the authentication state data (e.g., <AuthorizeView> components) that they need to re-render using the new data.

Authorization

Once you've determined who a user is, you can apply authorization rules to control what they can do.

You could grant or deny access based on:

  • ...whether a user is authenticated (logged in)
  • ...whether a user is in a certain role
  • ...whether a user has a certain claim
  • ...whether a certain policy is satisfied

Each of these concepts is the same as in ASP.NET Core MVC or Razor Pages.

1. Using <AuthorizeView>

The <AuthorizeView> component supports role-based or policy-based authorization.

For role-base authorization, use the Roles parameter. For more information, see documentation about user roles.

<AuthorizeView Roles="admin, superuser">
    You can only see this if you're an admin or superuser.
</AuthorizeView>

For policy-based authorization, use the Policy parameter. For more information, see documentation about policies. This explains how to define authorization policies. These APIs can be used with either server-side or client-side Blazor.

<AuthorizeView Policy="content-editor">
    You can only see this if you satify the "content-editor" policy.
</AuthorizeView>

Note that claims-based authorization is a special case of policy-based authorization. For example, you can define a policy that requires users to have a certain claim.

If neither Roles nor Policy is specified, then <AuthorizeView> uses the default policy, which by default is to treat authenticated users as authorized, and unauthenticated users as unauthorized.

Content displayed during asynchronous authentication

Blazor allows for authentication state to be determined asynchronously, i.e., the underlying AuthenticationStateProvider supplies a Task<AuthenticationState>. The main scenario where this matters is with client-side Blazor, as your app may need to make a request to an external endpoint to request authentication information.

So, what content should <AuthorizeView> display while authentication is in progress? By default, it displays nothing. But if you want, you can specify some content to be displayed during this process:

<AuthorizeView>
    <Authorized>Hello, @context.User.Identity.Name!</Authorized>
    <Authorizing>Please wait...</Authorizing>
</AuthorizeView>

Note that this isn't applicable for server-side Blazor by default, because by default, server-side Blazor always knows the authentication state immediately. As such you can specify Authorizing content if you wish, but it would never be displayed.

2. Using the [Authorize] attribute

Just like you can use [Authorize] with MVC controller or Razor pages, you can also use it with page components.

@page "/"
@attribute [Authorize]

You can only see this if you're logged in.

Important: it's only applicable to use [Authorize] on @page components reached via the router. Authorization is only performed as an aspect of routing, and not for child components rendered within a page. To authorize the display of specific parts within a page, use <AuthorizeView> instead.

Note that you may need to add @using Microsoft.AspNetCore.Authorization either to your page component or to _Imports.razor in order for this to compile.

The [Authorize] attribute also supports role-based or policy-based authorization. For role-based authorization, use the Roles parameter:

@page "/"
@attribute [Authorize(Roles = "admin, superuser")]

You can only see this if you're in the 'admin' or 'superuser' role.

For policy-based authorization, use the Policies parameter:

@page "/"
@attribute [Authorize(Policy = "content-editor")]

You can only see this if you satisfy the 'content-editor' policy.

If neither Roles nor Policy is specified, then [Authorize] uses the default policy, which by default is to treat authenticated users as authorized, and unauthenticated users as unauthorized.

Customizing the display for unauthenticated users

The Router component allows you to specify custom content to be rendered if a user fails an [Authorize] condition, or while asynchronous authentication is in progress. In the default project templates, this can be found in your App.razor file:

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Startup).Assembly">
        <NotFoundContent>
            <p>Sorry, there's nothing at this address.</p>
        </NotFoundContent>
        <NotAuthorizedContent>
            <h1>Sorry</h1>
            <p>You're not authorized to reach this page. You may need to log in as a different user.</p>
        </NotAuthorizedContent>
        <AuthorizingContent>
            <p>Please wait...</p>
        </AuthorizingContent>
    </Router>
</CascadingAuthenticationState>

As always, the content doesn't just have to be static HTML. You can include arbitrary content, such as interactive components.

If no NotAuthorizedContent is specified, then the router uses the following fallback message:

Not authorized.

Known issue: In ASP.NET Core 3.0 Preview 6, it's not possible to specify custom NotAuthorizedContent or AuthorizingContent on the Router component with server-side Blazor (though it does work with client-side Blazor). This will be fixed in the Preview 7 release.

3. Using procedural logic

If you need to check authorization rules as part of procedural logic, you can receive a cascaded parameter of type Task<AuthenticationState>. This can be used to obtain the user's ClaimsPrincipal, which in turn can be combined with other services such as IAuthorizationService to evaluate policies.

@inject IAuthorizationService AuthorizationService

<button @onclick="@DoSomething">Do something important</button>

@functions {
    [CascadingParameter] Task<AuthenticationState> authenticationStateTask { get; set; }

    async Task DoSomething()
    {
        var user = (await authenticationStateTask).User;

        if (user.Identity.IsAuthenticated)
        {
            // Perform some action only available to authenticated (logged-in) users
        }

        if (user.IsInRole("admin"))
        {
            // Perform some action only available to users in the 'admin' role
        }

        if ((await AuthorizationService.AuthorizeAsync(user, "content-editor")).Succeeded)
        {
            // Perform some action only available to users satifying the 'content-editor' policy
        }
    }
}

A reminder about authorization in client-side Blazor

With server-side Blazor, these checks cannot be bypassed, because the code runs on the server. But with client-side Blazor, the checks can be bypassed because all client-side code can be modified by users. The same is true for all client-side application technologies, including JavaScript SPA frameworks or native apps for any operating system.

So, you must always remember to perform authorization checks on your server within any API endpoints accessed by your client-side application.

@shoejosh

This comment has been minimized.

Copy link

shoejosh commented Jun 14, 2019

Thanks for this info @SteveSandersonMS! I ran into one small issue using AuthorizeView for a client-side Blazor app. After creating a custom AuthenticationStateProvider and registering it with DI I was still getting the following error:

System.InvalidOperationException: Cannot provide a value for property 'AuthorizationPolicyProvider' on type 'Microsoft.AspNetCore.Components.AuthorizeView'. There is no registered service of type 'Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider'

I fixed this by adding services.AddAuthorizationCore(); to Startup.ConfigureServices. Not sure if that is the recommended approach, but it seemed to do the trick.

@phinett

This comment has been minimized.

Copy link

phinett commented Jun 14, 2019

Is there a way of instead of it just saying not authorized....but to redirect to a login page by default for all Pages decorated with the [Authorize] attribute (Server Side Blazor)?

@SteveSandersonMS

This comment has been minimized.

Copy link
Owner Author

SteveSandersonMS commented Jun 14, 2019

@phinnett Yes, you could make a component called <RedirectToLogin> whose OnInitAsync checks if they are logged in, and if not, does the redirect. Then put that inside the <NotAuthorizedContent> parameter of your router. We might make a built-in option for this eventually but this would be the way to do it today.

@SteveSandersonMS

This comment has been minimized.

Copy link
Owner Author

SteveSandersonMS commented Jun 14, 2019

I fixed this by adding services.AddAuthorizationCore(); to Startup.ConfigureServices. Not sure if that is the recommended approach, but it seemed to do the trick.

Yes, that's exactly what to do. This should be in the docs.

@beb3704

This comment has been minimized.

Copy link

beb3704 commented Jul 19, 2019

How do you actually login in a user using server side blazor?

Anytime I try to use SignInManager I get the following.

System.InvalidOperationException: 'The response headers cannot be modified because the response has already started.'

@mihaimyh

This comment has been minimized.

Copy link

mihaimyh commented Jul 19, 2019

@phinnett Yes, you could make a component called <RedirectToLogin> whose OnInitAsync checks if they are logged in, and if not, does the redirect. Then put that inside the <NotAuthorizedContent> parameter of your router. We might make a built-in option for this eventually but this would be the way to do it today.

What is the code to do the redirect in Blazor?

@AJB-Sharp

This comment has been minimized.

Copy link

AJB-Sharp commented Jul 23, 2019

Hi I have tried option 2 and 3 using Windows Authentication and continually get the following when trying to access the name of the user:
'user.Identity.Name' threw an exception of type 'System.ObjectDisposedException'
Looking at other posts it suggests that I should clone the claims, which I do not know where or how to do. Does this only work if you do not have Windows Authentication?

@LooWooL

This comment has been minimized.

Copy link

LooWooL commented Aug 5, 2019

Can you please provide an example of a Server-Side Blazor with custom AuthenticationStateProvider. As we're porting an existing ASP.NET web forms app to Server-Side Blazor, and keeping the user database and authentication in our database for compatibility with existing users, as we need to authenticate against our existing SQL database that handles already user and role management.

@henkla

This comment has been minimized.

Copy link

henkla commented Aug 28, 2019

Blazor server-side, preview 8, configured with Windows Authentication. I am trying to get a Policy based Authorization working. I have a policy set up and registered in ConfigureServices:

services.AddAuthorization(options => { options.AddPolicy("User", policy => policy.Requirements.Add(new UserRequirement())); options.AddPolicy("Admin", policy => policy.Requirements.Add(new AdminRequirement())); });

I also have handlers registered:

services.AddSingleton<IAuthorizationHandler, UserHandler>(); services.AddSingleton<IAuthorizationHandler, AdminHandler>();

In ConfigureServices, I also have this set:

services.Configure<IISOptions>(options => options.AutomaticAuthentication = true); services.AddAuthenticationCore(options => { options.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme; options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme; });

In Configure method, I have put at the very top:

app.UseAuthentication(); app.UseAuthorization();

The issue
Whenever I run (in my handler's HandleRequirementAsync method):
context.User.IsInRole("MYDOMAIN\\myuser") to try and set context.Succeed(requirement), the IsInRole(string role) returns false, always.

I have some aspnet core (2.1) Web API's with the exact setup, only they are working as intended - i.e. IsInRole("MYDOMAIN\\myuser") returns true when it is supposed to.

What am I missing?

@Ignlek

This comment has been minimized.

Copy link

Ignlek commented Sep 3, 2019

Hello, I have a question how to make some pages to allow anonymous? I am using Azure Ad authentication as I read there is @attribute [AllowAnonymous] but i doesn`t do anything as soon as I start my application and go to Microsoft login form pops up, how to make to call this form after log in button clicked?

@tespheakdeyesoftix

This comment has been minimized.

Copy link

tespheakdeyesoftix commented Sep 3, 2019

Hello, I am new to this. How can I get user name from current login user in Model Class? I use server side project and create several classes that have created_by property. I want to set default value of created_by property to current login user name.

Can you give me example how to do it?

@SteveSandersonMS

This comment has been minimized.

Copy link
Owner Author

SteveSandersonMS commented Sep 4, 2019

For people posting questions, welcome, it's great to see you!

Please note that I don't generally monitor comments posted on gists, so it's not a good way to get support. For Blazor usage questions and community support, I'd recommend posting on StackOverflow or the Blazor gitter channel. For bug reports or feature requests, you can file issues on the ASP.NET Core repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.