diff --git a/samples/AutoAuthorization/Controllers/ApiController.cs b/samples/AutoAuthorization/Controllers/ApiController.cs
index 492cac0a..a6fcf052 100644
--- a/samples/AutoAuthorization/Controllers/ApiController.cs
+++ b/samples/AutoAuthorization/Controllers/ApiController.cs
@@ -1,4 +1,6 @@
namespace AutoAuthorization.Controllers;
+
+using Keycloak.AuthServices.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
@@ -8,4 +10,16 @@ public class ApiController : ControllerBase
[Route("/api/auto-auth")]
[ProducesResponseType(statusCode: 200, type: typeof(string))]
public IActionResult GetData() => this.Ok("Auto Authorization works.");
+
+ [HttpGet]
+ [Route("/api/no-auth")]
+ [ProducesResponseType(statusCode: 200, type: typeof(string))]
+ [DisableUriBasedResourceProtection]
+ public IActionResult GetDataWithoutAuth() => this.Ok("Auto Authorization works, but is disabled by attribute.");
+
+ [HttpGet]
+ [Route("/api/explicit-auth")]
+ [ProducesResponseType(statusCode: 200, type: typeof(string))]
+ [ExplicitResourceProtection("/api/auto-auth", "GET")]
+ public IActionResult GetDataWithExplicitAuth() => this.Ok("Auto Authorization works with explicit choosing resource name and scope.");
}
diff --git a/src/Keycloak.AuthServices.Authorization/ExplicitResourceProtectionAttribute.cs b/src/Keycloak.AuthServices.Authorization/ExplicitResourceProtectionAttribute.cs
new file mode 100644
index 00000000..021f5771
--- /dev/null
+++ b/src/Keycloak.AuthServices.Authorization/ExplicitResourceProtectionAttribute.cs
@@ -0,0 +1,30 @@
+namespace Keycloak.AuthServices.Authorization;
+
+///
+/// Use this attribute on a method when the
+/// to exclude the method from uri based resource protection and set a specific resource name and scope instead.
+///
+[AttributeUsage(AttributeTargets.Method, Inherited = false)]
+public class ExplicitResourceProtectionAttribute : Attribute
+{
+ ///
+ ///
+ ///
+ /// The name of the resource a configured in Keycloak mandatory and unique "Name" field (also referred as RESOURCE_ID).
+ /// A valid scope from the list of applied scopes to the resource.
+ public ExplicitResourceProtectionAttribute(string resourceName, string scope)
+ {
+ this.ResourceName = resourceName;
+ this.Scope = scope;
+ }
+
+ ///
+ /// The resource name to authorize for.
+ ///
+ public string ResourceName { get; }
+
+ ///
+ /// The scope to authorize the resource for.
+ ///
+ public string Scope { get; }
+}
\ No newline at end of file
diff --git a/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/DisableUriBasedResourceProtection.cs b/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/DisableUriBasedResourceProtection.cs
new file mode 100644
index 00000000..6ee06a10
--- /dev/null
+++ b/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/DisableUriBasedResourceProtection.cs
@@ -0,0 +1,10 @@
+namespace Keycloak.AuthServices.Authorization;
+
+///
+/// Use this attribute on a method when the
+/// to exclude the method from uri based resource protection.
+///
+[AttributeUsage(AttributeTargets.Method, Inherited = false)]
+public class DisableUriBasedResourceProtectionAttribute : Attribute
+{
+}
\ No newline at end of file
diff --git a/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtectionMiddleware.cs b/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/UriBasedResourceProtectionMiddleware.cs
similarity index 59%
rename from src/Keycloak.AuthServices.Authorization/UriBasedResourceProtectionMiddleware.cs
rename to src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/UriBasedResourceProtectionMiddleware.cs
index a13ed181..b4d79d12 100644
--- a/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtectionMiddleware.cs
+++ b/src/Keycloak.AuthServices.Authorization/UriBasedResourceProtection/UriBasedResourceProtectionMiddleware.cs
@@ -33,36 +33,64 @@ public UriBasedResourceProtectionMiddleware(RequestDelegate next, IKeycloakProte
///
public async Task InvokeAsync(HttpContext context)
{
- var targetAllowsAnonymous = context.Features?
+ if (this.EvaluateAuthorization(context).Result)
+ {
+ await this.next(context);
+ }
+ }
+
+ private async Task EvaluateAuthorization(HttpContext context)
+ {
+ var attributes = context.Features?
.Get()?
.Endpoint?
- .Metadata
- .Any(attribute => attribute is AllowAnonymousAttribute) ?? false;
+ .Metadata;
+
+ if (UriBasedProtectionDisabled(attributes))
+ {
+ return true;
+ }
- if (!targetAllowsAnonymous)
+ var resourceName = "";
+ var scope = "";
+
+ if (attributes != null && attributes
+ .SingleOrDefault(attribute => attribute is ExplicitResourceProtectionAttribute)
+ is ExplicitResourceProtectionAttribute explicitResourceProtectionAttribute)
+ {
+ resourceName = explicitResourceProtectionAttribute.ResourceName;
+ scope = explicitResourceProtectionAttribute.Scope;
+ }
+ else
{
- var isAuthorized = await this.client.VerifyAccessToResource(
- context.Request.Path, context.Request.Method, CancellationToken.None);
+ resourceName = context.Request.Path;
+ scope = context.Request.Method;
+ }
- if (!isAuthorized)
- {
- context.Response.StatusCode = StatusCodes.Status401Unauthorized;
- return;
- }
+ var isAuthorized = await this.client.VerifyAccessToResource(
+ resourceName, scope, CancellationToken.None);
+
+ if (isAuthorized)
+ {
+ context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+ return true;
}
- await this.next(context);
+ return false;
}
+
+ private static bool UriBasedProtectionDisabled(EndpointMetadataCollection? attributes) => attributes != null && attributes.Any(attribute => attribute is AllowAnonymousAttribute
+ or DisableUriBasedResourceProtectionAttribute);
}
///
public static class MiddlewareExtensions
{
///
- /// Extension method to enable automatic resource protection.
+ /// Extension method to enable automatic uri based resource protection.
///
/// The isntance.
/// The isntance with usage.
public static IApplicationBuilder UseUriBasedKeycloakEndpointProtection(
this IApplicationBuilder builder) => builder.UseMiddleware();
-}
+}
\ No newline at end of file