Access Policies
How to apply access policies for Management APIs
A Management API is by default available to any authorized Umbraco backoffice user.
To further restrict access we can apply access policies using the [Authorize] attribute.
Built-in access policies
Umbraco maintains a set of built-in access policies we can leverage in our own APIs. The policy names are defined in Umbraco.Cms.Web.Common.Authorization.AuthorizationPolicies.
For example, the following makes the API accessible only to users with Content section access:
using Umbraco.Cms.Web.Common.Authorization;
...
[Authorize(AuthorizationPolicies.SectionAccessContent)]
public class MyItemApiController : ManagementApiControllerBaseCustom access policies
We can also define our own access policies. Custom access policies are a great way of keeping access control in sync across multiple endpoints, as projects evolve over time.
A custom access policy is defined by means of composition.
The following access policy definition requires the user to be a member of both the Umbraco Administrators group and a custom defined group:
public class SampleCustomPolicyComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
=> builder.Services.AddAuthorization(options =>
options.AddPolicy(SiteConstants.CustomPolicyName, policy =>
{
policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
policy.RequireRole(SiteConstants.CustomGroupAlias);
policy.RequireRole(Constants.Security.AdminGroupAlias);
})
);
}With the policy defined, we can apply it to the API controller:
Restricting access to a backoffice section
The RequireRole approach shown above checks which user groups the current user belongs to. The approach does not check which backoffice sections the user can access.
The built-in SectionAccess policies cover only Umbraco's own sections, such as Content and Media. There is no built-in policy for a custom section alias. To authorize against your own section, read the sections granted to the current user and compare them to the section you want to protect.
Checking section access in a controller
You can check section access directly in a controller. Inject IBackOfficeSecurityAccessor to reach the current user. The AllowedSections property lists the section aliases the user can access.
Add the section alias to your constants:
Reusing the check across controllers
The in-controller check suits a single endpoint. When multiple controllers share the same rule, move the check into a policy. You then apply the policy with [Authorize] rather than repeating the code.
A section check needs to load the current user, so it depends on Umbraco services. Registering a policy happens at startup, where injecting services is awkward. ASP.NET Core avoids the problem by splitting authorization into two parts:
A requirement carries only data - the section to check. You construct it at startup, and it needs no services.
A handler performs the check. Umbraco resolves the handler from the container for each request, so you can inject services into it.
The section lookup belongs in the handler, not in the startup registration.
Defining the authorization requirement
The requirement holds the section aliases to check. Access to any one of them satisfies the requirement.
Handling the requirement
The handler injects IAuthorizationHelper to turn the request's principal into an IUser. It then reads AllowedSections from that user and compares the values to the requirement.
The handler derives from MustSatisfyRequirementAuthorizationHandler, so it always calls Succeed or Fail. An unauthorized caller then receives a 403 Forbidden response. This mirrors the approach Umbraco uses for its own built-in section policies.
Registering the policy
A composer registers the handler in the container and defines the policy. The policy registration adds the authentication scheme and the requirement. The registration does not perform the section lookup, so it needs no services.
Add a policy name to your constants:
Applying the policy
Apply the policy to the API controller in the same way as any other policy:
Accessing user data previously held in claims
The Umbraco 18 change removed three values from the backoffice identity:
The allowed sections,
The content start nodes, and
The media start nodes.
Each value is available from the IUser that you resolve from the current request.
Value removed from the identity
How to read it from IUser
Allowed sections
AllowedSections
Content start nodes
CalculateContentStartNodeIds(entityService, appCaches)
Media start nodes
CalculateMediaStartNodeIds(entityService, appCaches)
The start node methods are extension methods in the Umbraco.Cms.Core.Models namespace. They take an IEntityService and an AppCaches, which you inject into your controller.
Last updated
Was this helpful?