Connections and tokens
FabrCore 2.0 · Release and package availability
These guides track the current 2.0 source. Stable 2.0.0 publication is pending; package commands show the release target. Until it is published, follow the source quick start or use a matching available prerelease set. Release migration · Runtime modes
Connect a FabrCore principal to Microsoft, Google or an OIDC provider once, then explicitly grant agents access through named connections.
Your client application owns login and consent. FabrCore provides APIs, protected storage and token acquisition for trusted agent code. Connections are optional and provider-neutral; standalone agents need no external identity or cloud server.
For Copilot Studio, Work IQ and optional Entra Agent ID, follow the Microsoft integration guide.
Host setup
Reference the service packages that the host intends to enable. FabrCore Host now supplies credential protection automatically when connections are enabled. This uses the Data Protection cryptographic library, adds no UI, and does not change an application's authentication-cookie protection.
using Microsoft.AspNetCore.Builder;
using FabrCore.Host;
using FabrCore.Services.Connections;
using FabrCore.Services.RemoteAgents;
var builder = WebApplication.CreateBuilder(args);
builder.AddFabrCoreServer(new FabrCoreServerOptions {
AdditionalAssemblies = [typeof(ConnectionsExtensions).Assembly,
typeof(RemoteAgent).Assembly]
});
builder.Services.AddFabrCoreConnections(o => {
o.Enabled = true;
o.EntraAgentIdEnabled = false;
o.ClientHandoffEnabled = false;
});
builder.Services.AddFabrCoreRemoteAgents(o => o.Enabled = true);
// Also configure the host's normal user authentication/authorization.
var app = builder.Build();
app.UseFabrCoreServer();
app.MapFabrCoreConnections();
app.Run();
Automatic Data Protection
FabrCore:DataProtection:Mode defaults to Auto:
| Effective persistence | Automatic protection |
|---|---|
| Default Localhost with in-memory entities/grains | One singleton ephemeral provider; keys and connection state are lost on restart |
| Integrated SQL database, including explicit Localhost clustering | Encrypted shared key ring in the operational database |
| Orleans-only SqlServer mode | Encrypted shared key ring in StorageConnectionString, falling back to ConnectionString |
| Azure/custom persistence without SQL | Explicit shared provider required; never silently use ephemeral keys |
For SQL, supply the deployment's key-encryption certificate through protected host configuration. FabrCore manages the key repository and registration:
{
"FabrCore": {
"DataProtection": {
"ApplicationName": "my-cluster-production",
"CertificatePath": "/run/secrets/fabrcore-protection.pfx"
}
}
}
Use CertificatePassword from a secret provider if the PFX is password protected.
All silos require the private key. The database stores encrypted Data Protection
key XML in fabrOps.DataProtectionKey; it does not store the certificate private key.
Default application identity combines Orleans service ID, cluster ID, and host
environment. Set a unique stable ApplicationName when sharing a database across
clusters. Changing that name makes previous protected records inaccessible.
The key table is initialized only when protection is activated, using the existing
FabrCore:Database:AutoInitialize policy. For manual-schema deployments apply the
key-ring migration to the operational database.
Orleans-only SQL uses its AutoInitDatabase setting instead. Startup checks key
protection before accepting consent; missing certificates/schema or an unavailable
database cause failure without an ephemeral fallback. Existing cached keys can
continue to protect data during a later database outage.
For certificate rotation, distribute the new PFX to every silo, change
CertificatePath, and retain old private keys under
PreviousCertificates:0:Path / PreviousCertificates:0:Password (and subsequent
indices). Restart hosts to apply changes. Retain old decryption certificates and
key-ring rows while protected records need them; back up both. Switching providers
requires preserving the existing key ring and application identity, not generating
a replacement key ring for existing data. Protect silo-to-silo traffic as well.
An explicitly configured standard Data Protection repository with key encryption
is preserved by Auto. For other custom providers, set Mode=Custom and register
IDataProtectionProvider, or directly register IFabrCoreDataProtectionProvider
from FabrCore.Host.Security. Custom providers own their persistence/encryption
guarantees. Use this for vault-backed protection or other deployment-specific
stores. Mode=Ephemeral is rejected for known durable persistence. The old
ProtectedKeyRingConfigured property remains for source compatibility but is no
longer required or treated as proof of a working provider.
Omitting registration or setting Enabled = false adds no connection services,
workers, endpoints, or external requests. A disabled profile cannot acquire tokens.
Feature registration is host bootstrap configuration; an administrator cannot
enable Agent ID merely by submitting an agent blueprint.
Ownership and authorization
A connection is identified by (ownerPrincipal, name). An agent declares aliases
for those bindings, and the profile separately grants exact full agent handles.
Both are necessary. Delegated connections require the agent's owning principal to
match the connection owner. Application connections may belong to a service
principal and explicitly grant agents belonging to other principals.
The SDK obtains the principal and agent handle from trusted host context. Prompts cannot supply those identities. Diagnostic turns cannot access production connections. Do not expose raw-token methods as model tools.
The default user API resolver maps authenticated Entra tid and oid through
EntraPrincipalHandle.Create. Other client authentication systems register an
IConnectionPrincipalResolver which maps validated claims to the canonical
FabrCore principal. A connected Google account can belong to that FabrCore
principal without changing its FabrCore ID.
Profiles and application credentials
Profiles contain metadata and credential references, never secret values. Create
through FabrCoreConnectionAdministrationClient.SaveAsync(principal, profile, "*").
For updates, read GetAsync and pass the returned revision. Stale revisions fail
with 412; writes are not automatically retried. Any profile update invalidates
cached authorization and remote conversation bindings.
var mail = new ConnectionProfile {
Name = "mail-service",
Enabled = true,
Provider = "microsoft",
Authentication = ConnectionAuthentication.ClientCredentials,
Authority = $"https://login.microsoftonline.com/{tenantId}/v2.0",
ClientId = appRegistrationClientId,
CredentialReference = "mail-application",
AllowedAgents = ["alice:mail-agent"],
Resources = new() {
["graph"] = new() {
BaseUrl = "https://graph.microsoft.com/v1.0/",
Scopes = ["https://graph.microsoft.com/.default"]
}
}
};
await admin.SaveAsync("mail-service-principal", mail, "*");
The app registration must have the appropriate Microsoft Graph application
permissions and tenant administrator consent. For app-only mail access use
users/{mailbox}/messages, not /me. Provider-side mailbox restrictions continue
to apply. FabrCore grants access to the connection; it does not grant Graph
permissions. Configure only the permissions/mailboxes the application needs.
The default credential provider resolves one of these server configuration keys:
FabrCore:ConnectionCredentials:mail-application:SecretFabrCore:ConnectionCredentials:mail-application:CertificatePathand optionalCertificatePasswordfor a signing certificate with its private key.
Supply these through the host's protected configuration/vault system. Do not put
them in agent arguments, blueprints, cloud configuration documents, or client
requests. Certificate assertions are short-lived. An application can replace
IConnectionCredentialProvider to obtain federated client assertions or use a
different vault/HSM. Automatic managed-identity/federation discovery is not built
into the default provider.
Client credentials acquire a new access token when necessary; they do not need a refresh token. Disconnect clears local cached authorization, but an enabled application profile can acquire another token. Disable the profile to stop use.
User consent in an external client
An authorization-code profile declares Authority, ClientId, exact HTTPS
RedirectUris, SignInScopes including openid, and allowed resources. Request
offline_access with Microsoft when continued refresh is needed. For Google,
use https://accounts.google.com; the provider adapter requests offline access.
Google service-account/domain-wide delegation uses a different grant and is not
implemented by the standard OAuth ClientCredentials mode.
The app registration must allow the same callback and OAuth client configuration.
If the client is confidential, configure its credential reference on the host.
// http authenticates the current user to FabrCore, not to Graph or Google.
var connections = new FabrCoreConnectionsClient(http);
var transaction = await connections.BeginAsync(
new("work", "https://my-client.example/connections/callback"));
// Client navigates the browser to transaction.AuthorizationUrl.
// Remember transaction.State and connection name in the client's session.
// On the registered callback, verify returned state against that client session.
await connections.CompleteAsync(new(
"work", returnedState, returnedCode, returnedProviderError));
FabrCore generates state, nonce, and PKCE verifier, stores them protected for ten minutes, and exchanges the code. The callback is on the external client. Complete must authenticate as the same FabrCore principal as begin. A transaction is consumed before exchange and cannot be replayed. An uncertain exchange requires new consent rather than replaying a code. FabrCore validates the ID token's signature, issuer, audience, lifetime, and nonce before linking the account.
Authorization codes, assertions, tokens, and callback query strings must be excluded from application HTTP body/query logging. Cookie clients need a same-origin mutation request. Cross-origin client apps should use authenticated bearer requests and the host's explicit CORS policy.
The connection APIs return status, never access or refresh tokens. Provider
interaction/consent failures return interaction-required. Refresh is serialized
by a principal-owned Orleans grain, including refresh-token rotation.
Agent SDK usage and MCP
Inside a FabrCoreAgentProxy implementation:
using var graph = await Connections.GetHttpClientAsync("mail", "graph", ct);
var messages = await graph.GetStringAsync(
"users/" + Uri.EscapeDataString(mailbox) + "/messages?$top=10", ct);
// Only when a trusted SDK requires the raw token:
var token = await Connections.GetAccessTokenAsync("mail", "graph", ct);
await trustedSdk.CallAsync(token.AccessToken, ct);
The authenticated client acquires/renews a token for each request, restricts URLs
to the configured resource base, disables cookies and redirects, and fails if its
connection is replaced or disconnected. Tokens are excluded from JSON serialization
and ToString; trusted code must still avoid logging AccessToken itself.
For HTTP MCP, set McpServerConfig.Connection to the agent's alias and Resource
to its configured resource name. Do not supply a static Authorization header.
Existing unauthenticated/static-header MCP configurations keep their behavior.
After reauthorizing/replacing a connection, reconfigure the agent to establish a
new MCP session. Token renewal within the same authorization works per request.
Reusable blueprints
connectedAgents is a top-level canonical blueprint extension. Defaults apply
to every agent in that group. An agent can override individual bindings through
its args.connections JSON string. $principal resolves at deployment time.
Expansion and preview compute configurations only: no provisioning, grants,
consent, token acquisition, or remote requests happen during preview.
{
"name": "work-assistant",
"connectedAgents": {
"connections": {
"work": { "ownerPrincipal": "$principal", "name": "workiq" }
},
"agents": [{
"handle": "copilot",
"agentType": "remote-agent",
"args": {
"provider": "work-iq",
"connection": "work",
"resource": "workiq",
"endpoint": "https://workiq.svc.cloud.microsoft/a2a/",
"timeZone": "America/Chicago"
}
}]
}
}
Provision the workiq profile separately and allow the deployed full handle,
for example alice:copilot. For a service connection use its explicit owner
instead of $principal. Agent ID bindings live on the referenced connection
profile; blueprints reuse them without carrying credentials or provisioning
Microsoft directory objects.
Cloud administration and encrypted handoffs
Capability discovery advertises connections, connectedAgents, and the
separately enabled entra-agent-id / encrypted-client-handoff features.
Administration uses the existing privileged outbound cloud command channel:
| Method | Path under /fabrcoreapi/admin/v1/principals/{principal}/connections |
|---|---|
| GET | / — statuses |
| GET | /{name} — profile and revision |
| PUT | /{name} — profile; required If-Match revision (* for create) |
| DELETE | /{name}/authorization — clear cached authorization |
| POST | /{name}/handoff — short-lived public key challenge |
| POST | /{name}/handoff/complete — encrypted user operation |
User endpoints under /fabrcoreapi/connections/v1 provide GET status, POST
begin, complete, assertion, and DELETE /{name}. They use authenticated user
identity and do not accept an owner override. They are not the cloud admin routes.
For clients that reach the cluster through a cloud broker, enable
ClientHandoffEnabled and configure HandoffAuthority / HandoffAudience for a
delegated access token intended for the FabrCore API. The default validator uses
Entra user claims. Other authentication systems implement
IConnectionHandoffPrincipalValidator; it must validate a signed token and map
the trusted subject to a FabrCore principal. Never trust an owner string from
the broker as proof that the user consented.
// The app obtains this challenge through its authorized cloud backend.
var challenge = await admin.CreateHandoffAsync(owner, "work");
// Encrypt in the client BEFORE sending the result to the cloud backend.
var envelope = ConnectionHandoff.Encrypt(challenge, new() {
UserProof = fabrcoreAudienceUserToken,
Operation = "complete",
State = returnedState,
AuthorizationCode = returnedCode
});
// Only envelope crosses the durable cloud command channel.
await admin.CompleteHandoffAsync(owner, "work", envelope);
Use a fresh challenge for each operation (begin, complete, assertion, or
disconnect). Challenges expire in five minutes and are consumed durably before
decryption/validation. AES-256-GCM encrypts the payload; RSA-OAEP-SHA256 wraps its
key. Private keys and grants are encrypted in cluster storage. TLS and a trusted
cloud broker are still required to deliver the authentic cluster public key.
Neither administrator identity nor possession of an envelope replaces user proof.
Insights exposes these admin paths through its existing scoped cluster proxy and connect broker. Its environment Connections page manages metadata, grants, and disconnects. The client consent UI belongs to the external application. Any other cloud server can implement the same transport. The reference cloud server can forward these endpoints unchanged; it is a single-process conformance fixture, not a durable production broker. Never send plaintext assertions or codes through its ordinary command API.
Validation and current boundaries
Automated coverage includes disabled registration, principal isolation, explicit application grants, bounded HTTP destinations, token redaction, protected Orleans persistence, conditional profile updates, stale client rejection, handoff owner proof/replay/tampering, OAuth wire exchanges, and Work IQ stream/task parsing. No real Microsoft/Google tenant credentials were used for validation. Deployment requires tenant consent and live smoke tests for login, renewal, Studio, Work IQ, and any selected Agent ID combination. Provider-side revocation and consent remain provider operations; disconnect here clears FabrCore's local authorization.
References: Work IQ A2A, Work IQ API, Entra Agent ID, Microsoft Agents SDK, OAuth client credentials.