# Razor Pages: authorization conventions

> Razor Pages let us organize code around individual pages, which can make development faster and more focused. In this article, we will configure authorization…

- Author: Jérôme Giacomini
- Language: en
- Canonical URL: [Razor Pages: authorization conventions](https://jeromegiacomini.net/articles/2019/08/22/razor-pages-authorization-conventions)
- Published: 2019-08-22
- Last modified: 2026-09-05
- Topics: ASP.NET Core, C#

[Razor Pages](https://docs.microsoft.com/fr-fr/aspnet/core/razor-pages/?view=aspnetcore-2.2&amp;tabs=visual-studio) let us organize code around individual pages, which can make development faster and more focused. In this article, we will configure authorization conventions that apply one or more [policies](https://docs.microsoft.com/fr-fr/aspnet/core/security/authorization/policies?view=aspnetcore-2.2) across an entire site.

## Authorization convention: what's the point?

Adding the **[Authorize]** attribute to every page is both tedious and risky because it is easy to forget one.

The less repetitive security code you write, the lower the risk of introducing a bug. A central convention gives the site one place to manage authorization.

## And how does that work?

The first step is to create one or more [policies](https://docs.microsoft.com/fr-fr/aspnet/core/security/authorization/policies?view=aspnetcore-2.2) that define the required access level.

### Creating policies

This example creates two claim-based policies, but the same approach works with policies based on **requirements** or other authorization rules.

**Create a constants class containing the policy names:**

```csharp
public sealed class Policies
{
        public const string IsSuperAdmin = "IsSuperAdmin";
        public const string IsAdmin = "IsAdmin ";
}
```

Add the policies through `AddAuthorization` in `ConfigureServices`:

```csharp
services.AddAuthorization(auth =>
{
   // vérification que le claim SuperAdmin existe et que sa valeur = True
   auth.AddPolicy(Policies.IsSuperAdmin, policy => policy.RequireClaim("SuperAdmin", "True"));

   // vérification que le claim Tenant existe
   auth.AddPolicy(Policies.IsTenant, policy => policy.RequireClaim("Tenant"));
});
```

Added conventions:
Also in the ConfigureServices method we will change the default behavior of the AddMvc method by calling the AddRazorPagesOptions method.

```csharp
   services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            // Toutes les pages dans le dossier SuperAdmin "devront" satisfaire la Policy IsSuperAdmin
            options.Conventions.AuthorizeFolder("/SuperAdmin", Policies.IsSuperAdmin);
            // Toutes les pages dans le dossier Tenant "devront" satisfaire la Policy IsTenant
            options.Conventions.AuthorizeFolder("/Tenant", Policies.IsTenant);

            // Toutes les pages dans le dossier Secured "devront" avoir un utilisateur connecté sans Policy particulière
            options.Conventions.AuthorizeFolder("/Secured");

            // Toutes les pages du dossier PublicPages ne requerront pas d'authentification
           options.Conventions.AllowAnonymousToFolder("/PublicPages");
        })
      .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
```

### Combining conventions

You can disable security on a single page as follows:

```csharp
.AuthorizeFolder("/Admin", Policies.IsAdmin).AllowAnonymousToPage("/Admin/Public")
```

However, the reverse will not work:

```csharp
.AllowAnonymousToFolder("/Public").AuthorizePage("/Public/Tenant",Policies.IsTenant)
```

Similarly, this agreement will not work because it will always verify that the user is a Tenant before verifying that the user is a SuperAdmin.

```csharp
.AuthorizeFolder("/Tenant",Policies.IsTenant).AuthorizeFolder("/Admin/SA",, Policies.IsSuperAdmin)
```

However, this kind of behaviour may be desirable, to circumvent this limitation we will write our own convention and not use the “AuthorizeFolder” and AllowAnonymous : methods:

```csharp
options.Conventions.AddFolderApplicationModelConvention("/", model =>
{
    string policy = null;
    // Si la route commence par "Tenant/SA" on choisit d'utiliser la Policy.IsSuperAdmin
    if (model.RouteTemplate.StartsWith("Tenant/SA"))
    {
        policy = Policies.IsSuperAdmin;
    }
    else if (model.RouteTemplate.StartsWith("Tenant"))
    {
       policy = Policies.IsTenant;
    }
    if (policy != null)
    {
         model.Filters.Add(new AuthorizeFilter(policy));
    }
    else
    {
    }
});
```

In a few lines of code we were able to apply all the security rules to our entire website.
Happy coding 🙂
