Skip to content

How to Establish Trust with OpenID Federation

Audience Architects designing multi-party trust infrastructure, and developers configuring federation endpoints.
Purpose Show how to set up OpenID Federation 1.0 trust chains - configuring trust anchors on verifiers and publishing entity statements on issuers - using SdJwt.Net.OidFederation.
Scope Trust anchor configuration, trust chain resolution, entity statement publication, and automatic .well-known endpoint mapping. Out of scope: HAIP automatic resolution (note: HAIP automates trust chain resolution when enabled).
Success criteria Reader can configure a verifier with trust anchors, resolve a multi-hop trust chain to an unknown issuer, and publish an issuer entity statement that integrates into a federation tree.

Snippets in this guide are architecture-level pseudocode. For concrete package usage, see samples/SdJwt.Net.Samples/Standards/OpenId/OpenIdFederationExample.cs.


Key Decisions

Decision Options Guidance
Trust anchor selection? Single or multiple anchors Multiple for resilience
Metadata policy enforcement? Strict or permissive Strict for regulated ecosystems
Cache TTL for entity configurations? Minutes to hours 5-15 minutes typical
Fallback on resolution failure? Reject or use cached Reject if cache stale beyond TTL
Federation key separate from credential key? Yes/No Always yes for production

Prerequisites

Ensure your project references the necessary NuGet packages:

dotnet add package SdJwt.Net.OidFederation

1. Configure Federation (The Verifier)

Verifiers need to know which Root Authorities (Trust Anchors) they inherently trust. When a Verifier receives a credential from an unknown Issuer (e.g., a small rural bank), the Verifier will use Federation to see if that bank is endorsed by a trusted Authority (e.g., the National Central Bank).

Setup the Federation Client

In your Verifier's Program.cs:

using SdJwt.Net.OidFederation.Logic;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

// Register your configured trust anchors.
var trustAnchors = new Dictionary<string, SecurityKey>
{
    ["https://trust-anchor.example.com"] = trustAnchorPublicKey
};

// Register your app services and use federation primitives as needed.
var resolver = new TrustChainResolver(httpClient, trustAnchors);

var app = builder.Build();

Resolving a Trust Chain

When a Wallet presents a credential, it provides the Issuer's unique ID (usually their web address). Your Verifier must now dynamically resolve a Trust Chain to that Issuer to fetch their authentic public keys.

app.MapPost("/verify-login", async (
    PresentationResponse response,
    /* your verifier service */ verifier,
    TrustChainResolver federation) =>
{
    // The presentation claims it was issued by this bank
    string issuerId = "https://small-rural-bank.com";

    // 1. Resolve the Trust Chain!
    // The resolver automatically walks the tree backwards:
    // small-rural-bank -> Regional Authority -> National Financial Authority
    var trustChain = await federation.ResolveAsync(issuerId);

    if (!trustChain.IsValid)
    {
        // The bank is not trusted by any of our configured Trust Anchors!
        return Results.Unauthorized($"Untrusted Issuer: {trustChain.ErrorMessage}");
    }

    // 2. Crucially, the Trust Chain provides the *verified* metadata for the Issuer,
    // including their authentic Public Keys (JWKS).
    var verifiedMetadata = trustChain.ValidatedMetadata;
    var authenticPublicKeys = verifiedMetadata?.GetProtocolMetadata("openid_credential_issuer");

    // 3. Now verify the SD-JWT signature using the trusted keys
    var sdJwtResult = await verifier.VerifyPresentationAsync(response, authenticPublicKeys);

    if (sdJwtResult.IsValid)
    {
        return Results.Ok("User authenticated successfully.");
    }
});

Note: If you are using SdJwt.Net.HAIP, this trust chain resolution happens entirely automatically behind the scenes!

2. Participating in a Federation (The Issuer)

As an Issuer, you want Verifiers to trust you. To achieve this, you publish an Entity Statement (a signed JWT) at your /.well-known/openid_federation endpoint. This statement declares who you are, what your public keys are, and points to the Authorities that vouch for you.

Setup the Federation Endpoint

In your Issuer's Program.cs:

// Issuer-side federation metadata and statement publishing are application-specific.
// Use package builders/services to produce entity configuration and statements.

Automatic Endpoint Publishing

The SdJwt.Net.OidFederation package automatically maps the required .well-known endpoints.

var app = builder.Build();

// Map your federation endpoints and return signed entity statements.

When a Verifier's server hits GET https://small-rural-bank.com/.well-known/openid_federation, the package will dynamically generate, sign, and return your Entity Statement. The Verifier will then follow the AuthorityHints URL to ask the regional-financial-authority.gov if they have a signed record vouching for your bank, continuing up the chain until it hits a Trust Anchor.