diff --git a/.gitignore b/.gitignore
index 31c7257..89d521f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,8 @@
.tasks/
.agile/
+bin/
+obj/
+TestResults/
+.vs/
+*.user
+*.suo
diff --git a/BuildingBlock.Identity.slnx b/BuildingBlock.Identity.slnx
new file mode 100644
index 0000000..3ca3850
--- /dev/null
+++ b/BuildingBlock.Identity.slnx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/docs/migration/contract-extraction-map.md b/docs/migration/contract-extraction-map.md
index a1a120e..e0860fa 100644
--- a/docs/migration/contract-extraction-map.md
+++ b/docs/migration/contract-extraction-map.md
@@ -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.
diff --git a/src/BuildingBlock.Identity.Contracts/Abstractions/IIdentityCapabilityContract.cs b/src/BuildingBlock.Identity.Contracts/Abstractions/IIdentityCapabilityContract.cs
new file mode 100644
index 0000000..f5a73bc
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Abstractions/IIdentityCapabilityContract.cs
@@ -0,0 +1,8 @@
+namespace BuildingBlock.Identity.Contracts.Abstractions;
+
+///
+/// Marker contract for identity capability request/response definitions.
+///
+public interface IIdentityCapabilityContract
+{
+}
diff --git a/src/BuildingBlock.Identity.Contracts/Adapters/IIdentityGrpcContractAdapter.cs b/src/BuildingBlock.Identity.Contracts/Adapters/IIdentityGrpcContractAdapter.cs
new file mode 100644
index 0000000..b985ccb
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Adapters/IIdentityGrpcContractAdapter.cs
@@ -0,0 +1,20 @@
+using BuildingBlock.Identity.Contracts.Grpc;
+using BuildingBlock.Identity.Contracts.Requests;
+
+namespace BuildingBlock.Identity.Contracts.Adapters;
+
+///
+/// Defines gRPC mapping boundary for identity capability requests.
+///
+public interface IIdentityGrpcContractAdapter
+{
+ ///
+ /// Maps request into gRPC contract shape.
+ ///
+ IdentityPolicyGrpcContract ToGrpc(EvaluateIdentityPolicyRequest request);
+
+ ///
+ /// Maps gRPC contract shape into request.
+ ///
+ EvaluateIdentityPolicyRequest FromGrpc(IdentityPolicyGrpcContract contract);
+}
diff --git a/src/BuildingBlock.Identity.Contracts/BuildingBlock.Identity.Contracts.csproj b/src/BuildingBlock.Identity.Contracts/BuildingBlock.Identity.Contracts.csproj
new file mode 100644
index 0000000..04a4bbc
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/BuildingBlock.Identity.Contracts.csproj
@@ -0,0 +1,10 @@
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
diff --git a/src/BuildingBlock.Identity.Contracts/Conventions/IIdentityContractRequest.cs b/src/BuildingBlock.Identity.Contracts/Conventions/IIdentityContractRequest.cs
new file mode 100644
index 0000000..bd263d7
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Conventions/IIdentityContractRequest.cs
@@ -0,0 +1,12 @@
+namespace BuildingBlock.Identity.Contracts.Conventions;
+
+///
+/// Defines shared envelope metadata for identity capability requests.
+///
+public interface IIdentityContractRequest
+{
+ ///
+ /// Gets request envelope metadata.
+ ///
+ IdentityContractEnvelope Envelope { get; }
+}
diff --git a/src/BuildingBlock.Identity.Contracts/Conventions/IdentityAuthProvider.cs b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityAuthProvider.cs
new file mode 100644
index 0000000..6d4001b
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityAuthProvider.cs
@@ -0,0 +1,22 @@
+namespace BuildingBlock.Identity.Contracts.Conventions;
+
+///
+/// Supported identity authentication providers.
+///
+public enum IdentityAuthProvider
+{
+ ///
+ /// AgileWebs-issued internal JWT flow.
+ ///
+ InternalJwt = 0,
+
+ ///
+ /// Microsoft Entra ID / Azure AD OAuth/OIDC flow.
+ ///
+ AzureAd = 1,
+
+ ///
+ /// Google OAuth/OIDC flow.
+ ///
+ Google = 2
+}
diff --git a/src/BuildingBlock.Identity.Contracts/Conventions/IdentityContractEnvelope.cs b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityContractEnvelope.cs
new file mode 100644
index 0000000..30f6546
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityContractEnvelope.cs
@@ -0,0 +1,8 @@
+namespace BuildingBlock.Identity.Contracts.Conventions;
+
+///
+/// Defines transport-neutral envelope metadata for identity contract messages.
+///
+/// Contract schema version.
+/// Correlation identifier for cross-service tracing.
+public sealed record IdentityContractEnvelope(string ContractVersion, string CorrelationId);
diff --git a/src/BuildingBlock.Identity.Contracts/Conventions/IdentityPackageContract.cs b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityPackageContract.cs
new file mode 100644
index 0000000..fee6a59
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Conventions/IdentityPackageContract.cs
@@ -0,0 +1,8 @@
+namespace BuildingBlock.Identity.Contracts.Conventions;
+
+///
+/// Marker type for identity capability package discovery.
+///
+public sealed class IdentityPackageContract
+{
+}
diff --git a/src/BuildingBlock.Identity.Contracts/Grpc/IdentityPolicyGrpcContract.cs b/src/BuildingBlock.Identity.Contracts/Grpc/IdentityPolicyGrpcContract.cs
new file mode 100644
index 0000000..d508935
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Grpc/IdentityPolicyGrpcContract.cs
@@ -0,0 +1,14 @@
+namespace BuildingBlock.Identity.Contracts.Grpc;
+
+///
+/// Defines transport-neutral shape for policy evaluation gRPC contract mapping.
+///
+/// Identity subject identifier.
+/// Tenant identifier.
+/// Permission code.
+/// Auth provider.
+public sealed record IdentityPolicyGrpcContract(
+ string SubjectId,
+ string TenantId,
+ string PermissionCode,
+ string Provider = "InternalJwt");
diff --git a/src/BuildingBlock.Identity.Contracts/Requests/EvaluateIdentityPolicyRequest.cs b/src/BuildingBlock.Identity.Contracts/Requests/EvaluateIdentityPolicyRequest.cs
new file mode 100644
index 0000000..f60ad23
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Requests/EvaluateIdentityPolicyRequest.cs
@@ -0,0 +1,18 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Requests;
+
+///
+/// Requests identity policy evaluation for a subject and permission.
+///
+/// Identity subject identifier.
+/// Tenant identifier.
+/// Permission code to evaluate.
+/// Auth provider used for the request.
+public sealed record EvaluateIdentityPolicyRequest(
+ string SubjectId,
+ string TenantId,
+ string PermissionCode,
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
+ : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Requests/ExchangeIdentityProviderTokenRequest.cs b/src/BuildingBlock.Identity.Contracts/Requests/ExchangeIdentityProviderTokenRequest.cs
new file mode 100644
index 0000000..8b992f5
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Requests/ExchangeIdentityProviderTokenRequest.cs
@@ -0,0 +1,17 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Requests;
+
+///
+/// Requests identity provider token exchange for a subject token.
+///
+/// Tenant identifier.
+/// External identity provider.
+/// Provider-issued token (id/access token).
+/// Correlation identifier.
+public sealed record ExchangeIdentityProviderTokenRequest(
+ string TenantId,
+ IdentityAuthProvider Provider,
+ string ExternalToken,
+ string CorrelationId = "") : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Requests/IdentityPolicyContextRequest.cs b/src/BuildingBlock.Identity.Contracts/Requests/IdentityPolicyContextRequest.cs
new file mode 100644
index 0000000..7ab75d5
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Requests/IdentityPolicyContextRequest.cs
@@ -0,0 +1,18 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Requests;
+
+///
+/// Requests identity policy context lookup.
+///
+/// Identity subject identifier.
+/// Tenant identifier.
+/// Permission code to evaluate.
+/// Auth provider used for the request.
+public sealed record IdentityPolicyContextRequest(
+ string SubjectId,
+ string TenantId,
+ string PermissionCode,
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
+ : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Requests/IssueIdentityTokenRequest.cs b/src/BuildingBlock.Identity.Contracts/Requests/IssueIdentityTokenRequest.cs
new file mode 100644
index 0000000..7154b43
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Requests/IssueIdentityTokenRequest.cs
@@ -0,0 +1,17 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Requests;
+
+///
+/// Requests identity token issuance.
+///
+/// Identity subject identifier.
+/// Tenant identifier.
+/// Auth provider used for the request.
+/// External provider token when applicable.
+public sealed record IssueIdentityTokenRequest(
+ string SubjectId,
+ string TenantId,
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt,
+ string ExternalToken = "") : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Requests/RefreshIdentitySessionRequest.cs b/src/BuildingBlock.Identity.Contracts/Requests/RefreshIdentitySessionRequest.cs
new file mode 100644
index 0000000..9050aae
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Requests/RefreshIdentitySessionRequest.cs
@@ -0,0 +1,16 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Requests;
+
+///
+/// Requests identity session refresh.
+///
+/// Refresh token value.
+/// Correlation identifier.
+/// Auth provider used for the request.
+public sealed record RefreshIdentitySessionRequest(
+ string RefreshToken,
+ string CorrelationId,
+ IdentityAuthProvider Provider = IdentityAuthProvider.InternalJwt)
+ : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Responses/EvaluateIdentityPolicyResponse.cs b/src/BuildingBlock.Identity.Contracts/Responses/EvaluateIdentityPolicyResponse.cs
new file mode 100644
index 0000000..c6ca817
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Responses/EvaluateIdentityPolicyResponse.cs
@@ -0,0 +1,12 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+
+namespace BuildingBlock.Identity.Contracts.Responses;
+
+///
+/// Returns identity policy evaluation result.
+///
+/// Identity subject identifier.
+/// Permission code evaluated.
+/// Whether access is granted.
+public sealed record EvaluateIdentityPolicyResponse(string SubjectId, string PermissionCode, bool IsAllowed)
+ : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Responses/ExchangeIdentityProviderTokenResponse.cs b/src/BuildingBlock.Identity.Contracts/Responses/ExchangeIdentityProviderTokenResponse.cs
new file mode 100644
index 0000000..3ebee51
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Responses/ExchangeIdentityProviderTokenResponse.cs
@@ -0,0 +1,17 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+
+namespace BuildingBlock.Identity.Contracts.Responses;
+
+///
+/// Returns token exchange result from an external provider token.
+///
+/// Resolved identity subject identifier.
+/// Tenant identifier.
+/// External identity provider.
+/// Whether provider token was accepted.
+public sealed record ExchangeIdentityProviderTokenResponse(
+ string SubjectId,
+ string TenantId,
+ IdentityAuthProvider Provider,
+ bool IsAuthenticated) : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Responses/IdentityPolicyContextResponse.cs b/src/BuildingBlock.Identity.Contracts/Responses/IdentityPolicyContextResponse.cs
new file mode 100644
index 0000000..6a2e77f
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Responses/IdentityPolicyContextResponse.cs
@@ -0,0 +1,12 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+
+namespace BuildingBlock.Identity.Contracts.Responses;
+
+///
+/// Returns policy context lookup result.
+///
+/// Identity subject identifier.
+/// Permission code evaluated.
+/// Whether contextual policy requirements were satisfied.
+public sealed record IdentityPolicyContextResponse(string SubjectId, string PermissionCode, bool ContextSatisfied)
+ : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Responses/IssueIdentityTokenResponse.cs b/src/BuildingBlock.Identity.Contracts/Responses/IssueIdentityTokenResponse.cs
new file mode 100644
index 0000000..8a77465
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Responses/IssueIdentityTokenResponse.cs
@@ -0,0 +1,10 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+
+namespace BuildingBlock.Identity.Contracts.Responses;
+
+///
+/// Returns issued identity token payload.
+///
+/// Issued token value.
+/// Token lifetime in seconds.
+public sealed record IssueIdentityTokenResponse(string Token, int ExpiresInSeconds) : IIdentityCapabilityContract;
diff --git a/src/BuildingBlock.Identity.Contracts/Responses/RefreshIdentitySessionResponse.cs b/src/BuildingBlock.Identity.Contracts/Responses/RefreshIdentitySessionResponse.cs
new file mode 100644
index 0000000..77d43b5
--- /dev/null
+++ b/src/BuildingBlock.Identity.Contracts/Responses/RefreshIdentitySessionResponse.cs
@@ -0,0 +1,11 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+
+namespace BuildingBlock.Identity.Contracts.Responses;
+
+///
+/// Returns refreshed identity session token payload.
+///
+/// Refreshed token value.
+/// Token lifetime in seconds.
+public sealed record RefreshIdentitySessionResponse(string Token, int ExpiresInSeconds)
+ : IIdentityCapabilityContract;
diff --git a/tests/BuildingBlock.Identity.Contracts.UnitTests/BuildingBlock.Identity.Contracts.UnitTests.csproj b/tests/BuildingBlock.Identity.Contracts.UnitTests/BuildingBlock.Identity.Contracts.UnitTests.csproj
new file mode 100644
index 0000000..6b898fe
--- /dev/null
+++ b/tests/BuildingBlock.Identity.Contracts.UnitTests/BuildingBlock.Identity.Contracts.UnitTests.csproj
@@ -0,0 +1,26 @@
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
diff --git a/tests/BuildingBlock.Identity.Contracts.UnitTests/ContractShapeTests.cs b/tests/BuildingBlock.Identity.Contracts.UnitTests/ContractShapeTests.cs
new file mode 100644
index 0000000..fa0ab12
--- /dev/null
+++ b/tests/BuildingBlock.Identity.Contracts.UnitTests/ContractShapeTests.cs
@@ -0,0 +1,72 @@
+using BuildingBlock.Identity.Contracts.Abstractions;
+using BuildingBlock.Identity.Contracts.Conventions;
+using BuildingBlock.Identity.Contracts.Requests;
+using BuildingBlock.Identity.Contracts.Responses;
+
+namespace BuildingBlock.Identity.Contracts.UnitTests;
+
+public class ContractShapeTests
+{
+ [Fact]
+ public void IdentityEnvelope_WhenCreated_PreservesValues()
+ {
+ var envelope = new IdentityContractEnvelope("1.0.0", "corr-identity-001");
+
+ Assert.Equal("1.0.0", envelope.ContractVersion);
+ Assert.Equal("corr-identity-001", envelope.CorrelationId);
+ }
+
+ [Fact]
+ public void IdentityContracts_WhenInstantiated_ImplementMarkerInterface()
+ {
+ 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",
+ IdentityAuthProvider.Google);
+ IIdentityCapabilityContract policyResponse = new EvaluateIdentityPolicyResponse("subject-b", "identity.token.issue", true);
+ 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);
+ Assert.NotNull(policyRequest);
+ 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);
+ }
+}