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

Add middleware for automatic authorization based on request endpoint #55

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

wanderzirkus
Copy link

Hi there.

First things first: Thanks for this repo and the good work! :) This is also my first PR on GitHub ever, so I hope I didn't violate any policies with my PR.

Lets get to the topic: I like using the Keycloak authorization services in a way, that I don't have to recompile if any thing about authorization changes (users, roles, permissions or even policies). More over I want a clean separation of concerns and that for I don't want to annotate the request endpoints in the controllers with authorization specific information (except "AllowAnonymous" of course).
To achieve this, I'd like to have a middleware, which takes path and method of the request, pass them to the "IKeycloakProtectionClient.VerifyAccessToResource()"-method and proceed with the request depending on the result.

What do you think about it?

Please let me know if I should any unittests for this, too.

@NikiforovAll
Copy link
Owner

NikiforovAll commented Feb 29, 2024

@wanderzirkus
First of all, thank you for your contribution. This kind of middleware is useful for specific scenarios as you described. I will think about how to include this kind of contribution to this project, but I don't think it is a good idea to include it in a core due to reason I described above.

We could create something like Keycloak.AuthServices.Contrib project and put it there

@NikiforovAll
Copy link
Owner

This could be interesting, could you please consider adding some attributes that provide metadata regarding Resouce and Scope?

Also, how do we validate based on entity Id? E.g: we have a rule for specific entities to be protected. The default path convention is interesting, but it requires certain conventions on the side of the authorization server, which is not always true. I think we need to be able to adapt C# code

@wanderzirkus
Copy link
Author

Hey thanks for your interest, glad to here that! :)
What exactly do you mean with attributes providing metadata regarding Resource and Scope? Do you mean additional comments?

The idea is that the authorization server has appropriate resources prepared:
Resource uri = request path
Resource scope = request method
If there is a usecase that a specific entity needs to be protected, which dont match this pattern, another attribute analogous to "AllowAnonymous" could be introduced for bypassing the middleware. The name could be "ExplicitAuthorization" for example. The clearity of the automatic authorization concept would suffer a bit from this, I think. Especially at keycloak site.

Let me know if I understood you correctly and what you think about my thoughts.

@NikiforovAll
Copy link
Owner

I would like to suggest you have a quick call to discuss this functionality. Could you please write me an email so we continue discussing time slots?

Copy link
Owner

@NikiforovAll NikiforovAll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR, I left some comments. We might need to have a call to resolve them to save your time

[HttpGet]
[Route("/api/explicit-auth")]
[ProducesResponseType(statusCode: 200, type: typeof(string))]
[ExplicitResourceProtection("/api/auto-auth", "GET")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can rename it to something simple:

ProtectedResource(string resource, string scope)

Copy link
Author

@wanderzirkus wanderzirkus May 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok edit: after giving it a second thought, I don't like ProtectedResource as much, because it implies, that all methods, that dont have the attribute are not protected. Thats why I put in "explicit" as a prefix.

[HttpGet]
[Route("/api/no-auth")]
[ProducesResponseType(statusCode: 200, type: typeof(string))]
[DisableUriBasedResourceProtection]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest not focus on URI based, but on the general one, so it could be [ProtectedResouce(Disable = true)]

Let me know what you think about this design. I like it because you don't need to remember a lot of other attributes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea :)

{
services.AddKeycloakAuthentication(configuration);

services.AddAuthorization(authorizationOptions => authorizationOptions.FallbackPolicy = new AuthorizationPolicyBuilder()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean exactly?

{
public static IServiceCollection AddAuth(this IServiceCollection services, IConfiguration configuration)
{
services.AddKeycloakAuthentication(configuration);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrate to 2.0.0. Basically, you need to rebase on the main branch to see the latest changes. We can setup a call to migrate to 2.0.0 together if needed

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much trouble would it be for you to do the migration? I don't know if I have the time to do this currently, depending how much it is.

/// b) annotate controller methods with the "[Authorize]" attribute
/// It is possible to exclude methods from the policy enforcement by annotate them with the "[AllowAnonymous]" attribute, however.
/// </summary>
public class UriBasedResourceProtectionMiddleware
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some tests? We can discuss what types of tests are needed for this one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a collection of unittest. Which additional tets do you have in mind?

using Moq;

#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "<Pending>")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline suppression should be avoided, let's configure it via .editorconfig for test project.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing. I didn't want to change it, because its your project after all and you are making the style rules :)

var requestDelegateMock = new Mock<RequestDelegate>();

// Act & Assert
Assert.Throws<ArgumentNullException>(() => _ = new UriBasedResourceProtectionMiddleware(requestDelegateMock.Object, null));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Fluent Assertions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@NikiforovAll
Copy link
Owner

NikiforovAll commented May 6, 2024

Also, I think this case is partially covered by ProtectedResourcePolicyProvider. Could you please take a look?

https://nikiforovall.github.io/keycloak-authorization-services-dotnet/authorization/policy-provider.html

@NikiforovAll
Copy link
Owner

This is a very niche functionality, We should consider creating Contribution repository that will contain components from the Community.

Friedemann Braune added 13 commits May 11, 2024 09:10
…rceProtectionAttribute; adapted tests; adapted samples; disable to rules by test specific .editorconfig
… working example for URI based keycloak authorization
… working example for URI based keycloak authorization
…rceProtectionAttribute; adapted tests; adapted samples; disable to rules by test specific .editorconfig
rename AutoProtectResource to UriBasedResourceProtection; add minimal working example for URI based keycloak authorization

introduce DisableAutoProtection and ExplicitResourceProtection; Refactor Middleware; Extend example app

add testclass for middleware; fix bug where status set to 401 on successful auth in middleware

add middleware and builder extension method for usage

rename AutoProtectResource to UriBasedResourceProtection; add minimal working example for URI based keycloak authorization

introduce DisableAutoProtection and ExplicitResourceProtection; Refactor Middleware; Extend example app

merged DisableUriBasedResourceProtection attribute into ExplicitResourceProtectionAttribute; adapted tests; adapted samples; disable to rules by test specific .editorconfig

add middleware and builder extension method for usage

rename AutoProtectResource to UriBasedResourceProtection; add minimal working example for URI based keycloak authorization

introduce DisableAutoProtection and ExplicitResourceProtection; Refactor Middleware; Extend example app

add testclass for middleware; fix bug where status set to 401 on successful auth in middleware

add middleware and builder extension method for usage

rename AutoProtectResource to UriBasedResourceProtection; add minimal working example for URI based keycloak authorization

introduce DisableAutoProtection and ExplicitResourceProtection; Refactor Middleware; Extend example app

merged DisableUriBasedResourceProtection attribute into ExplicitResourceProtectionAttribute; adapted tests; adapted samples; disable to rules by test specific .editorconfig
@wanderzirkus
Copy link
Author

Alright, I think I got the latest version of main merged into my feature branch now. I forgot to fetch the latest version from remote before rebasing/merging *facepalm
I see the migration issue now, too. What should we use instead of "VerifyAccessToResource" on the IKeycloakProtectionClient?

@wanderzirkus
Copy link
Author

This is a very niche functionality, We should consider creating Contribution repository that will contain components from the Community.

Well if you don't want this feature at all feel free to just close the PR. I find it most useful and thought I could give s.th. back, but I am not upset, if you say its to niche to be considered valuable enough for the repo. :)

@NikiforovAll
Copy link
Owner

@wanderzirkus let's discuss the feature in discord. Do you have time for that?

@NikiforovAll
Copy link
Owner

Alright, I think I got the latest version of main merged into my feature branch now. I forgot to fetch the latest version from remote before rebasing/merging *facepalm I see the migration issue now, too. What should we use instead of "VerifyAccessToResource" on the IKeycloakProtectionClient?

It's a https://nikiforovall.github.io/keycloak-authorization-services-dotnet/authorization/resources-client.html

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

Successfully merging this pull request may close these issues.

2 participants