ExecuteServiceAsync w/ Attribute Auth

So I’m trying to figure out how to “enable” attribute authentication when doing an ExecuteServiceAsync().

When I add attributes to the service model being executed, I can see the attribute’s ctor firing, but the ExecuteAsync() inside the attribute never fires, and I end up on the ServiceInterface even though the incoming request does not have the [RequiredRole()]

Within the initial ServiceInterface POST method I am trying to call another endpoint via the AppHost.ExecuteService:

var response = await HostContext.AppHost.ExecuteServiceAsync(new SomeRequest(), Request).ConfigureAwait(false);

The service model of the request being executed:

[Authenticate]
[RequiredRole("some:role")]
[RequiresAnyRole("some:other:role")]
[Restrict(VisibleInternalOnly = true)]
public class SomeRequest : IReturn<SomeRequestResponse>
{
}

Question: How do I get the ExecuteServiceAsync to actually evaluate the Attribute Authentication like it would if I hit the end-point directly?

The Request Fitler Attributes don’t get executed for In Process Requests, you’d need to validate it in code:

public object Any(SomeRequest request)
{
    var session = this.GetSession(); 
    if (!session.IsAuthenticated)
        throw HttpError.Unauthorized("Not Authenticated");
    if (!session.HasRole(theRole, AuthRepository))
        throw HttpError.Forbidden($$"Requires Role {theRole}");
}

I don’t want to “do it in code” in the service interface, as I want to centralize all auth logic into attributes… i.e. I don’t want have to touch the service interface per say, I want all that code to sit in the attributes so that I can mix and match based on the request and abstract it away from the implementation of the request.

How or where can I override the behavior so that these “In Process” requests will fire the attribute to ensure authentication? OR is there a method available where I can pass in the service model, along with the IRequest and have the assertion take place?

The Request Filter Attributes are only executed for HTTP Requests, where it terminates the Request Pipeline before the Request ever reaches your Service Implementation. By contrast Internal Process Requests only executes the Service Implementation so it by passes the Services Request Filter attributes.

My recommendation would be that you perform any Auth/Roles Validation before you call ExecuteServiceAsync() instead of performing it within the Service so that it mimics the behavior of the Request Filter attributes where validation is performed before the Service is Executed. So instead of calling ExecuteServiceAsync() directly, call your method first which performs any authorization before executing the Service.

To reduce boilerplate you can wrap the boilerplate code within a single extension method, you could also implement the OnBeforeExecute() hook to Intercept all Service Requests for that Service, e.g something like:

class MyServices : Service
{
    public override void OnBeforeExecute(object requestDto)
    {
        if (Request.IsInProcessRequest())
        {
            var op = HostContext.Metadata.GetOperation(requestDto.GetType());
            if (op.RequiresAuthentication || op.RequiredRoles.Count > 0)
            {
                var session = this.GetSession();
                if (!session.IsAuthenticated)
                    throw HttpError.Unauthorized("Not Authenticated");

                foreach (var requiredRole in op.RequiredRoles)
                {
                    if (!session.HasRole(requiredRole, AuthRepository))
                      throw HttpError.Forbidden($"Require Role {requiredRole}");
                }
            }
        }
    }
}

This is perfect. Thank-you.

1 Like