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 contracts | Contracts/Policies | Preserve policy semantics and required fields |
| thalos-service policy context contracts | Contracts/Context | Keep field naming stable for compatibility window | | 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 | | 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 ## Namespace Strategy
- Current Thalos namespaces are mapped to `BuildingBlock.Identity.Contracts.*`. - Current Thalos namespaces are mapped to `BuildingBlock.Identity.Contracts.*`.
@ -18,3 +19,4 @@
2. Add compatibility bridge in Thalos consumers. 2. Add compatibility bridge in Thalos consumers.
3. Migrate service consumers first, then BFF consumers. 3. Migrate service consumers first, then BFF consumers.
4. Deprecate old namespace usage after compatibility window. 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="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param> /// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code.</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.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests; namespace BuildingBlock.Identity.Contracts.Requests;
@ -8,5 +9,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// <param name="SubjectId">Identity subject identifier.</param> /// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param> /// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code to evaluate.</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; : 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.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests; namespace BuildingBlock.Identity.Contracts.Requests;
@ -8,5 +9,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// <param name="SubjectId">Identity subject identifier.</param> /// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant identifier.</param> /// <param name="TenantId">Tenant identifier.</param>
/// <param name="PermissionCode">Permission code to evaluate.</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; : IIdentityCapabilityContract;

View File

@ -1,4 +1,5 @@
using BuildingBlock.Identity.Contracts.Abstractions; using BuildingBlock.Identity.Contracts.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests; namespace BuildingBlock.Identity.Contracts.Requests;
@ -7,4 +8,10 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// </summary> /// </summary>
/// <param name="SubjectId">Identity subject identifier.</param> /// <param name="SubjectId">Identity subject identifier.</param>
/// <param name="TenantId">Tenant 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.Abstractions;
using BuildingBlock.Identity.Contracts.Conventions;
namespace BuildingBlock.Identity.Contracts.Requests; namespace BuildingBlock.Identity.Contracts.Requests;
@ -7,5 +8,9 @@ namespace BuildingBlock.Identity.Contracts.Requests;
/// </summary> /// </summary>
/// <param name="RefreshToken">Refresh token value.</param> /// <param name="RefreshToken">Refresh token value.</param>
/// <param name="CorrelationId">Correlation identifier.</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; : 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] [Fact]
public void IdentityContracts_WhenInstantiated_ImplementMarkerInterface() 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 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 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 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(issueRequest);
Assert.NotNull(issueResponse); Assert.NotNull(issueResponse);
@ -32,5 +53,20 @@ public class ContractShapeTests
Assert.NotNull(policyResponse); Assert.NotNull(policyResponse);
Assert.NotNull(refreshRequest); Assert.NotNull(refreshRequest);
Assert.NotNull(refreshResponse); 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);
} }
} }