-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add middleware and builder extension method for usage
- Loading branch information
Friedemann Braune
committed
Jan 7, 2024
1 parent
2062b24
commit aeef9ac
Showing
1 changed file
with
68 additions
and
0 deletions.
There are no files selected for viewing
68 changes: 68 additions & 0 deletions
68
src/Keycloak.AuthServices.Authorization/AutoProtectResourceMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
namespace Keycloak.AuthServices.Authorization; | ||
|
||
using Keycloak.AuthServices.Sdk.AuthZ; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Http.Features; | ||
|
||
/// <summary> | ||
/// Policy Enforcment Point, which automatically protects request URLs by requesting permission at the keycloak authorization API. | ||
/// Using this, there is no need to: | ||
/// a) register policies in code (as the policies provided in keycloak will be used for authentication) | ||
/// 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 AutoProtectResourceMiddleware | ||
{ | ||
private readonly RequestDelegate next; | ||
private readonly IKeycloakProtectionClient client; | ||
|
||
/// <summary> | ||
/// <see cref="AutoProtectResourceMiddleware"/> | ||
/// </summary> | ||
/// <param name="next">The <see cref="RequestDelegate"/> to proceed with in case of authorization success.</param> | ||
/// <param name="client">The <see cref="IKeycloakProtectionClient"/> is used for the authorization request.</param> | ||
/// <exception cref="ArgumentNullException">Thrown, if <see cref="IKeycloakProtectionClient"/> is null.</exception> | ||
public AutoProtectResourceMiddleware(RequestDelegate next, IKeycloakProtectionClient client) | ||
{ | ||
this.next = next; | ||
this.client = client ?? throw new ArgumentNullException(nameof(client)); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public async Task InvokeAsync(HttpContext context) | ||
{ | ||
var targetAllowsAnonymous = context.Features? | ||
.Get<IEndpointFeature>()? | ||
.Endpoint? | ||
.Metadata | ||
.Any(attribute => attribute is AllowAnonymousAttribute) ?? false; | ||
|
||
if (!targetAllowsAnonymous) | ||
{ | ||
var isAuthorized = await this.client.VerifyAccessToResource( | ||
context.Request.Path, context.Request.Method, CancellationToken.None); | ||
|
||
if (!isAuthorized) | ||
{ | ||
context.Response.StatusCode = StatusCodes.Status401Unauthorized; | ||
return; | ||
} | ||
} | ||
|
||
await this.next(context); | ||
} | ||
} | ||
|
||
/// <summary/> | ||
public static class MiddlewareExtensions | ||
{ | ||
/// <summary> | ||
/// Extension method to enable automatic resource protection. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="IApplicationBuilder"/> isntance.</param> | ||
/// <returns>The <see cref="IApplicationBuilder"/> isntance with <see cref="AutoProtectResourceMiddleware"/> usage.</returns> | ||
public static IApplicationBuilder UseAutomaticKeycloakEndpointProtection( | ||
this IApplicationBuilder builder) => builder.UseMiddleware<AutoProtectResourceMiddleware>(); | ||
} |