You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
653 lines
20 KiB
653 lines
20 KiB
![]()
2 years ago
|
---
|
||
|
sidebar_position: 3
|
||
|
title: Authentication
|
||
|
---
|
||
|
|
||
|
ASP.NET Core supports configuration and management of security in Blazor apps.
|
||
|
|
||
|
Security scenarios differ between Blazor Server and Blazor WebAssembly apps.
|
||
|
Because Blazor Server apps run on the server, permission checks can determine the following:
|
||
|
|
||
|
* User interface options presented to a user (for example, menu entries available to a user).
|
||
|
* Access rules for application areas and components.
|
||
|
|
||
|
Blazor WebAssembly apps run on the client.
|
||
|
Authorization is only used to determine which UI options to display.
|
||
|
Because client-side controls can be modified or overridden by a user, a Blazor WebAssembly app can't enforce authorization access rules.
|
||
|
|
||
|
For our examples we will use a small example project available [here](/DemoAuthentication.zip).
|
||
|
|
||
|
## Creating a client application
|
||
|
|
||
|
We are going to create a client application to manage local authentication.
|
||
|
|
||
|
Create a new Blazor WASM app.
|
||
|
|
||
|
Install the `Microsoft.AspNetCore.Components.Authorization` package in version 5.0.13.
|
||
|
|
||
|

|
||
|
|
||
|
Or using the Package Manager console: `PM> Install-Package Microsoft.AspNetCore.Components.Authorization -Version 5.0.13`
|
||
|
|
||
|
:::caution
|
||
|
|
||
|
In Blazor WebAssembly apps, authentication checks can be skipped because all client-side code can be modified by users.
|
||
|
This also applies to all client-side application technologies, including JavaScript SPA application frameworks or native applications for any operating system.
|
||
|
|
||
|
:::
|
||
|
|
||
|
## Models
|
||
|
|
||
|
As usual, we need to create the model classes that would take various authentication parameters for login and registration of new users.
|
||
|
|
||
|
We will create these classes in the `Models` folder.
|
||
|
|
||
|
```csharp title="Models/RegisterRequest.cs"
|
||
|
public class RegisterRequest
|
||
|
{
|
||
|
[Required]
|
||
|
public string Password { get; set; }
|
||
|
|
||
|
[Required]
|
||
|
[Compare(nameof(Password), ErrorMessage = "Passwords do not match!")]
|
||
|
public string PasswordConfirm { get; set; }
|
||
|
|
||
|
[Required]
|
||
|
public string UserName { get; set; }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```csharp title="Models/LoginRequest.cs"
|
||
|
public class LoginRequest
|
||
|
{
|
||
|
[Required]
|
||
|
public string Password { get; set; }
|
||
|
|
||
|
[Required]
|
||
|
public string UserName { get; set; }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```csharp title="Models/CurrentUser.cs"
|
||
|
public class CurrentUser
|
||
|
{
|
||
|
public Dictionary<string, string> Claims { get; set; }
|
||
|
public bool IsAuthenticated { get; set; }
|
||
|
public string UserName { get; set; }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```csharp title="Models/AppUser.cs"
|
||
|
public class AppUser
|
||
|
{
|
||
|
public string Password { get; set; }
|
||
|
public List<string> Roles { get; set; }
|
||
|
public string UserName { get; set; }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We now have classes to help persist authentication settings.
|
||
|
|
||
|
## Creating the authentication service
|
||
|
|
||
|
We will now create our authentication service, this one will use a list of users in memory, only the user `Admin` will be defined by default.
|
||
|
|
||
|
Add a new interface in the project.
|
||
|
|
||
|
```csharp title="Services/IAuthService.cs"
|
||
|
public interface IAuthService
|
||
|
{
|
||
|
CurrentUser GetUser(string userName);
|
||
|
|
||
|
void Login(LoginRequest loginRequest);
|
||
|
|
||
|
void Register(RegisterRequest registerRequest);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Let's add a concrete class and implement the previous interface.
|
||
|
|
||
|
```csharp title="Services/AuthService.cs"
|
||
|
public class AuthService : IAuthService
|
||
|
{
|
||
|
private static readonly List<AppUser> CurrentUser;
|
||
|
|
||
|
static AuthService()
|
||
|
{
|
||
|
CurrentUser = new List<AppUser>
|
||
|
{
|
||
|
new AppUser { UserName = "Admin", Password = "123456", Roles = new List<string> { "admin" } }
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public CurrentUser GetUser(string userName)
|
||
|
{
|
||
|
var user = CurrentUser.FirstOrDefault(w => w.UserName == userName);
|
||
|
|
||
|
if (user == null)
|
||
|
{
|
||
|
throw new Exception("User name or password invalid !");
|
||
|
}
|
||
|
|
||
|
var claims = new List<Claim>();
|
||
|
claims.AddRange(user.Roles.Select(s => new Claim(ClaimTypes.Role, s)));
|
||
|
|
||
|
return new CurrentUser
|
||
|
{
|
||
|
IsAuthenticated = true,
|
||
|
UserName = user.UserName,
|
||
|
Claims = claims.ToDictionary(c => c.Type, c => c.Value)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public void Login(LoginRequest loginRequest)
|
||
|
{
|
||
|
var user = CurrentUser.FirstOrDefault(w => w.UserName == loginRequest.UserName && w.Password == loginRequest.Password);
|
||
|
|
||
|
if (user == null)
|
||
|
{
|
||
|
throw new Exception("User name or password invalid !");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Register(RegisterRequest registerRequest)
|
||
|
{
|
||
|
CurrentUser.Add(new AppUser { UserName = registerRequest.UserName, Password = registerRequest.Password, Roles = new List<string> { "guest" } });
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Authentication State Provider
|
||
|
|
||
|
As the name suggests, this class provides the authentication state of the user in Blazor Applications.
|
||
|
`AuthenticationStateProvider` is an abstract class in the `Authorization` namespace.
|
||
|
Blazor uses this class which will be inherited and replaced by us with a custom implementation to get user state.
|
||
|
This state can come from session storage, cookies or local storage as in our case.
|
||
|
|
||
|
Let's start by adding the Provider class to the `Services` folder. Let's call it `CustomStateProvider`. As mentioned, this class will inherit from the `AuthenticationStateProvider` class.
|
||
|
|
||
|
```csharp title="Services/CustomStateProvider.cs"
|
||
|
public class CustomStateProvider : AuthenticationStateProvider
|
||
|
{
|
||
|
private readonly IAuthService _authService;
|
||
|
private CurrentUser _currentUser;
|
||
|
|
||
|
public CustomStateProvider(IAuthService authService)
|
||
|
{
|
||
|
this._authService = authService;
|
||
|
}
|
||
|
|
||
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||
|
{
|
||
|
var identity = new ClaimsIdentity();
|
||
|
try
|
||
|
{
|
||
|
var userInfo = GetCurrentUser();
|
||
|
if (userInfo.IsAuthenticated)
|
||
|
{
|
||
|
var claims = new[] { new Claim(ClaimTypes.Name, _currentUser.UserName) }.Concat(_currentUser.Claims.Select(c => new Claim(c.Key, c.Value)));
|
||
|
identity = new ClaimsIdentity(claims, "Server authentication");
|
||
|
}
|
||
|
}
|
||
|
catch (HttpRequestException ex)
|
||
|
{
|
||
|
Console.WriteLine("Request failed:" + ex);
|
||
|
}
|
||
|
|
||
|
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||
|
}
|
||
|
|
||
|
public async Task Login(LoginRequest loginParameters)
|
||
|
{
|
||
|
_authService.Login(loginParameters);
|
||
|
|
||
|
// No error - Login the user
|
||
|
var user = _authService.GetUser(loginParameters.UserName);
|
||
|
_currentUser = user;
|
||
|
|
||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||
|
}
|
||
|
|
||
|
public async Task Logout()
|
||
|
{
|
||
|
_currentUser = null;
|
||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||
|
}
|
||
|
|
||
|
public async Task Register(RegisterRequest registerParameters)
|
||
|
{
|
||
|
_authService.Register(registerParameters);
|
||
|
|
||
|
// No error - Login the user
|
||
|
var user = _authService.GetUser(registerParameters.UserName);
|
||
|
_currentUser = user;
|
||
|
|
||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||
|
}
|
||
|
|
||
|
private CurrentUser GetCurrentUser()
|
||
|
{
|
||
|
if (_currentUser != null && _currentUser.IsAuthenticated)
|
||
|
{
|
||
|
return _currentUser;
|
||
|
}
|
||
|
|
||
|
return new CurrentUser();
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
You can see that, by default, we manage to implement a `GetAuthenticationStateAsync` function.
|
||
|
Now, this function is quite important because Blazor calls it very often to check the authentication status of the user in the application.
|
||
|
|
||
|
Additionally, we will inject the `AuthService` service and the `CurrentUser` model to the constructor of this class.
|
||
|
The idea behind this is that we will not directly use the service instance in the view (Razor components), rather we will inject the `CustomStateProvider` instance in our views which will in turn access the services.
|
||
|
|
||
|
**Explanation**
|
||
|
|
||
|
`GetAuthenticationStateAsync` - Get the current user from the service object. if the user is authenticated, we will add their claims to a list and create a claims identity. After that, we will return an authentication state with the required data.
|
||
|
|
||
|
The other 3 methods are quite simple. We will simply call the required service methods. But here is one more thing I would like to explain.
|
||
|
Now, with each connection, registration, disconnection, there is technically a change of state in the authentication.
|
||
|
We need to notify the entire application that the user's state has changed.
|
||
|
Therefore, we use a notify method and pass the current authentication state by calling `GetAuthenticationStateAsync`. Pretty logical, huh?
|
||
|
|
||
|
Now, to enable these services and dependencies in the project, we need to register them in the IOC, right?
|
||
|
To do this, navigate to the project's `Program.cs` and make the following additions.
|
||
|
|
||
|
```csharp {6-10} title="Program.cs"
|
||
|
public static async Task Main(string[] args)
|
||
|
{
|
||
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||
|
builder.RootComponents.Add<App>("#app");
|
||
|
|
||
|
builder.Services.AddOptions();
|
||
|
builder.Services.AddAuthorizationCore();
|
||
|
builder.Services.AddScoped<CustomStateProvider>();
|
||
|
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomStateProvider>());
|
||
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||
|
|
||
|
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||
|
|
||
|
await builder.Build().RunAsync();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Now let's work on adding the Razor components for the UI.
|
||
|
Here we are going to add 2 components i.e. Login component and Register component.
|
||
|
We will protect the entire application by making it secure.
|
||
|
This means that only an authenticated user will be allowed to view page data.
|
||
|
Thus, we will have a separate layout component for the login/registration components.
|
||
|
|
||
|
Go to the `Shared` folder of the project.
|
||
|
Here is where you should ideally add all shared Razor components.
|
||
|
In our case, let's add a new Razor component and call it, `AuthLayout.razor`.
|
||
|
|
||
|
```html title="Shared/AuthLayout.razor"
|
||
|
@inherits LayoutComponentBase
|
||
|
<div class="main">
|
||
|
<div class="content px-4">
|
||
|
@Body
|
||
|
</div>
|
||
|
</div>
|
||
|
```
|
||
|
|
||
|
You can see it's a pretty basic html file with an inheritance tag.
|
||
|
We won't focus on css/html.
|
||
|
However, this layout component will act as a container that will hold the login and register component itself.
|
||
|
|
||
|
## Login Component
|
||
|
|
||
|
```html title="Pages/Authentication/Login.razor"
|
||
|
@page "/login"
|
||
|
@layout AuthLayout
|
||
|
|
||
|
<h1 class="h2 font-weight-normal login-title">
|
||
|
Login
|
||
|
</h1>
|
||
|
|
||
|
<EditForm class="form-signin" OnValidSubmit="OnSubmit" Model="loginRequest">
|
||
|
<DataAnnotationsValidator />
|
||
|
|
||
|
<label for="inputUsername" class="sr-only">User Name</label>
|
||
|
<InputText id="inputUsername" class="form-control" @bind-Value="loginRequest.UserName" autofocus placeholder="Username" />
|
||
|
<ValidationMessage For="@(() => loginRequest.UserName)" />
|
||
|
|
||
|
<label for="inputPassword" class="sr-only">Password</label>
|
||
|
<InputText type="password" id="inputPassword" class="form-control" placeholder="Password" @bind-Value="loginRequest.Password" />
|
||
|
<ValidationMessage For="@(() => loginRequest.Password)" />
|
||
|
|
||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||
|
|
||
|
<label class="text-danger">@error</label>
|
||
|
|
||
|
<NavLink href="register">
|
||
|
<h6 class="font-weight-normal text-center">Create account</h6>
|
||
|
</NavLink>
|
||
|
</EditForm>
|
||
|
```
|
||
|
|
||
|
```csharp title="Pages/Authentication/Login.razor.cs"
|
||
|
public partial class Login
|
||
|
{
|
||
|
[Inject]
|
||
|
public CustomStateProvider AuthStateProvider { get; set; }
|
||
|
|
||
|
[Inject]
|
||
|
public NavigationManager NavigationManager { get; set; }
|
||
|
|
||
|
private string error { get; set; }
|
||
|
private LoginRequest loginRequest { get; set; } = new LoginRequest();
|
||
|
|
||
|
private async Task OnSubmit()
|
||
|
{
|
||
|
error = null;
|
||
|
try
|
||
|
{
|
||
|
await AuthStateProvider.Login(loginRequest);
|
||
|
NavigationManager.NavigateTo("");
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
error = ex.Message;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Register Component
|
||
|
|
||
|
```html title="Pages/Authentication/Register.razor"
|
||
|
@page "/register"
|
||
|
@layout AuthLayout
|
||
|
|
||
|
<h1 class="h2 font-weight-normal login-title">
|
||
|
Register
|
||
|
</h1>
|
||
|
|
||
|
<EditForm class="form-signin" OnValidSubmit="OnSubmit" Model="registerRequest">
|
||
|
<DataAnnotationsValidator />
|
||
|
|
||
|
<label for="inputUsername" class="sr-only">User Name</label>
|
||
|
<InputText id="inputUsername" class="form-control" placeholder="Username" autofocus @bind-Value="@registerRequest.UserName" />
|
||
|
<ValidationMessage For="@(() => registerRequest.UserName)" />
|
||
|
|
||
|
<label for="inputPassword" class="sr-only">Password</label>
|
||
|
<InputText type="password" id="inputPassword" class="form-control" placeholder="Password" @bind-Value="@registerRequest.Password" />
|
||
|
<ValidationMessage For="@(() => registerRequest.Password)" />
|
||
|
|
||
|
<label for="inputPasswordConfirm" class="sr-only">Password Confirmation</label>
|
||
|
<InputText type="password" id="inputPasswordConfirm" class="form-control" placeholder="Password Confirmation" @bind-Value="@registerRequest.PasswordConfirm" />
|
||
|
<ValidationMessage For="@(() => registerRequest.PasswordConfirm)" />
|
||
|
|
||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Create account</button>
|
||
|
|
||
|
<label class="text-danger">@error</label>
|
||
|
<NavLink href="login">
|
||
|
<h6 class="font-weight-normal text-center">Already have an account? Click here to login</h6>
|
||
|
</NavLink>
|
||
|
</EditForm>
|
||
|
```
|
||
|
|
||
|
```csharp title="Pages/Authentication/Register.razor.cs"
|
||
|
public partial class Register
|
||
|
{
|
||
|
[Inject]
|
||
|
public CustomStateProvider AuthStateProvider { get; set; }
|
||
|
|
||
|
[Inject]
|
||
|
public NavigationManager NavigationManager { get; set; }
|
||
|
|
||
|
private string error { get; set; }
|
||
|
private RegisterRequest registerRequest { get; set; } = new RegisterRequest();
|
||
|
|
||
|
private async Task OnSubmit()
|
||
|
{
|
||
|
error = null;
|
||
|
try
|
||
|
{
|
||
|
await AuthStateProvider.Register(registerRequest);
|
||
|
NavigationManager.NavigateTo("");
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
error = ex.Message;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Enable authentication
|
||
|
|
||
|
Now we need to let Blazor know that authentication is enabled and we need to pass the `Authorize` attribute throughout the application.
|
||
|
To do this, modify the main component, i.e. the `App.razor` component.
|
||
|
|
||
|
```html title="App.razor"
|
||
|
<Router AppAssembly="@typeof(Program).Assembly">
|
||
|
<Found Context="routeData">
|
||
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||
|
</Found>
|
||
|
<NotFound>
|
||
|
<CascadingAuthenticationState>
|
||
|
<LayoutView Layout="@typeof(MainLayout)">
|
||
|
<p>Sorry, there's nothing at this address.</p>
|
||
|
</LayoutView>
|
||
|
</CascadingAuthenticationState>
|
||
|
</NotFound>
|
||
|
</Router>
|
||
|
```
|
||
|
|
||
|
### What is `AuthorizeRoteView`?
|
||
|
It is a combination of `AuthorizeView` and `RouteView`, so it shows the specific page but only to authorized users.
|
||
|
|
||
|
### What is `CascadingAuthenticationState`?
|
||
|
This provides a cascading parameter to all descendant components.
|
||
|
|
||
|
Let's add a logout button to the top navigation bar of the app to allow the user to logout as well as force redirect to the `Login` page if the user is not logged in.
|
||
|
We will need to make changes to the `MainLayout.razor` component for this.
|
||
|
|
||
|
```html {11} title="Shared/MainLayout.razor"
|
||
|
@inherits LayoutComponentBase
|
||
|
|
||
|
<div class="page">
|
||
|
<div class="sidebar">
|
||
|
<NavMenu />
|
||
|
</div>
|
||
|
|
||
|
<div class="main">
|
||
|
<div class="top-row px-4">
|
||
|
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
|
||
|
<button type="button" class="btn btn-link ml-md-auto" @onclick="@LogoutClick">Logout</button>
|
||
|
</div>
|
||
|
|
||
|
<div class="content px-4">
|
||
|
@Body
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
```
|
||
|
|
||
|
```csharp title="Shared/MainLayout.razor.cs"
|
||
|
public partial class MainLayout
|
||
|
{
|
||
|
[Inject]
|
||
|
public CustomStateProvider AuthStateProvider { get; set; }
|
||
|
|
||
|
[Inject]
|
||
|
public NavigationManager NavigationManager { get; set; }
|
||
|
|
||
|
[CascadingParameter]
|
||
|
private Task<AuthenticationState> AuthenticationState { get; set; }
|
||
|
|
||
|
protected override async Task OnParametersSetAsync()
|
||
|
{
|
||
|
if (!(await AuthenticationState).User.Identity.IsAuthenticated)
|
||
|
{
|
||
|
NavigationManager.NavigateTo("/login");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async Task LogoutClick()
|
||
|
{
|
||
|
await AuthStateProvider.Logout();
|
||
|
NavigationManager.NavigateTo("/login");
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We want to display our username and these roles on the index page soon after we log in.
|
||
|
Go to `Pages/Index.razor` and make the following changes.
|
||
|
|
||
|
```html title="Pages/Index.razor"
|
||
|
@page "/"
|
||
|
<AuthorizeView>
|
||
|
<Authorized>
|
||
|
<h1>Hello @context.User.Identity.Name !!</h1>
|
||
|
|
||
|
<p>Welcome to Blazor Learner.</p>
|
||
|
|
||
|
<ul>
|
||
|
@foreach (var claim in context.User.Claims)
|
||
|
{
|
||
|
<li>@claim.Type: @claim.Value</li>
|
||
|
}
|
||
|
</ul>
|
||
|
|
||
|
</Authorized>
|
||
|
<Authorizing>
|
||
|
<h1>Loading ...</h1>
|
||
|
</Authorizing>
|
||
|
<NotAuthorized>
|
||
|
<h1>Authentication Failure!</h1>
|
||
|
<p>You're not signed in.</p>
|
||
|
</NotAuthorized>
|
||
|
</AuthorizeView>
|
||
|
```
|
||
|
|
||
|
You can see there's a bunch of unsolvable errors that probably indicate few things are undefined in the namespace or something.
|
||
|
This is because we didn't import the new namespaces.
|
||
|
To do this, navigate to `_Import.razor` and add these lines at the bottom.
|
||
|
|
||
|
```html title="_Imports.razor"
|
||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||
|
```
|
||
|
|
||
|
Similarly we want to create an administration page accessible only by users with the `admin` role.
|
||
|
|
||
|
Create the `Pages/Admin.razor` page:
|
||
|
|
||
|
```html title="Pages/Admin.razor"
|
||
|
@page "/admin"
|
||
|
|
||
|
<h3>Admin Page</h3>
|
||
|
```
|
||
|
|
||
|
We are going to put in our menu our new page:
|
||
|
|
||
|
```html {10-16} title="Shared/NavMenu.razor"
|
||
|
...
|
||
|
|
||
|
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
|
||
|
<ul class="nav flex-column">
|
||
|
<li class="nav-item px-3">
|
||
|
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||
|
<span class="oi oi-home" aria-hidden="true"></span> Home
|
||
|
</NavLink>
|
||
|
</li>
|
||
|
<AuthorizeView Roles="admin">
|
||
|
<li class="nav-item px-3">
|
||
|
<NavLink class="nav-link" href="admin" Match="NavLinkMatch.All">
|
||
|
<span class="oi oi-home" aria-hidden="true"></span> Admin page
|
||
|
</NavLink>
|
||
|
</li>
|
||
|
</AuthorizeView>
|
||
|
</ul>
|
||
|
</div>
|
||
|
|
||
|
...
|
||
|
```
|
||
|
|
||
|
## Concept: Composant AuthorizeView
|
||
|
|
||
|
The `AuthorizeView` component displays UI content selectively, depending on whether the user is authorized or not.
|
||
|
This approach is useful when you only need to display user data and don't need to use the user's identity in procedural logic.
|
||
|
|
||
|
The component exposes a context variable of type `AuthenticationState`, which you can use to access information about the logged-in user:
|
||
|
|
||
|
```html
|
||
|
<AuthorizeView>
|
||
|
<h1>Hello, @context.User.Identity.Name!</h1>
|
||
|
<p>You can only see this content if you're authenticated.</p>
|
||
|
</AuthorizeView>
|
||
|
```
|
||
|
|
||
|
You can also provide different content for display if the user is not authorized:
|
||
|
|
||
|
```html
|
||
|
<AuthorizeView>
|
||
|
<Authorized>
|
||
|
<h1>Hello, @context.User.Identity.Name!</h1>
|
||
|
<p>You can only see this content if you're authorized.</p>
|
||
|
<button @onclick="SecureMethod">Authorized Only Button</button>
|
||
|
</Authorized>
|
||
|
<NotAuthorized>
|
||
|
<h1>Authentication Failure!</h1>
|
||
|
<p>You're not signed in.</p>
|
||
|
</NotAuthorized>
|
||
|
</AuthorizeView>
|
||
|
|
||
|
@code {
|
||
|
private void SecureMethod() { ... }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The content of the `<Authorized>` and `<NotAuthorized>` tags can include arbitrary elements, such as other interactive components.
|
||
|
|
||
|
A default event handler for an authorized element, such as the `SecureMethod` method of the `<button>` element in the preceding example, can be called only by an authorized user.
|
||
|
|
||
|
Authorization requirements, such as roles or policies that control user interface or access options, are covered in the `Authorization` section.
|
||
|
|
||
|
If authorization conditions are not specified, `AuthorizeView` uses a default policy and processes:
|
||
|
* Users authenticated (logged in) as authorized.
|
||
|
* Unauthenticated (logged out) users as unauthorized.
|
||
|
|
||
|
## Concept: Authorization based on role and policy
|
||
|
|
||
|
The `AuthorizeView` component supports both role-based and policy-based authorization.
|
||
|
|
||
|
For role-based authorization, use the `Roles` parameter:
|
||
|
|
||
|
```html
|
||
|
<AuthorizeView Roles="admin, superuser">
|
||
|
<p>You can only see this if you're an admin or superuser.</p>
|
||
|
</AuthorizeView>
|
||
|
```
|
||
|
|
||
|
## Concept: Attribut [Authorize]
|
||
|
|
||
|
The [Authorize] attribute can be used in Razor components:
|
||
|
|
||
|
```html
|
||
|
@page "/"
|
||
|
@attribute [Authorize]
|
||
|
|
||
|
You can only see this if you're signed in.
|
||
|
```
|
||
|
|
||
|
:::caution
|
||
|
|
||
|
Only use `[Authorize]` on `@page` components reached through the Blazor router.
|
||
|
Authorization is done only as an aspect of routing and not for child components rendered in a page.
|
||
|
To authorize viewing specific items in a page, use `AuthorizeView` instead.
|
||
|
|
||
|
:::
|
||
|
|
||
|
The [Authorize] attribute also supports role-based or policy-based authorization. For role-based authorization, use the `Roles` parameter:
|
||
|
|
||
|
```html
|
||
|
@page "/"
|
||
|
@attribute [Authorize(Roles = "admin, superuser")]
|
||
|
|
||
|
<p>You can only see this if you're in the 'admin' or 'superuser' role.</p>
|
||
|
```
|