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

Allow more failure details for AuthZ handlers #35319

Closed
HaoK opened this issue Aug 13, 2021 · 19 comments · Fixed by #35425
Closed

Allow more failure details for AuthZ handlers #35319

HaoK opened this issue Aug 13, 2021 · 19 comments · Fixed by #35425
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer
Milestone

Comments

@HaoK
Copy link
Member

HaoK commented Aug 13, 2021

Today there's no way to signal anything other than which requirements have failed or an explicit Fail https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authorization/Core/src/AuthorizationHandlerContext.cs#L82

This makes it hard for a handler to flow more specific error information/details to the IAuthorizationMiddlewareResultHandler

@HaoK HaoK added the area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer label Aug 13, 2021
@blowdart blowdart added this to the 6.0-rc1 milestone Aug 13, 2021
@Tratcher
Copy link
Member

Tratcher commented Aug 13, 2021

New Overload Fail on AuthorizationHandlerContext to pass failure reason. New AuthorizationFailureReason base class has a string message and the handler which called failed, developers can inherit and pass their own derived failure reasons which can contain typed state as needed.

+public class AuthorizationFailureReason
{
+ Encapsulates a reason why authorization failed
+ public AuthorizationFailureReason(IAuthorizationHandler handler, string message)
+ public string Message { get; set; }
+ public IAuthorizationHandler Handler { get; set; }
public class AuthorizationHandlerContext
{
+ // Calls Fail() and stores the failure state for future reference. Can be called multiple times.
+ public virtual void Fail(AuthorizationFailureReason);
+ public IEnumerable<AuthorizationFailureReason> FailureReasons { get; }

The IAuthorizationEvaluator checks for failure and bundles it into an AuthorizationFailure

: AuthorizationResult.Failed(context.HasFailed
? AuthorizationFailure.ExplicitFail()

public static AuthorizationFailure ExplicitFail()

public class AuthorizationFailure
{
+ // Calls Fail and stores the failure reasons for future reference. Can be called multiple times.
+ public static void ExplicitFail(IEnumerable<AuthorizationFailureReason> reasons);
+ public IEnumerable<AuthorizationFailureReason> FailureStates { get; }

Which ends up on the AuthorizationResult:

public AuthorizationFailure? Failure { get; private set; }

Which is then passed to the IAuthorizationMiddlewareResultHandler
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult!, context, resource);
var authorizationMiddlewareResultHandler = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>();
await authorizationMiddlewareResultHandler.HandleAsync(_next, context, policy, authorizeResult);

Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult);

Sample consumption: https://github.com/dotnet/aspnetcore/blob/main/src/Security/samples/CustomAuthorizationFailureResponse/Authorization/SampleAuthorizationMiddlewareResultHandler.cs#L36

Full PR: #35425

@Tratcher Tratcher added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Aug 13, 2021
@HaoK
Copy link
Member Author

HaoK commented Aug 17, 2021

@Tratcher @blowdart how do you feel about ExplicitFail/States => FailureReason(s)?

@HaoK
Copy link
Member Author

HaoK commented Aug 17, 2021

I don't think it necessarily makes sense to force correlation of requirements/reasons, but we could do Handler -> reasons, since that is really the split, but the failure state should be the mechanism to communicate information, if the handler wants to enumerate the requirements that failed, they are free to put that in the failure reason explicitly, but that mapping is completely up to the app/handlers

@blowdart
Copy link
Contributor

State has multiple meanings, I'd prefer reasons. But object feels, odd to me. I'd suggest an interface here with, at least, a message property, so folks don't have to cast around if they don't want to?

As an aside aren't there old issues about this? We should link them.

@HaoK
Copy link
Member Author

HaoK commented Aug 17, 2021

Isn't the argument against interfaces future breaking change concerns? We could do a base FailureReason class which has a message property?

@HaoK HaoK added ✔️ Resolution: Fixed The bug or enhancement requested in this issue has been checked-in! Done This issue has been fixed labels Aug 18, 2021
@davidfowl
Copy link
Member

Did this get API reviewed?

@HaoK
Copy link
Member Author

HaoK commented Aug 18, 2021

Oops, no I'll add the labels

@HaoK HaoK reopened this Aug 18, 2021
@HaoK HaoK added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Aug 18, 2021
@ghost
Copy link

ghost commented Aug 18, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@HaoK HaoK removed Done This issue has been fixed ✔️ Resolution: Fixed The bug or enhancement requested in this issue has been checked-in! labels Aug 18, 2021
@ghost
Copy link

ghost commented Aug 18, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

1 similar comment
@ghost
Copy link

ghost commented Aug 18, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@pranavkm
Copy link
Contributor

  • Remove setters from properties.
public class AuthorizationFailureReason
{
+   public AuthorizationFailureReason(IAuthorizationHandler handler, string message)
+   public string Message { get; }
+   public IAuthorizationHandler Handler { get; }
}

API consideration: Can this type be sealed and use a `IDictionary` to pass additional data instead e.g.

```diff
+public sealed class AuthorizationFailureReason
{
+   public AuthorizationFailureReason(IAuthorizationHandler handler, string message)
+   public string Message { get; }
+   public IAuthorizationHandler Handler { get; }
+   public IDictionary<object, object> Properties { get; } 
}

AuthorizationFailure

  • Update property name to FailureReasons for consistency:
public class AuthorizationFailure
{
+ // Calls Fail and stores the failure reasons for future reference. Can be called multiple times.
+ public static void ExplicitFail(IEnumerable<AuthorizationFailureReason> reasons);
+ public IEnumerable<AuthorizationFailureReason> FailureReasons { get; }

@pranavkm pranavkm added api-approved API was approved in API review, it can be implemented and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Aug 23, 2021
@pranavkm
Copy link
Contributor

@blowdart / @adityamandaleeka is someone on point to update the PR based on the API feedback?

@blowdart
Copy link
Contributor

If hao isn't here @Tratcher i'd guess

@Kahbazi
Copy link
Member

Kahbazi commented Aug 23, 2021

@pranavkm I'm interested in doing this. May I send the PR?

@pranavkm
Copy link
Contributor

We'd appreciate that @Kahbazi. Thanks!

@Tratcher
Copy link
Member

A) There's an RC1 PR here that needs updating: #35529. @Kahbazi you'll need to send a PR to that branch.
B) Main also needs updating.
C) There's an outstanding point to resolve first:

API consideration: Can this type be sealed and use a `IDictionary` to pass additional data instead e.g.

@blowdart? When designing this we specifically discussed an unsealed type that could be derived from to add strongly typed data. Using a loosely typed dictionary didn't come up. Either way requires downcasting, though with the derived type it would only be a single downcast instead of per property.

@blowdart
Copy link
Contributor

A single cast seems the better approach, less error prone and faster.

@Tratcher
Copy link
Member

+ public static void ExplicitFail(IEnumerable<AuthorizationFailureReason> reasons);

This is outdated, the name used in the PR is:

+ public static void Fail(IEnumerable<AuthorizationFailureReason> reasons);

@davidfowl
Copy link
Member

Are we sure we want IEnumerable<T> , allocations...

@ghost ghost locked as resolved and limited conversation to collaborators Sep 23, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants