Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trouble with Dapr auth when api versioning and default policy is in place. #1406

Open
WTW-Jacob-Evans opened this issue Nov 15, 2024 · 2 comments
Labels
question Further information is requested

Comments

@WTW-Jacob-Evans
Copy link

Wasn't sure what to post this under, so created a general discussion.

Dapr conflicting with in place authorization policy

When calling in Startup

serviceCollection.AddDaprClient();

serviceCollection.AddAuthentication("Dapr").AddDapr();

serviceCollection.AddAuthorization(options =>
{
    options.AddDapr();
});

and then calling:

endpointBuilder.MapSubscribeHandler()

I would expect the Dapr endpoints to have the authorization "Dapr" policy applied to them (perhaps if the APP_API_TOKEN is in place, as this would indicate that I am trying to use daprs sidecar auth).

This causes conflict when I have a default Bearer AuthN and AuthZ policy applied to my endpoints; the policy I want to apply for my other controllers, like so:

serviceCollection.AddAuthorization(options => options.AddMainAppPermissions());

public static void AddMainAppPermissions(this AuthorizationOptions options)
 {
   var apiDefaultAuthPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
      .RequireAuthenticatedUser()
      .AddRequirements(new HasPermissionRequirement(Permissions.MainAppAccess))
      .Build();

   options.DefaultPolicy = apiDefaultAuthPolicy;
   options.FallbackPolicy = apiDefaultAuthPolicy; 

this is called just before the Dapr AuthZ and AuthN in Startup:

What I observed is the Dapr user is authenticated (although I have observed it not being authenticated), but then falling into the default authPolicy (as I have set it with options.DefaultPolicy);

obviously I cant remove this default policy, as it would mean altering the entire applications permissions for our pubsub component.

I can add:

endpointBuilder.MapSubscribeHandler() .RequireAuthorization("Dapr");
which will cause the dapr/subscribe endpoint to use the policy and authenticate and authorize properly. This will still cause the dapr/config endpoint to fail authorization.

I also decorate my controllers that are dapr topic endpoints with [Authorize("Dapr")]

Additionally, the default "Dapr" auth policy could be exposed by the .NET SDK so that it can be used by the main application.

API Versioning

Another issue is that when dapr tries to execute the mapped endpoint discovered off /dapr/subscribe, it wont include API versioning, which causes the app to log this error:

HTTP POST /main-app-entity-updated responded 200 in 437.8232 ms
[10:12:37 INF] │ Request finished HTTP/1.1 POST https://127.0.0.1:443/main-app-entity-updated - 200 0 null 473.9029ms
[10:12:37 INF] ├ HTTP POST /main-app-entity-updated (475.889 ms)
[10:12:37 INF] │ Request did not specify a service API version, but multiple candidate actions were found. Candidate actions: MainApp.Events.StateStoreEventSubscriber.SectionChangedEventHandler

I assume it is trying to auth against the fallback policy.

I have two work arounds for this currently; either removing the [ApiController] attribute and making it a [Controller], or adding [ApiVersionNeutral]. It would be helpful if there was a way to specify in the dapr sidecar config to dispatch requests with api versioning (ideally in different formats: URL segment, query param, headers etc), however I understand you have a policy of not adapting the runtime to benefit certain implementations, which that may fall under.

This issue seems relevant #977

I did a pretty in-depth look at the docs and SDK source to find answers to my question (hence the default Dapr policy suggestion), but apologies if I've missed something.

@WhitWaldo WhitWaldo added the question Further information is requested label Nov 15, 2024
@WhitWaldo
Copy link
Contributor

@WTW-Jacob-Evans Let me try to address these concerns one at a time starting with authentication and authorization. If you aren't passing options that contain the API token to the extensions, there's not a huge point in using them, especially if they're conflicting with your application. If you do want to use the token and it's conflicting, I'd suggest putting your API token instead in the DAPR_API_TOKEN environment variable as the DaprClient instance will automatically pick it up from there so all calls through the client will contain it.

With 1.15 (releasing in December), this behavior will change ever so slightly in that it'll first read the value for the same key from your IConfiguration, if any, and then from an environment variable.

And with regards to API Versioning, let me research this and get back to you. I'm not aware that there's anything in the existing implementation that's using anything from a versioning perspective, so there's nothing that can be overridden, but if it's proving to be a problem for you, it's something I'd like to look at addressing. I'll give it a look and follow up with any questions in the coming days.

@WTW-Jacob-Evans
Copy link
Author

WTW-Jacob-Evans commented Nov 16, 2024

Thanks for the reply @WhitWaldo. In reference to the authorization; I have the required env vars in place as specified in the documentation; my problem is that even with these, Dapr is still not able to pick up on the authN / AuthZ dapr policy, even after configuring it with the extensions:
Trying to call the Subscribe endpoint on https://localhost:5002/dapr/subscribe, with the header token Dapr-Api-Token set to the token in the env var, but without RequireAuthorization("Dapr"); being called on MapSubscriberHandlers gives:

DenyAnonymousAuthorizationRequirement: Requires an authenticated user. API.Auth.HasPermissionRequirement [21:44:01 INF] │ AuthenticationScheme: Bearer was challenged. [21:44:01 INF] │ Request finished HTTP/2 GET https://localhost:5002/dapr/subscribe - 401 0 null 12.4688ms

Once .RequireAuthorization("Dapr"); has been added in, I am able to get the topic endpoints from the subscribe route, correctly using the Dapr auth policy.

Similarly, on the topic routes themselves; without explicitly decorating the controller with [Authorize("Dapr")] (The internal scheme name that I am suggesting is exposed by the SDK for this purpose), I am also greeted with an auth errors.
DenyAnonymousAuthorizationRequirement: Requires an authenticated user. Api.Auth.HasPermissionRequirement [21:50:20 INF] │ AuthenticationScheme: Bearer was challenged. [21:50:20 INF] │ Request finished HTTP/2 POST https://localhost:5002/entity-updated - 401 0 null 37.4207ms [21:50:20 INF] └─ HTTP POST /entity-updated (38.731 ms)

Once the attribute is in place, Dapr can publish to the topic route correctly.
[22:01:15 INF] │ Request starting HTTP/2 POST https://localhost:5002/entity-updated - application/json 61 [22:01:15 INF] │ Executing endpoint 'API.Events.EntityEventSubscriber.EntityUpdatedEventHandlerAsync [22:01:15 INF] │ Route matched with {action = "EntityUpdatedEventHandler", controller = "EntityEventSubscriber"}. (API) - Validation state: Valid [22:01:16 INF] │ Executed endpoint 'API.Events.EntityEventSubscriber.EntityUpdatedEventHandlerAsync' [22:01:16 INF] │ HTTP POST /entity-updated responded 200 in 635.5640 ms [22:01:16 INF] │ Request finished HTTP/2 POST https://localhost:5002/entity-updated - 200 0 null 667.7928ms [22:01:16 INF] └─ HTTP POST /entity-updated (669.747 ms)

It would seem to me that the Dapr auth policy does not seem to actually get applied directly to the Dapr routes that the SDK establishes, and there is an assumption that this is the only authorization policy in place. If the Dapr auth policy is the only one present then it will of course be applied, but alongside any others it will be overridden (unless of course the Dapr auth policy was applied first and is the default). This goes back to my original suggestion that the auth policy should be applied directly to routes that Dapr is publishing to (although this would prove difficult for topic routes, given these are completely user defined and simply discovered by the Dapr/subscribe route on sidecar startup). Perhaps this is a case for extending the documentation.

Apologies again if I have missed some key piece of config, but this should fully explain the issue I have had.

On the API versioning issue, I have found a few more open issues that discuss this problem:
#1130,
#791,
#882

I think supporting versioning could be incredibly useful, and help integrate Dapr within APIs that have API versioning set up; as you can see in the original post, without specifying it the application will reject the request. This can be hardcoded as [HttpPost("api/v1/topic-name")], but then does not sync with the API version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants