Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

<PageTitle>Home</PageTitle>

<h1>Roles Sample App</h1>
<h1>Authorization Sample App</h1>

<AuthorizeView>
<Authorized>
<h2>Policy-based authorization example</h2>
<div class="nav-item px-3">
<NavLink class="nav-link" href="pass-atleast21-policy-with-authorizeview">
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Pass 'AtLeast21' Policy
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Pass 'AtLeast21' policy
</NavLink>
</div>
<h2>Role-based authorization examples</h2>
<div class="nav-item px-3">
<NavLink class="nav-link" href="role-checks-with-authorizeview">
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Role Checks with AuthorizeView
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Role checks with AuthorizeView
</NavLink>
</div>
<div class="nav-item px-3">
Expand Down Expand Up @@ -74,6 +74,28 @@
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Pass 'CustomerServiceMember' and 'HumanResourcesMember' policies with [Authorize] attributes
</NavLink>
</div>
<h2>Policy provider authorization example</h2>
<div class="nav-item px-3">
<NavLink class="nav-link" href="pass-minimumage21-policy">
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Pass 'MinimumAge21' policy
</NavLink>
</div>
<AuthorizeView Policy="MinimumAge21" Context="minimumAge21Context">
<Authorized>
<div class="nav-item px-3">
<NavLink class="nav-link" href="pass-minimumage21-policy-with-attribute">
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> Pass 'MinimumAge21' policy (attribute-based)
</NavLink>
</div>
</Authorized>
<NotAuthorized>
<p>
You can't see this link to the 'PassMinimumAge21PolicyWithAttribute' component because you don't satisfy
the 'MinimumAge21' policy. If you navigate to the page at '/pass-minimumage21-policy-with-attribute' in
the browser's address bar, the Access Denied page is loaded.
</p>
</NotAuthorized>
</AuthorizeView>
</Authorized>
<NotAuthorized>
<p>Sign into the app with one of the seeded accounts. See the README file for accounts and credentials.</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@page "/pass-minimumage21-policy"

<h1>Pass 'MinimumAge21' policy (weakly-typed approach)</h1>

<p>
Uses an AuthorizeView component to apply the policy using the policy's name.
This approach is shown for demonstration purposes and isn't recommended for
production code.
</p>

<AuthorizeView Policy="MinimumAge21">
<Authorized>
<p>You satisfy the 'MinimumAge21' policy.</p>
</Authorized>
<NotAuthorized>
<p>You <b>don't</b> satisfy the 'MinimumAge21' policy.</p>
</NotAuthorized>
</AuthorizeView>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@page "/pass-minimumage21-policy-with-attribute"
@using BlazorWebAppAuthorization.Policies.Attributes
@attribute [MinimumAgeAuthorize(21)]

<h1>Pass 'MinimumAge21' policy (strongly-typed approach)</h1>

<p>
Applies the policy to the Razor component with a custom
[MinimumAgeAuthorize] attribute (derived from AuthorizeAttribute).
This approach is preferred for production code, as it's strongly-typed
and avoids the use of a string to set the policy and minimum age.
</p>

<p>You satisfy the 'MinimumAge21' policy.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Authorization;

namespace BlazorWebAppAuthorization.Policies.Attributes;

public class MinimumAgeAuthorizeAttribute : AuthorizeAttribute
{
private const string PolicyPrefix = "MinimumAge";

public MinimumAgeAuthorizeAttribute(int age) => Age = age;

public int Age
{
get
{
if (!string.IsNullOrEmpty(Policy) &&
Policy.StartsWith(PolicyPrefix,
StringComparison.OrdinalIgnoreCase) &&
int.TryParse(Policy.AsSpan(PolicyPrefix.Length), out var age))
{
return age;
}

return default;
}
Comment thread
guardrex marked this conversation as resolved.
set
{
ArgumentOutOfRangeException.ThrowIfNegative(value);
Policy = $"{PolicyPrefix}{value}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using BlazorWebAppAuthorization.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;

namespace BlazorWebAppAuthorization.Policies.Providers;

public class MinimumAgePolicyProvider(IOptions<AuthorizationOptions> options)
: IAuthorizationPolicyProvider
{
private const string PolicyPrefix = "MinimumAge";

private DefaultAuthorizationPolicyProvider DefaultPolicyProvider { get; } =
new(options);

public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
if (policyName.StartsWith(
PolicyPrefix, StringComparison.OrdinalIgnoreCase) &&
int.TryParse(policyName.AsSpan(PolicyPrefix.Length), out var age) &&
age >= 0)
{
var policy = new AuthorizationPolicyBuilder(
IdentityConstants.ApplicationScheme);
policy.AddRequirements(new MinimumAgeRequirement(age));

return Task.FromResult<AuthorizationPolicy?>(policy.Build());
}

return DefaultPolicyProvider.GetPolicyAsync(policyName);
}

public Task<AuthorizationPolicy> GetDefaultPolicyAsync() =>
DefaultPolicyProvider.GetDefaultPolicyAsync();

public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() =>
DefaultPolicyProvider.GetFallbackPolicyAsync();
}
2 changes: 2 additions & 0 deletions security/authorization/BlazorWebAppAuthorization/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using BlazorWebAppAuthorization.Data;
using BlazorWebAppAuthorization.Identity;
using BlazorWebAppAuthorization.Policies.Handlers;
using BlazorWebAppAuthorization.Policies.Providers;
using BlazorWebAppAuthorization.Policies.Requirements;

var builder = WebApplication.CreateBuilder(args);
Expand Down Expand Up @@ -84,6 +85,7 @@
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();
builder.Services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();

var app = builder.Build();

Expand Down
6 changes: 3 additions & 3 deletions security/authorization/BlazorWebAppAuthorization/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ For more information, see the following resources:
* A `Department` claim with a `Human Resources` value.
* The author of a test resource with an `Id` (Guid) of `aaaabbbb-0000-cccc-1111-dddd2222eeee` (Test Document 1).
* Can perform full CRUD operations on resources in the `AccessDocumentCrud` page.
* Satisfies the 'AtLeast21' policy with a `DateOfBirth` claim with a value of `2000-01-01`.
* Has a `DateOfBirth` claim with a value of `2000-01-01`, which satisfies the 'AtLeast21' and 'MinimumAge21' policies.
* `harry@contoso.com` (Password: `Passw0rd!`)
* The `Admin` role.
* An `EmployeeNumber` claim with a value of `10`.
* A `Department` claim with a `Customer Service` value.
* The author of a test resource with an `Id` (Guid) of `00001111-aaaa-2222-bbbb-3333cccc4444` (Test Document 2).
* Can create, read, and update resources in the `AccessDocumentCrud` page.
* Doesn't satisfy the 'AtLeast21' policy with a `DateOfBirth` claim with a value of `2008-01-01`.
* Has a `DateOfBirth` claim with a value of `2008-01-01`, which doesn't satisfy the 'AtLeast21' and 'MinimumAge21' policies.
* `sarah@contoso.com` (Password: `Passw0rd!`)
* The `SuperUser` role.
* Doesn't have claims as an employee (`EmployeeNumber`) or for a department (`Department`).
* The author of a test resource with an `Id` (Guid) of `11112222-bbbb-3333-cccc-4444dddd5555` (Test Document 3).
* Can delete and read resources in the `AccessDocumentCrud` page.
* Doesn't satisfy the 'AtLeast21' policy with a `DateOfBirth` claim with a value of `2008-01-01`.
* Has a `DateOfBirth` claim with a value of `2008-01-01`, which doesn't satisfy the 'AtLeast21' and 'MinimumAge21' policies.