diff --git a/src/BuildingBlocks/Security/Authorization/AuthorizationPipeline.cs b/src/BuildingBlocks/Security/Authorization/AuthorizationPipeline.cs
new file mode 100644
index 0000000..88869e0
--- /dev/null
+++ b/src/BuildingBlocks/Security/Authorization/AuthorizationPipeline.cs
@@ -0,0 +1,44 @@
+namespace OpenHealthOS.Security.Authorization;
+
+using OpenHealthOS.Contracts.Authorization;
+
+///
+/// Provides the application authorization pipeline by delegating
+/// authorization requests to the authorization service.
+///
+public sealed class AuthorizationPipeline : IAuthorizationPipeline
+{
+ private readonly IAuthorizationService _authorizationService;
+
+ ///
+ /// Initializes a new instance of the
+ /// class.
+ ///
+ ///
+ /// The authorization service used to evaluate requests.
+ ///
+ public AuthorizationPipeline(
+ IAuthorizationService authorizationService)
+ {
+ ArgumentNullException.ThrowIfNull(authorizationService);
+
+ _authorizationService = authorizationService;
+ }
+
+ ///
+ /// Evaluates an authorization request.
+ ///
+ ///
+ /// The authorization request to evaluate.
+ ///
+ ///
+ /// The authorization decision returned by the authorization service.
+ ///
+ public AuthorizationDecision Authorize(
+ AuthorizationRequest request)
+ {
+ ArgumentNullException.ThrowIfNull(request);
+
+ return _authorizationService.Authorize(request.Context);
+ }
+}
diff --git a/src/BuildingBlocks/Security/Authorization/AuthorizationRequest.cs b/src/BuildingBlocks/Security/Authorization/AuthorizationRequest.cs
new file mode 100644
index 0000000..1f56283
--- /dev/null
+++ b/src/BuildingBlocks/Security/Authorization/AuthorizationRequest.cs
@@ -0,0 +1,29 @@
+namespace OpenHealthOS.Security.Authorization;
+
+using OpenHealthOS.Contracts.Authorization;
+
+///
+/// Represents an authorization request submitted to the authorization
+/// pipeline.
+///
+public sealed record AuthorizationRequest
+{
+ ///
+ /// Initializes a new instance of the
+ /// class.
+ ///
+ ///
+ /// The authorization context associated with the request.
+ ///
+ public AuthorizationRequest(AuthorizationContextDto context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+
+ Context = context;
+ }
+
+ ///
+ /// Gets the authorization context.
+ ///
+ public AuthorizationContextDto Context { get; }
+}
diff --git a/src/BuildingBlocks/Security/Authorization/IAuthorizationPipeline.cs b/src/BuildingBlocks/Security/Authorization/IAuthorizationPipeline.cs
new file mode 100644
index 0000000..2e6ce80
--- /dev/null
+++ b/src/BuildingBlocks/Security/Authorization/IAuthorizationPipeline.cs
@@ -0,0 +1,21 @@
+namespace OpenHealthOS.Security.Authorization;
+
+using OpenHealthOS.Contracts.Authorization;
+
+///
+/// Represents the application authorization pipeline.
+///
+public interface IAuthorizationPipeline
+{
+ ///
+ /// Evaluates an authorization request.
+ ///
+ ///
+ /// The authorization request to evaluate.
+ ///
+ ///
+ /// The resulting authorization decision.
+ ///
+ AuthorizationDecision Authorize(
+ AuthorizationRequest request);
+}
diff --git a/tests/UnitTests/Authorization/AuthorizationPipelineTests.cs b/tests/UnitTests/Authorization/AuthorizationPipelineTests.cs
new file mode 100644
index 0000000..1ddb1dd
--- /dev/null
+++ b/tests/UnitTests/Authorization/AuthorizationPipelineTests.cs
@@ -0,0 +1,116 @@
+namespace OpenHealthOS.UnitTests.Authorization;
+
+using OpenHealthOS.Contracts.Authorization;
+using OpenHealthOS.Contracts.Identity;
+using OpenHealthOS.Security.Authorization;
+using Xunit;
+
+public sealed class AuthorizationPipelineTests
+{
+ [Fact]
+ public void Authorize_ShouldReturnAllow_WhenServiceAllows()
+ {
+ var service = new StubAuthorizationService(
+ AuthorizationDecision.Allow);
+
+ var pipeline = new AuthorizationPipeline(service);
+
+ var request = CreateRequest();
+
+ var result = pipeline.Authorize(request);
+
+ Assert.Equal(
+ AuthorizationDecision.Allow,
+ result);
+ }
+
+ [Fact]
+ public void Authorize_ShouldReturnDeny_WhenServiceDenies()
+ {
+ var service = new StubAuthorizationService(
+ AuthorizationDecision.Deny);
+
+ var pipeline = new AuthorizationPipeline(service);
+
+ var request = CreateRequest();
+
+ var result = pipeline.Authorize(request);
+
+ Assert.Equal(
+ AuthorizationDecision.Deny,
+ result);
+ }
+
+ [Fact]
+ public void Constructor_ShouldThrow_WhenServiceIsNull()
+ {
+ Assert.Throws(
+ () => new AuthorizationPipeline(null!));
+ }
+
+ [Fact]
+ public void Authorize_ShouldThrow_WhenRequestIsNull()
+ {
+ var service = new StubAuthorizationService(
+ AuthorizationDecision.Allow);
+
+ var pipeline = new AuthorizationPipeline(service);
+
+ Assert.Throws(
+ () => pipeline.Authorize(null!));
+ }
+
+ [Fact]
+ public void Authorize_ShouldPassContextToService()
+ {
+ var service = new StubAuthorizationService(
+ AuthorizationDecision.Allow);
+
+ var pipeline = new AuthorizationPipeline(service);
+
+ var request = CreateRequest();
+
+ pipeline.Authorize(request);
+
+ Assert.Same(request.Context, service.LastContext);
+ }
+
+ private static AuthorizationRequest CreateRequest()
+ {
+ var context = new AuthorizationContextDto
+ {
+ Identity = new IdentityContextDto
+ {
+ SubjectId = "test-user",
+ PrincipalType = PrincipalType.User,
+ Scopes = ["patient.read"],
+ },
+ Permission = new Permission("patient.read"),
+ ResourceType = "Patient",
+ };
+
+ return new AuthorizationRequest(context);
+ }
+
+ private sealed class StubAuthorizationService
+ : IAuthorizationService
+ {
+ private readonly AuthorizationDecision _decision;
+
+ public StubAuthorizationService(
+ AuthorizationDecision decision)
+ {
+ _decision = decision;
+ }
+
+ public AuthorizationContextDto? LastContext { get; private set; }
+
+ public AuthorizationDecision Authorize(
+ AuthorizationContextDto context)
+ {
+ LastContext = context;
+
+ return _decision;
+ }
+ }
+}