feat(building-block-identity): extend provider-aware identity contracts

This commit is contained in:
José René White Enciso 2026-02-25 13:13:56 -06:00
parent 1cba08de5b
commit 7d9db8c3d6
10 changed files with 131 additions and 8 deletions

View File

@ -8,6 +8,7 @@
| thalos-service policy contracts | Contracts/Policies | Preserve policy semantics and required fields |
| thalos-service policy context contracts | Contracts/Context | Keep field naming stable for compatibility window |
| thalos-bff refresh session contracts | Contracts/Sessions | Candidate for shared capability standardization |
| provider flow metadata (JWT/Azure/Google) | Contracts/Conventions | Provider metadata stays transport-neutral and additive |
## Namespace Strategy
- Current Thalos namespaces are mapped to `BuildingBlock.Identity.Contracts.*`.
@ -18,3 +19,4 @@
2. Add compatibility bridge in Thalos consumers.
3. Migrate service consumers first, then BFF consumers.
4. Deprecate old namespace usage after compatibility window.
5. Keep provider enum and provider-specific fields additive to avoid breaking consumers.

View File

@ -0,0 +1,22 @@
namespace BuildingBlock.Identity.Contracts.Conventions;
/// <summary>
/// Supported identity authentication providers.
/// </summary>
public enum IdentityAuthProvider
{
/// <summary>
/// AgileWebs-issued internal JWT flow.
/// </summary>
InternalJwt = 0,
/// <summary>
/// Microsoft Entra ID / Azure AD OAuth/OIDC flow.
/// </summary>
AzureAd = 1,
/// <summary>
/// Google OAuth/OIDC flow.
/// </summary>
Google = 2
}

View File

@ -6,4 +6,9 @@ namespace BuildingBlock.Identity.Contracts.Grpc;
/// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code.</param>
public sealed record IdentityPolicyGrpcContract(string SubjectId, string TenantId, string PermissionCode);
/// <param name="Provider">Auth provider.</param>
public sealed record IdentityPolicyGrpcContract(
string SubjectId,
string TenantId,
string PermissionCode,
string Provider = "InternalJwt");

View File

@ -1,4 +1,5 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests;
@ -8,5 +9,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code to evaluate.</param>
public sealed record EvaluateIdentityPolicyRequest(string SubjectId, string TenantId, string PermissionCode)
/// <param name="Provider">Auth provider used for the request.</param>
public sealed record EvaluateIdentityPolicyRequest(
string SubjectId,
string TenantId,
string PermissionCode,
IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
: IIdentityCapabilityContract;

View File

@ -0,0 +1,17 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests;
/// <summary>
/// Requests identity provider token exchange for a subject token.
/// </summary>
/// <param name="TenantId">Tenant identifier.</param>
/// <param name="Provider">External identity provider.</param>
/// <param name="ExternalToken">Provider-issued token (id/access token).</param>
/// <param name="CorrelationId">Correlation identifier.</param>
public sealed record ExchangeIdentityProviderTokenRequest(
string TenantId,
IdentityAuthProvider Provider,
string ExternalToken,
string CorrelationId = "") : IIdentityCapabilityContract;

View File

@ -1,4 +1,5 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests;
@ -8,5 +9,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code to evaluate.</param>
public sealed record IdentityPolicyContextRequest(string SubjectId, string TenantId, string PermissionCode)
/// <param name="Provider">Auth provider used for the request.</param>
public sealed record IdentityPolicyContextRequest(
string SubjectId,
string TenantId,
string PermissionCode,
IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
: IIdentityCapabilityContract;

View File

@ -1,4 +1,5 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests;
@ -7,4 +8,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// </summary>
/// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param>
public sealed record IssueIdentityTokenRequest(string SubjectId, string TenantId) : IIdentityCapabilityContract;
/// <param name="Provider">Auth provider used for the request.</param>
/// <param name="ExternalToken">External provider token when applicable.</param>
public sealed record IssueIdentityTokenRequest(
string SubjectId,
string TenantId,
IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt,
string ExternalToken = "") : IIdentityCapabilityContract;

View File

@ -1,4 +1,5 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests;
@ -7,5 +8,9 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// </summary>
/// <param name="RefreshToken">Refresh token value.</param>
/// <param name="CorrelationId">Correlation identifier.</param>
public sealed record RefreshIdentitySessionRequest(string RefreshToken, string CorrelationId)
/// <param name="Provider">Auth provider used for the request.</param>
public sealed record RefreshIdentitySessionRequest(
string RefreshToken,
string CorrelationId,
IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
: IIdentityCapabilityContract;

View File

@ -0,0 +1,17 @@
using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Responses;
/// <summary>
/// Returns token exchange result from an external provider token.
/// </summary>
/// <param name="SubjectId">Resolved identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param>
/// <param name="Provider">External identity provider.</param>
/// <param name="IsAuthenticated">Whether provider token was accepted.</param>
public sealed record ExchangeIdentityProviderTokenResponse(
string SubjectId,
string TenantId,
IdentityAuthProvider Provider,
bool IsAuthenticated) : IIdentityCapabilityContract;

View File

@ -19,12 +19,33 @@ public class ContractShapeTests
[Fact]
public void IdentityContracts_WhenInstantiated_ImplementMarkerInterface()
{
IIdentityCapabilityContract issueRequest = new IssueIdentityTokenRequest("subject-a", "tenant-a");
IIdentityCapabilityContract issueRequest = new IssueIdentityTokenRequest(
"subject-a",
"tenant-a",
IdentityAuthProvider.AzureAd,
"external-token");
IIdentityCapabilityContract issueResponse = new IssueIdentityTokenResponse("token-a", 1800);
IIdentityCapabilityContract policyRequest = new EvaluateIdentityPolicyRequest("subject-b", "tenant-b", "identity.token.issue");
IIdentityCapabilityContract policyRequest = new EvaluateIdentityPolicyRequest(
"subject-b",
"tenant-b",
"identity.token.issue",
IdentityAuthProvider.Google);
IIdentityCapabilityContract policyResponse = new EvaluateIdentityPolicyResponse("subject-b", "identity.token.issue", true);
IIdentityCapabilityContract refreshRequest = new RefreshIdentitySessionRequest("refresh-a", "corr-refresh");
IIdentityCapabilityContract refreshRequest = new RefreshIdentitySessionRequest(
"refresh-a",
"corr-refresh",
IdentityAuthProvider.InternalJwt);
IIdentityCapabilityContract refreshResponse = new RefreshIdentitySessionResponse("token-b", 900);
IIdentityCapabilityContract exchangeRequest = new ExchangeIdentityProviderTokenRequest(
"tenant-c",
IdentityAuthProvider.AzureAd,
"provider-token",
"corr-exchange");
IIdentityCapabilityContract exchangeResponse = new ExchangeIdentityProviderTokenResponse(
"subject-c",
"tenant-c",
IdentityAuthProvider.AzureAd,
true);
Assert.NotNull(issueRequest);
Assert.NotNull(issueResponse);
@ -32,5 +53,20 @@ public class ContractShapeTests
Assert.NotNull(policyResponse);
Assert.NotNull(refreshRequest);
Assert.NotNull(refreshResponse);
Assert.NotNull(exchangeRequest);
Assert.NotNull(exchangeResponse);
}
[Fact]
public void IssueIdentityTokenRequest_WhenProviderSpecified_PreservesProviderMetadata()
{
var request = new IssueIdentityTokenRequest(
"subject-1",
"tenant-1",
IdentityAuthProvider.Google,
"google-token");
Assert.Equal(IdentityAuthProvider.Google, request.Provider);
Assert.Equal("google-token", request.ExternalToken);
}
}