tpeczek/Lib.AspNetCore.ServerSentEvents

Authorization policies

Closed this issue · 2 comments

A question... Is it possible to make use of ASP.NET Core authorization policies to restrict an SSE endpoint to authenticated users? I'm struggling to figure out how this could be configured.

This falls under general problem of using authorization policies with middleware (for example if you would want to limit access to some static resources). ASP.NET Core provides direct authorization access only for MVC (AuthorizeAttribute), if you want to protect middleware you need your own code which utilizes IAuthorizationService (similar to view-based authorization). One way to do that is creating another reusable middleware which can validate a policy.

internal class AuthorizationPolicyMiddleware
{
	private readonly RequestDelegate _next;
	private readonly string _policyName;

	public AuthorizationPolicyMiddleware(RequestDelegate next, string policyName)
	{
		_next = next;
		_policyName = policyName;
	}

	public async Task Invoke(HttpContext httpContext, IAuthorizationService authorizationService)
	{
		AuthorizationResult authorizationResult = await authorizationService.AuthorizeAsync(httpContext.User, null, _policyName);
		if (!authorizationResult.Succeeded)
		{
			await httpContext.ChallengeAsync();
			return;
		}

		await _next(httpContext);
	}
}

internal static class AuthorizationApplicationBuilderExtensions
{
	public static IApplicationBuilder UseAuthorizationPolicy(this IApplicationBuilder app, string policyName)
	{
		if (app == null)
		{
			throw new ArgumentNullException(nameof(app));
		}

		if (String.IsNullOrWhiteSpace(policyName))
		{
			throw new ArgumentNullException(nameof(policyName));
		}

		return app.UseMiddleware<AuthorizationPolicyMiddleware>(policyName);
	}
}

With such middleware you can branch the pipeline with Map and place it before the SSE middleware.

public class Startup
{
    ...

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        ...

        app..UseAuthentication();

        …

        app.Map("/sse-notifications-authorized", branchedApp =>
        {
            branchedApp.UseAuthorizationPolicy("PolicyName");
            branchedApp.UseServerSentEvents<NotificationsServerSentEventsService>();
        });

        ...
    }
}

If you want to validate multiple policies you just need to call UseAuthorizationPolicy multiple times.

I think I should add this sample to documentation...

After some thinking I've decided to add authorization support directly, so no additional middleware is necessary. It will look like this:

public class Startup
{
    ...

    public void Configure(IApplicationBuilder app)
    {
        ...

        app.MapServerSentEvents("/default-sse-endpoint", new ServerSentEventsOptions
        {
            Authorization = new ServerSentEventsAuthorization
            {
                Policy = "PolicyName"
            }
        });

        ...
    }
}

It will support same settings as AuthorizeAttribute.