Skip to content

feat: add OpenID Federation entity configuration endpoint - #514

Merged
masv3971 merged 11 commits into
SUNET:mainfrom
sirosfoundation:feat/federation-entity-config
Jul 30, 2026
Merged

feat: add OpenID Federation entity configuration endpoint#514
masv3971 merged 11 commits into
SUNET:mainfrom
sirosfoundation:feat/federation-entity-config

Conversation

@leifj

@leifj leifj commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Implement /.well-known/openid-federation for both APIGW (issuer) and verifier services per OpenID Federation 1.0 §5.2.

Changes

  • New pkg/federation package providing:
    • Config type for YAML configuration
    • Service that builds and signs entity configuration JWTs
    • EntityMetadata types for typed metadata sections
  • Entity configuration endpoint registered on both services
  • Self-signed JWT containing entity metadata, JWKS, authority hints, and trust marks
  • Configuration via federation: section in YAML config

Configuration Example

federation:
  enabled: true
  entity_id: "https://issuer.example.com"
  authority_hints:
    - "https://trust-anchor.example.com"
  organization_name: "Example Org"
  ttl: 86400

When federation.enabled is false (default), the endpoint returns 404.

Context

Phase 3a of the client-id-strategy plan — enables SUNET/vc to participate in OpenID Federation trust chains.

Implement /.well-known/openid-federation for both APIGW (issuer) and
verifier services per OpenID Federation 1.0 §5.2.

The endpoint serves a self-signed JWT containing:
- Entity metadata (openid_credential_issuer, openid_relying_party, etc.)
- JWKS with the service's signing key
- Authority hints for trust chain resolution
- Optional trust marks

New pkg/federation package provides:
- Config type for YAML configuration
- Service that builds and signs entity configuration JWTs
- EntityMetadata types for typed metadata sections

Configuration:
  federation:
    enabled: true
    entity_id: "https://issuer.example.com"  # defaults to public_url
    authority_hints:
      - "https://trust-anchor.example.com"
    organization_name: "Example Org"
    ttl: 86400

When federation.enabled is false (default), the endpoint returns 404.

Phase 3a of the client-id-strategy plan.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements OpenID Federation entity configuration (/.well-known/openid-federation) for both the APIGW (issuer) and verifier services, backed by a new pkg/federation package that constructs and signs entity-statement JWTs from YAML configuration.

Changes:

  • Adds pkg/federation (config/types + service for building/signing entity configuration JWTs) and initial unit tests.
  • Extends APIGW and Verifier YAML config models with an optional federation: section.
  • Registers /.well-known/openid-federation on both services and serves a self-signed entity statement JWT when enabled.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/model/config.go Adds Federation *federation.Config to APIGW/Verifier configuration.
pkg/federation/entity_config.go Introduces federation config schema + entity configuration/metadata/trust-mark types.
pkg/federation/service.go Implements entity configuration JWT claim construction + signing.
pkg/federation/federation_test.go Adds tests for federation types/claims structures (but not the signer-backed build path).
internal/apigw/httpserver/service.go Registers the APIGW federation well-known endpoint.
internal/apigw/httpserver/endpoints_federation.go Serves APIGW entity statement JWT (issuer/AS metadata).
internal/verifier/httpserver/service.go Registers the verifier federation well-known endpoint.
internal/verifier/httpserver/endpoints_federation.go Serves verifier entity statement JWT (RP metadata).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/apigw/httpserver/endpoints_federation.go Outdated
Comment thread internal/apigw/httpserver/endpoints_federation.go Outdated
Comment thread internal/apigw/httpserver/endpoints_federation.go Outdated
Comment thread internal/verifier/httpserver/endpoints_federation.go Outdated
Comment thread internal/verifier/httpserver/endpoints_federation.go Outdated
Comment thread internal/verifier/httpserver/endpoints_openidfederation.go Outdated
Comment thread pkg/openidfederation/federation_test.go
Address review feedback: add TestBuildEntityConfigurationSignedJWT that
generates an ephemeral ECDSA P-256 key, signs a real entity configuration
JWT via BuildEntityConfiguration(), then parses the JWT with ParseUnverified
and asserts all claims (iss, sub, authority_hints, metadata, jwks, trust_marks,
exp, iat) are correctly structured.
Comment thread internal/verifier/httpserver/endpoints_federation.go Outdated
Derive the request_object_signing_alg in verifier RP metadata from the
actual SignerConfig key material instead of hard-coding ES256.
Also use federation service's EntityID for client_id consistency.

Addresses review comment on PR SUNET#514.
@leifj
leifj requested a review from s-jairl July 14, 2026 10:04

@s-jairl s-jairl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

internal/apigw/httpserver/endpoints_federation.go:19

  • Returning nil after calling c.Status(http.StatusNotFound) will be overwritten by RegEndpoint's defaultStatus (it unconditionally calls c.Status(defaultStatus) when res == nil). Use AbortWithStatus (writes headers immediately) so the 404 is preserved when federation is disabled.
	cfg := s.cfg.APIGW.Federation
	if cfg == nil || !cfg.Enabled {
		c.Status(http.StatusNotFound)
		return nil, nil
	}

internal/verifier/httpserver/endpoints_federation.go:19

  • Returning nil after calling c.Status(http.StatusNotFound) will be overwritten by RegEndpoint's defaultStatus (it unconditionally calls c.Status(defaultStatus) when res == nil). Use AbortWithStatus (writes headers immediately) so the 404 is preserved when federation is disabled.
	cfg := s.cfg.Verifier.Federation
	if cfg == nil || !cfg.Enabled {
		c.Status(http.StatusNotFound)
		return nil, nil
	}

Comment thread internal/verifier/httpserver/endpoints_federation.go
Comment thread pkg/federation/service.go Outdated
Comment thread pkg/federation/entity_config.go
Comment thread pkg/federation/federation_test.go Outdated
- Omit vp_formats from openid_relying_party metadata entirely when
  PreferredVPFormats is unset, instead of serializing it as null.
  Extracted the map-building into buildOpenIDRelyingPartyMetadata for
  testability.
- Make BuildEntityConfiguration clone the caller-supplied *EntityMetadata
  (and its maps) before injecting federation_entity fields, so callers
  that reuse a metadata instance across requests never see mutation or
  cross-request state leakage.
- Add validate:"omitempty,dive" to Config.TrustMarks so nested
  TrustMarkConfig required fields (id/jwt) are actually checked by the
  validator, matching the dive convention used elsewhere in the codebase.
- Fix a stale test comment that claimed no test used a real signer, even
  though TestBuildEntityConfigurationSignedJWT already does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@leifj

leifj commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all 4 Copilot review comments:

  1. vp_formats null when unconfigured (internal/verifier/httpserver/endpoints_federation.go) — extracted the openid_relying_party map-building into buildOpenIDRelyingPartyMetadata, which now only sets the vp_formats key when PreferredVPFormats is non-nil, so the key is omitted entirely instead of serialized as null. Added a unit test covering both cases.

  2. BuildEntityConfiguration mutating caller's metadata (pkg/federation/service.go) — added cloneMetadata/cloneMetadataMap helpers; the function now clones the input *EntityMetadata and all of its map fields before injecting federation_entity org/logo data, so the caller's original object (and its maps) are never mutated. Added a test that reuses one *EntityMetadata across two differently-configured BuildEntityConfiguration calls and asserts no cross-request leakage.

  3. TrustMarks slice not validated (pkg/federation/entity_config.go) — added validate:"omitempty,dive" to Config.TrustMarks, matching the dive convention used elsewhere in the codebase (e.g. pkg/model/datastore.go), so nested TrustMarkConfig.ID/JWT required-field violations are now caught. Added validation tests for valid/missing-id/missing-jwt/empty cases.

  4. Stale test comment (pkg/federation/federation_test.go) — updated the comment on the config defaults sub-test; it previously implied no test used a real signer, which is no longer true now that TestBuildEntityConfigurationSignedJWT does.

go build ./... and go test ./... pass across the whole repo. gofmt -l is clean for all files touched by this PR (a few pre-existing formatting issues remain in unrelated files outside this PR's scope and were left untouched).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

internal/apigw/httpserver/endpoints_federation.go:19

  • This handler sets 404 via c.Status(...) but then returns (nil, nil). pkg/httphelpers/server.RegEndpoint will treat res==nil as success and unconditionally set the endpoint's defaultStatus (200), overwriting the 404. Use AbortWithStatus (writes headers immediately) to preserve 404 when federation is disabled.
	cfg := s.cfg.APIGW.Federation
	if cfg == nil || !cfg.Enabled {
		c.Status(http.StatusNotFound)
		return nil, nil
	}

internal/verifier/httpserver/endpoints_federation.go:20

  • This handler sets 404 via c.Status(...) but then returns (nil, nil). pkg/httphelpers/server.RegEndpoint will treat res==nil as success and unconditionally set the endpoint's defaultStatus (200), overwriting the 404. Use AbortWithStatus (writes headers immediately) to preserve 404 when federation is disabled.
	cfg := s.cfg.Verifier.Federation
	if cfg == nil || !cfg.Enabled {
		c.Status(http.StatusNotFound)
		return nil, nil
	}

pkg/federation/service.go:90

  • BuildEntityConfiguration assumes s.signer is non-nil and will panic on s.signer.GetJWK() if a nil signer was passed to NewService. Returning a clear error here avoids a hard crash and makes misuse easier to diagnose.
func (s *Service) BuildEntityConfiguration(metadata *EntityMetadata) (string, error) {
	// Build JWKS from the signing key
	jwk, err := s.signer.GetJWK()
	if err != nil {
		return "", fmt.Errorf("federation: get signing JWK: %w", err)
	}

pkg/federation/service.go:26

  • NewService dereferences cfg without a nil check (cfg.EntityID). Since NewService is exported, passing a nil cfg would panic at runtime. Consider defensively defaulting to an empty Config when cfg is nil to keep the constructor safe.

This issue also appears on line 85 of the same file.

func NewService(cfg *Config, signer *pki.SignerConfig, publicURL string) *Service {
	entityID := cfg.EntityID
	if entityID == "" {
		entityID = publicURL
	}

@masv3971

Copy link
Copy Markdown
Member

run make gen-config-docs

Comment thread internal/apigw/httpserver/endpoints_federation.go Outdated
Comment thread pkg/federation/service.go Outdated
- Move APIGW's federation entity-config handler logic out of the
  httpserver layer into apiv1 (new Client.FederationEntityConfig),
  matching the thin-handler pattern used by every other apigw
  endpoint (JWKS, SDJWTVCIssuerMetadata, etc.). Also switches the
  disabled-federation path to AbortWithStatus so the 404 isn't
  silently overwritten to 200 by RegEndpoint's default-status
  fallback.
- Rename pkg/federation -> pkg/openidfederation to avoid ambiguity
  with other kinds of federation the project may add later.
- Run `make gen-config-docs` to bring docs/CONFIGURATION.md back in
  sync with pkg/model/config.go (it was stale since 2026-06-12,
  predating this PR - the diff includes some already-existing,
  previously-undocumented fields like Kafka SASL/mTLS in addition to
  the new federation config).
@leifj

leifj commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both comments (commit ca5db01):

  1. "this is apiv1 logic, use the same structure as in other endpoints" — extracted the APIGW federation handler's logic into a new apiv1.Client.FederationEntityConfig method; the httpserver handler is now a thin wrapper (tracer span + delegate + write response), matching the JWKS/SDJWTVCIssuerMetadata pattern used everywhere else in apigw. While in there, also switched the disabled-federation path to AbortWithStatus instead of Status, since RegEndpoint overwrites a plain Status(404) with the route's default (200) when the handler returns nil, nil — same bug shape as an earlier Copilot comment on this file that got marked resolved without an actual code fix.

  2. Package naming — renamed pkg/federationpkg/openidfederation per your suggestion, to leave room for other federation types later without ambiguity.

Also ran make gen-config-docs per your comment — docs/CONFIGURATION.md was stale since 2026-06-12 (predates this PR), so the diff includes a few already-existing fields (Kafka SASL/mTLS) getting documented for the first time, in addition to the new federation config block.

Heads up, not fixed here: internal/verifier/httpserver/endpoints_federation.go has the identical disabled-federation 404-gets-overwritten-to-200 bug (still c.Status, not AbortWithStatus) — it wasn't part of your comment so I left it alone to stay scoped, but it's a live bug worth a follow-up.

go build ./... and go test ./... pass repo-wide.

Comment thread internal/verifier/httpserver/endpoints_federation.go Outdated
leifj added 2 commits July 29, 2026 13:52
Per masv3971's follow-up comment on PR SUNET#514 - the internal Go
identifiers (handler function names, file names, apiv1 method/type
names) still said "Federation" after the package itself was renamed
to openidfederation. Renamed:

- endpoints_federation.go -> endpoints_openidfederation.go (apigw + verifier)
- handlers_federation.go -> handlers_openidfederation.go (apigw apiv1)
- endpointFederationEntityConfig -> endpointOpenIDFederationEntityConfig
- apiv1.FederationEntityConfig -> apiv1.OpenIDFederationEntityConfig
- FederationEntityConfigReply -> OpenIDFederationEntityConfigReply

Left the yaml config key (federation:) and Cfg struct field name
(Federation) as-is - that's the operator-facing config schema, a
bigger change than what was asked for here.
@leifj

leifj commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Fixed (commit a1c624a) — renamed the internal Go identifiers to match the openidfederation package: endpoints_federation.goendpoints_openidfederation.go (apigw + verifier), handlers_federation.gohandlers_openidfederation.go, endpointFederationEntityConfigendpointOpenIDFederationEntityConfig, apiv1.FederationEntityConfig/FederationEntityConfigReplyOpenIDFederationEntityConfig/OpenIDFederationEntityConfigReply.

Left the yaml config key (federation:) and the Cfg struct field name (Federation) as-is — that's the operator-facing config schema, which felt like a bigger change than intended here; happy to rename that too if you'd like.

Also rebased onto current main (a lot has merged since — #481/#488/#519/#522/#493/#512 plus deps/security updates) and resolved the resulting conflict in pkg/model/config.go (both sides just added a new field to the same struct, no actual overlap). go build ./... and go test ./... pass repo-wide.

Comment thread pkg/openidfederation/service.go Outdated
Comment thread pkg/openidfederation/service.go Outdated
Comment thread internal/apigw/apiv1/handlers_openidfederation.go Outdated
Comment thread pkg/model/config.go Outdated
leifj added 2 commits July 29, 2026 19:14
- Rename openidfederation.NewService -> New (matches the New()
  convention used by every other package in this codebase).
- Move cloneMetadata to a Clone() method on *EntityMetadata.
- Accept a local Signer interface (GetJWK + SignJWT) instead of the
  concrete *pki.SignerConfig, and construct the signer/Service once in
  each service's New() instead of per-request (matches the underlying
  concern from an earlier Copilot comment on this same PR too - a
  request handler was re-loading key material on every single call).
  Full "reuse c.pkiSigner" wasn't possible as suggested: that field is
  typed as the narrower pki.Signer interface (Sign+Algorithm only),
  which doesn't expose GetJWK/SignJWT - so a separate signer is built
  once at service-construction time instead.
- Rename Cfg.Federation -> Cfg.OpenIDFederation (Go field only; kept
  the `federation:` yaml key since it wasn't specifically called out
  and changing it isn't free the way the Go-only renames are).

Applied to both apigw and verifier, which have the identical pattern.
@leifj

leifj commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all 4 (commit 6c013ed, rebased in 9c3c8f8):

  1. NewServiceNew — done, matches every other package's constructor convention in this codebase.
  2. Clone() method on EntityMetadata — done, moved cloneMetadata to (*EntityMetadata).Clone().
  3. Signer interface / avoid per-request signer construction — added a local Signer interface (GetJWK + SignJWT) to openidfederation, and construct the signer + Service once in each service's New() instead of on every request. Couldn't literally reuse c.pkiSigner as suggested — that field is typed as the narrower pki.Signer interface (Sign+Algorithm only), which doesn't expose GetJWK/SignJWT — so I built a separate signer once at construction time instead of on every request. This also resolves the earlier Copilot comment on this PR about re-loading key material per-request (was marked resolved before but the code never actually changed — confirmed and fixed now).
  4. FederationOpenIDFederation — renamed the Go field on Cfg. Kept the yaml key as federation: since that wasn't specifically what was flagged and renaming the wire format felt like a separate decision - happy to change that too if wanted.

go build ./... and go test ./... pass repo-wide.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

pkg/openidfederation/service.go:117

  • BuildEntityConfiguration always calls metadata.Clone(); since (*EntityMetadata).Clone() returns a non-nil empty struct on a nil receiver, this forces the JWT to include "metadata": {} even when the caller passed nil and no org/logo injection is needed, defeating the omitempty on the Metadata pointer.
	metadata = metadata.Clone()
	if s.config.OrganizationName != "" || s.config.LogoURI != "" {

docs/CONFIGURATION.md:720

  • The new federation section in CONFIGURATION.md only documents the YAML path but not the available fields (enabled/entity_id/authority_hints/ttl/trust_marks/etc). This is inconsistent with other sections which provide a field table, so users won't know how to configure the endpoint.
### `federation`

> **Path:** `.apigw.federation`, `.verifier.federation`

pkg/openidfederation/service.go:1

  • PR description says a new pkg/federation package was added, but the implementation is introduced under pkg/openidfederation. If the rename is intentional, the PR summary should be updated; if not, the package path should be aligned to avoid confusion for reviewers and future contributors.
package openidfederation

- pkg/openidfederation/service.go: BuildEntityConfiguration always
  called metadata.Clone(), and Clone() on a nil receiver returns a
  non-nil empty *EntityMetadata. This forced "metadata": {} into the
  signed JWT even when the caller passed nil and no org/logo injection
  was configured, defeating the Metadata field's omitempty. Now only
  clones when there's something to put in the result. Added
  TestBuildEntityConfiguration_OmitsMetadataWhenNilAndNoOrgInfo.
- developer_tools/scripts/gen_config_docs/main.go: the doc generator's
  hardcoded package directory list didn't include pkg/openidfederation,
  so the generated `federation` section in CONFIGURATION.md had no
  field table at all (just the YAML path) — inconsistent with every
  other config section. Added it to the list and regenerated.

(The third suppressed comment — PR description says pkg/federation but
the code lives in pkg/openidfederation — is a stale description vs.
code naming note, not a code issue; the package was intentionally named
openidfederation.)
@leifj

leifj commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Found and fixed 2 real issues from Copilot's "suppressed due to low confidence" comments (commit 4b07e6c):

  • `BuildEntityConfiguration` always forced `"metadata": {}` into the signed JWT: it unconditionally called `metadata.Clone()`, and `Clone()` on a nil receiver returns a non-nil empty `*EntityMetadata` — so passing `nil` with no org/logo configured still produced a non-nil metadata pointer, defeating `omitempty`. Now only clones when there's actually something to put in the result. Added a test (`TestBuildEntityConfiguration_OmitsMetadataWhenNilAndNoOrgInfo`) confirming `metadata` is omitted in that case.
  • `docs/CONFIGURATION.md`'s `federation` section had no field table: `developer_tools/scripts/gen_config_docs`'s hardcoded package list didn't include `pkg/openidfederation`, so the generated docs only showed the YAML path with none of the actual fields (enabled/entity_id/authority_hints/organization_name/logo_uri/trust_marks/ttl). Added the package to the list and regenerated — the section now has a full field table matching every other config section.

(The third suppressed comment — PR description says pkg/federation but code lives in pkg/openidfederation — is a stale description note, not a code issue; the package name is intentional.)

Build, vet, and make test all green.

@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (6)

internal/verifier/httpserver/endpoints_federation_test.go:40

  • This subtest should validate the standard "vp_formats_supported" key instead of "vp_formats" to match OpenID4VP client metadata naming used elsewhere in the repo.
	t.Run("vp_formats present when configured", func(t *testing.T) {
		formats := &openid4vp.VPFormatsSupported{
			SDJWT: &openid4vp.SDJWTVCFormat{},
		}
		m := buildOpenIDRelyingPartyMetadata("https://verifier.example.com", "Example Verifier", "ES256", formats)

		if m["vp_formats"] != formats {
			t.Errorf("expected vp_formats = %v, got %v", formats, m["vp_formats"])
		}

internal/verifier/httpserver/endpoints_federation_test.go:28

  • This assertion should check for "vp_formats_supported" rather than "vp_formats" to match the OpenID4VP field name used elsewhere in the repo.
		if _, ok := raw["vp_formats"]; ok {
			t.Errorf("expected serialized JSON to omit \"vp_formats\", got %s", data)
		}

internal/verifier/httpserver/endpoints_federation_test.go:51

  • This assertion should check for "vp_formats_supported" rather than "vp_formats" to match the OpenID4VP field name used elsewhere in the repo.
		if _, ok := raw["vp_formats"]; !ok {
			t.Errorf("expected serialized JSON to include \"vp_formats\", got %s", data)
		}

internal/verifier/httpserver/endpoints_openidfederation.go:63

  • The verifier OpenID Federation metadata uses the key "vp_formats", but the rest of the codebase (and OpenID4VP client metadata) uses "vp_formats_supported" (see pkg/oauth2/client_metadata.go and pkg/openid4vp/request_object.go). Using a different key risks wallets ignoring the advertised formats.
// buildOpenIDRelyingPartyMetadata builds the openid_relying_party metadata
// map advertised in the entity configuration. vpFormats is optional; the
// map is untyped (map[string]any), so a struct json tag can't omit an
// unset field for us -- the "vp_formats" key is only added when vpFormats
// is non-nil, to avoid serializing "vp_formats": null.
func buildOpenIDRelyingPartyMetadata(clientID, clientName, signingAlg string, vpFormats *openid4vp.VPFormatsSupported) map[string]any {
	m := map[string]any{
		"client_id":                  clientID,
		"response_types":             []string{"vp_token"},
		"client_name":                clientName,
		"request_object_signing_alg": signingAlg,
	}
	if vpFormats != nil {
		m["vp_formats"] = vpFormats
	}

pkg/openidfederation/service.go:134

  • BuildEntityConfiguration only places the JWKS at the top-level "jwks" claim. Elsewhere in this repo, key extraction from OpenID Federation entity configuration (trust_metadata) expects JWKS nested under metadata.openid_relying_party/openid_provider -> jwks (see pkg/keyresolver/did_helpers.go and its tests). Without also embedding JWKS in the relevant metadata section, downstream key resolution can fail.
	if metadata != nil || s.config.OrganizationName != "" || s.config.LogoURI != "" {
		metadata = metadata.Clone()
		if s.config.OrganizationName != "" || s.config.LogoURI != "" {
			if metadata.FederationEntity == nil {
				metadata.FederationEntity = make(map[string]any)
			}
			if s.config.OrganizationName != "" {
				metadata.FederationEntity["organization_name"] = s.config.OrganizationName
			}
			if s.config.LogoURI != "" {
				metadata.FederationEntity["logo_uri"] = s.config.LogoURI
			}
		}
	}

internal/verifier/httpserver/endpoints_federation_test.go:16

  • The tests are validating a "vp_formats" key, but OpenID4VP client metadata in this repo uses "vp_formats_supported" (e.g., pkg/oauth2/client_metadata.go). Align the test expectations with the on-the-wire field name to avoid locking in a non-standard key.

This issue also appears in the following locations of the same file:

  • line 26
  • line 31
  • line 49
	t.Run("vp_formats omitted when nil", func(t *testing.T) {
		m := buildOpenIDRelyingPartyMetadata("https://verifier.example.com", "Example Verifier", "ES256", nil)

		if _, ok := m["vp_formats"]; ok {
			t.Errorf("expected \"vp_formats\" key to be absent, got %v", m["vp_formats"])

@masv3971
masv3971 merged commit 178d098 into SUNET:main Jul 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants