diff --git a/docs/architecture/bff-identity-boundary.md b/docs/architecture/bff-identity-boundary.md
index efe43ea..a778e74 100644
--- a/docs/architecture/bff-identity-boundary.md
+++ b/docs/architecture/bff-identity-boundary.md
@@ -7,6 +7,8 @@ Keep thalos-bff as an edge adapter layer that consumes thalos-service and adopte
- Edge contract handling
- Service client adaptation
- Correlation/tracing propagation
+- Single active edge protocol policy enforcement (`rest`)
+- Provider metadata propagation (`InternalJwt`, `AzureAd`, `Google`)
## Prohibited
- Direct DAL access
diff --git a/src/Thalos.Bff.Application/Adapters/IdentityEdgeContractAdapter.cs b/src/Thalos.Bff.Application/Adapters/IdentityEdgeContractAdapter.cs
index 3f38a68..22259b0 100644
--- a/src/Thalos.Bff.Application/Adapters/IdentityEdgeContractAdapter.cs
+++ b/src/Thalos.Bff.Application/Adapters/IdentityEdgeContractAdapter.cs
@@ -12,13 +12,21 @@ public sealed class IdentityEdgeContractAdapter : IIdentityEdgeContractAdapter
///
public EvaluateIdentityPolicyRequest ToPolicyRequest(IssueTokenApiRequest request, string permissionCode)
{
- return new EvaluateIdentityPolicyRequest(request.SubjectId, request.TenantId, permissionCode);
+ return new EvaluateIdentityPolicyRequest(
+ request.SubjectId,
+ request.TenantId,
+ permissionCode,
+ request.Provider);
}
///
public IssueIdentityTokenRequest ToIssueTokenRequest(IssueTokenApiRequest request)
{
- return new IssueIdentityTokenRequest(request.SubjectId, request.TenantId);
+ return new IssueIdentityTokenRequest(
+ request.SubjectId,
+ request.TenantId,
+ request.Provider,
+ request.ExternalToken);
}
///
@@ -30,7 +38,10 @@ public sealed class IdentityEdgeContractAdapter : IIdentityEdgeContractAdapter
///
public RefreshIdentitySessionRequest ToRefreshSessionRequest(RefreshSessionApiRequest request)
{
- return new RefreshIdentitySessionRequest(request.RefreshToken, request.CorrelationId);
+ return new RefreshIdentitySessionRequest(
+ request.RefreshToken,
+ request.CorrelationId,
+ request.Provider);
}
///
diff --git a/src/Thalos.Bff.Application/Adapters/IdentityEdgeGrpcContractAdapter.cs b/src/Thalos.Bff.Application/Adapters/IdentityEdgeGrpcContractAdapter.cs
index c06c1ee..7f2fc4f 100644
--- a/src/Thalos.Bff.Application/Adapters/IdentityEdgeGrpcContractAdapter.cs
+++ b/src/Thalos.Bff.Application/Adapters/IdentityEdgeGrpcContractAdapter.cs
@@ -11,12 +11,32 @@ public sealed class IdentityEdgeGrpcContractAdapter : IIdentityEdgeGrpcContractA
///
public IssueIdentityTokenGrpcContract ToGrpc(IssueTokenApiRequest request)
{
- return new IssueIdentityTokenGrpcContract(request.SubjectId, request.TenantId, request.CorrelationId);
+ return new IssueIdentityTokenGrpcContract(
+ request.SubjectId,
+ request.TenantId,
+ request.CorrelationId,
+ request.Provider.ToString(),
+ request.ExternalToken);
}
///
public IssueTokenApiRequest FromGrpc(IssueIdentityTokenGrpcContract contract)
{
- return new IssueTokenApiRequest(contract.SubjectId, contract.TenantId, contract.CorrelationId);
+ return new IssueTokenApiRequest(
+ contract.SubjectId,
+ contract.TenantId,
+ contract.CorrelationId,
+ ParseProvider(contract.Provider),
+ contract.ExternalToken);
+ }
+
+ private static BuildingBlock.Identity.Contracts.Conventions.IdentityAuthProvider ParseProvider(string provider)
+ {
+ return Enum.TryParse(
+ provider,
+ true,
+ out var parsedProvider)
+ ? parsedProvider
+ : BuildingBlock.Identity.Contracts.Conventions.IdentityAuthProvider.InternalJwt;
}
}
diff --git a/src/Thalos.Bff.Application/Grpc/IssueIdentityTokenGrpcContract.cs b/src/Thalos.Bff.Application/Grpc/IssueIdentityTokenGrpcContract.cs
index ac57ab0..e49ccc1 100644
--- a/src/Thalos.Bff.Application/Grpc/IssueIdentityTokenGrpcContract.cs
+++ b/src/Thalos.Bff.Application/Grpc/IssueIdentityTokenGrpcContract.cs
@@ -6,4 +6,11 @@ namespace Thalos.Bff.Application.Grpc;
/// Identity subject identifier.
/// Tenant identifier.
/// Request correlation identifier.
-public sealed record IssueIdentityTokenGrpcContract(string SubjectId, string TenantId, string CorrelationId);
+/// Identity provider.
+/// External provider token when applicable.
+public sealed record IssueIdentityTokenGrpcContract(
+ string SubjectId,
+ string TenantId,
+ string CorrelationId,
+ string Provider = "InternalJwt",
+ string ExternalToken = "");
diff --git a/src/Thalos.Bff.Contracts/Api/IssueTokenApiRequest.cs b/src/Thalos.Bff.Contracts/Api/IssueTokenApiRequest.cs
index 7014cc6..aea9771 100644
--- a/src/Thalos.Bff.Contracts/Api/IssueTokenApiRequest.cs
+++ b/src/Thalos.Bff.Contracts/Api/IssueTokenApiRequest.cs
@@ -1,3 +1,5 @@
+using BuildingBlock.Identity.Contracts.Conventions;
+
namespace Thalos.Bff.Contracts.Api;
///
@@ -6,4 +8,11 @@ namespace Thalos.Bff.Contracts.Api;
/// Identity subject identifier.
/// Tenant identifier.
/// Request correlation identifier.
-public sealed record IssueTokenApiRequest(string SubjectId, string TenantId, string CorrelationId = "");
+/// Identity auth provider.
+/// External provider token when applicable.
+public sealed record IssueTokenApiRequest(
+ string SubjectId,
+ string TenantId,
+ string CorrelationId = "",
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt,
+ string ExternalToken = "");
diff --git a/src/Thalos.Bff.Contracts/Api/RefreshSessionApiRequest.cs b/src/Thalos.Bff.Contracts/Api/RefreshSessionApiRequest.cs
index a1765d3..43980d3 100644
--- a/src/Thalos.Bff.Contracts/Api/RefreshSessionApiRequest.cs
+++ b/src/Thalos.Bff.Contracts/Api/RefreshSessionApiRequest.cs
@@ -1,3 +1,5 @@
+using BuildingBlock.Identity.Contracts.Conventions;
+
namespace Thalos.Bff.Contracts.Api;
///
@@ -5,4 +7,8 @@ namespace Thalos.Bff.Contracts.Api;
///
/// Refresh token value.
/// Request correlation identifier.
-public sealed record RefreshSessionApiRequest(string RefreshToken, string CorrelationId = "");
+/// Identity auth provider.
+public sealed record RefreshSessionApiRequest(
+ string RefreshToken,
+ string CorrelationId = "",
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt);
diff --git a/src/Thalos.Bff.Contracts/Thalos.Bff.Contracts.csproj b/src/Thalos.Bff.Contracts/Thalos.Bff.Contracts.csproj
index 04a4bbc..9a5109e 100644
--- a/src/Thalos.Bff.Contracts/Thalos.Bff.Contracts.csproj
+++ b/src/Thalos.Bff.Contracts/Thalos.Bff.Contracts.csproj
@@ -6,5 +6,6 @@
+
diff --git a/src/Thalos.Bff.Rest/Adapters/ThalosServiceGrpcClientAdapter.cs b/src/Thalos.Bff.Rest/Adapters/ThalosServiceGrpcClientAdapter.cs
index 1a597db..01f3724 100644
--- a/src/Thalos.Bff.Rest/Adapters/ThalosServiceGrpcClientAdapter.cs
+++ b/src/Thalos.Bff.Rest/Adapters/ThalosServiceGrpcClientAdapter.cs
@@ -25,7 +25,9 @@ public sealed class ThalosServiceGrpcClientAdapter(
var grpcRequest = new IssueIdentityTokenGrpcRequest
{
SubjectId = request.SubjectId,
- TenantId = request.TenantId
+ TenantId = request.TenantId,
+ Provider = request.Provider.ToString(),
+ ExternalToken = request.ExternalToken
};
var grpcResponse = await grpcClient.IssueIdentityTokenAsync(
@@ -43,7 +45,8 @@ public sealed class ThalosServiceGrpcClientAdapter(
{
SubjectId = request.SubjectId,
TenantId = request.TenantId,
- PermissionCode = request.PermissionCode
+ PermissionCode = request.PermissionCode,
+ Provider = request.Provider.ToString()
};
var grpcResponse = await grpcClient.EvaluateIdentityPolicyAsync(
@@ -63,7 +66,8 @@ public sealed class ThalosServiceGrpcClientAdapter(
var grpcRequest = new IssueIdentityTokenGrpcRequest
{
SubjectId = request.RefreshToken,
- TenantId = refreshTenantId
+ TenantId = refreshTenantId,
+ Provider = request.Provider.ToString()
};
var grpcResponse = await grpcClient.IssueIdentityTokenAsync(
diff --git a/src/Thalos.Bff.Rest/Program.cs b/src/Thalos.Bff.Rest/Program.cs
index 3944eee..0c31b59 100644
--- a/src/Thalos.Bff.Rest/Program.cs
+++ b/src/Thalos.Bff.Rest/Program.cs
@@ -11,6 +11,12 @@ using Thalos.Service.Grpc;
const string CorrelationHeaderName = "x-correlation-id";
var builder = WebApplication.CreateBuilder(args);
+var edgeProtocol = builder.Configuration["ThalosBff:EdgeProtocol"] ?? "rest";
+if (!string.Equals(edgeProtocol, "rest", StringComparison.OrdinalIgnoreCase))
+{
+ throw new InvalidOperationException(
+ $"Thalos BFF supports one active edge protocol per deployment. Configured: '{edgeProtocol}'. Expected: 'rest'.");
+}
builder.Services.AddHttpContextAccessor();
builder.Services.AddHealthChecks();
diff --git a/tests/Thalos.Bff.Application.UnitTests/ContractShapeTests.cs b/tests/Thalos.Bff.Application.UnitTests/ContractShapeTests.cs
index 8f3e5b4..99f4d1c 100644
--- a/tests/Thalos.Bff.Application.UnitTests/ContractShapeTests.cs
+++ b/tests/Thalos.Bff.Application.UnitTests/ContractShapeTests.cs
@@ -1,4 +1,5 @@
using Core.Blueprint.Common.Contracts;
+using BuildingBlock.Identity.Contracts.Conventions;
using Thalos.Bff.Contracts.Api;
using Thalos.Bff.Contracts.Conventions;
@@ -14,6 +15,7 @@ public class ContractShapeTests
Assert.Equal("user-1", request.SubjectId);
Assert.Equal("tenant-1", request.TenantId);
Assert.Equal("corr-123", request.CorrelationId);
+ Assert.Equal(IdentityAuthProvider.InternalJwt, request.Provider);
}
[Fact]