Skip to content

Introduce support for defining Authorization policies via Configuration #42172

Open
@DamianEdwards

Description

@DamianEdwards

Spin-off of #39855 focusing on addition of top-level authorization configuration API.

Example matching Authorization changes to consider, allowing sharing of policies, etc.:

var builder = WebApplication.CreateBuilder(args);

builder.Authentication.AddJwtBearer();
builder.Authorization.AddPolicy("HasProtectedAccess", policy =>
    policy.RequireClaim("scope", "myapi:protected-access"));

var app = builder.Build();

app.MapGet("/hello", () => "Hello!");

app.MapGet("/hello-protected", () => "Hello, you are authorized to see this!")
    .RequireAuthorization("HasProtectedAccess");

app.MapGet("/hello-also-protected", () => "Hello, you authorized to see this to!")
    .RequireAuthorization("HasProtectedAccess");

app.Run();

The WebApplicationBuilder.Authorization property is typed as AuthorizationOptions allowing simple creation of policies and configuration of the default and fallback policies:

builder.Authorization.AddPolicy("HasProtectedAccess", policy => policy.RequireClaim("scope", "myapi:protected-access"));
builder.Authorization.DefaultPolicy = builder.Authorization.GetPolicy("HasProtectedAccess");

// Consider new methods to enable easily setting default/fallback policies by name
builder.Authorization.SetDefaultPolicy("HasProtectedAccess");
builder.Authorization.SetFallbackPolicy("HasProtectedAccess");

The WebApplicationBuilder would register an IConfigureOptions<AuthorizationOptions> in the services collection with a delegate that applies the settings.

Note this suggestion has a fundamental issue in that the AuthorizationOptions isn't designed to be mutated in this way, rather it should be configured via a callback registered in DI so that it runs at the appropriate time during app startup and composes with other code that wishes to configure it.

Perhaps instead the Authentication property should also read from configuration for authorization settings, and the Authorization property would be a new type that simply provides easy access to adding a configuration delegate, e.g.:

{
  "Authorization": {
    "DefaultPolicy": "HasProtectedAccess",
    "FallbackPolicy": "",
    "InvokeHandlersAfterFailure": true,
    "Policies": {
      "HasProtectedAccess": {
        "Claims": [
          { "scope" : "myapi:protected-access" }
        ]
      }
    }
  }
}
builder.Authentication.AddJwtBearer();
builder.Authorization.Configure(authz =>
{
    // Following is the code-based equivalent of config above
    authz.AddPolicy("HasProtectedAccess", policy => policy.RequireClaim("scope", "myapi:protected-access"));
    authz.DefaultPolicy = authz.GetPolicy("HasProtectedAccess");
});

Some other potential example policies as defined via configuration:

{
  "Authorization": {
    "DefaultPolicy": "HasProtectedAccess",
    "Policies": {
      "AuthenticatedUsers": {
        "AuthenticationRequired": true
      },
      "Employees": {
        "AuthenticationRequired": true,
        "Roles": [ "Employees" ]
      },
      "OnlyHomers": {
        "AuthenticationRequired": true,
        "UserName": "Homer"
      },
      "ApiClients": {
        "AuthenticationRequired": true,
        // Any unrecognized properties are auto-mapped as claims perhaps?
        "scope": [ "myapi:read", "myapi:protected-access" ]
      }
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-authIncludes: Authn, Authz, OAuth, OIDC, Bearerarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-minimal-hosting

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions