diff --git a/client/config.go b/client/config.go index 102bbf52..505993d6 100644 --- a/client/config.go +++ b/client/config.go @@ -259,11 +259,12 @@ func (m *MTLSEndpoints) ApplyForSenderConstrain(endpoints *Endpoints) bool { // ApplyForClientAuth is ApplyForSenderConstrain's own counterpart for // RFC 8705 ยง2 client authentication // (Config.ClientAuthMethod == ClientAuthMethodSelfSignedTLSClientAuth -// or ClientAuthMethodTLSClientAuth): overrides endpoints' Token and -// PushedAuthorizationRequest fields, since a certificate-authenticated -// client must present its certificate at PAR too, not just at the -// token endpoint โ€” the same asymmetry a server's own client -// authentication enforces. m may be nil, in which case +// or ClientAuthMethodTLSClientAuth): overrides endpoints' Token, +// PushedAuthorizationRequest and Revocation fields, since a +// certificate-authenticated client must present its certificate at PAR +// and token revocation (RevokeToken) too, not just at the token +// endpoint โ€” the same asymmetry a server's own client authentication +// enforces. m may be nil, in which case // ApplyForClientAuth leaves endpoints untouched and reports false, for // the same reason ApplyForSenderConstrain does. func (m *MTLSEndpoints) ApplyForClientAuth(endpoints *Endpoints) bool { @@ -276,6 +277,9 @@ func (m *MTLSEndpoints) ApplyForClientAuth(endpoints *Endpoints) bool { if !m.PushedAuthorizationRequest.IsZero() { endpoints.PushedAuthorizationRequest = m.PushedAuthorizationRequest } + if !m.Revocation.IsZero() { + endpoints.Revocation = m.Revocation + } return true } diff --git a/client/mtls_endpoints_test.go b/client/mtls_endpoints_test.go index f677cd8f..5fd2c806 100644 --- a/client/mtls_endpoints_test.go +++ b/client/mtls_endpoints_test.go @@ -50,19 +50,21 @@ func TestMTLSEndpointsApplyForSenderConstrainOverridesTokenAndBackchannel(t *tes } // TestMTLSEndpointsApplyForClientAuthOverridesTokenAndPAR covers -// ApplyForClientAuth's own field selection: Token and -// PushedAuthorizationRequest move to their mTLS alias; +// ApplyForClientAuth's own field selection: Token, +// PushedAuthorizationRequest and Revocation move to their mTLS alias; // BackchannelAuthentication is untouched (only ApplyForSenderConstrain // ever moves it). func TestMTLSEndpointsApplyForClientAuthOverridesTokenAndPAR(t *testing.T) { aliases := &client.MTLSEndpoints{ Token: mustEndpointURL(t, "https://mtls.example.com/token"), PushedAuthorizationRequest: mustEndpointURL(t, "https://mtls.example.com/par"), + Revocation: mustEndpointURL(t, "https://mtls.example.com/revoke"), } endpoints := client.Endpoints{ Token: mustEndpointURL(t, "https://as.example.com/token"), PushedAuthorizationRequest: mustEndpointURL(t, "https://as.example.com/par"), BackchannelAuthentication: mustEndpointURL(t, "https://as.example.com/backchannel"), + Revocation: mustEndpointURL(t, "https://as.example.com/revoke"), } if !aliases.ApplyForClientAuth(&endpoints) { @@ -74,6 +76,9 @@ func TestMTLSEndpointsApplyForClientAuthOverridesTokenAndPAR(t *testing.T) { if got, want := endpoints.PushedAuthorizationRequest.String(), aliases.PushedAuthorizationRequest.String(); got != want { t.Errorf("PushedAuthorizationRequest = %s, want alias %s", got, want) } + if got, want := endpoints.Revocation.String(), aliases.Revocation.String(); got != want { + t.Errorf("Revocation = %s, want alias %s", got, want) + } if got, want := endpoints.BackchannelAuthentication.String(), "https://as.example.com/backchannel"; got != want { t.Errorf("BackchannelAuthentication = %s, want untouched %s", got, want) } diff --git a/federation/resolver.go b/federation/resolver.go index 7da7a3c3..d7b356f8 100644 --- a/federation/resolver.go +++ b/federation/resolver.go @@ -300,10 +300,12 @@ func newChainWalkState(subjectID string, leafStmt intfed.Statement, leafToken st // Resolve resolves subjectID's Trust Chain against one of // Config.TrustAnchors and returns its Resolved Metadata, enforcing // every Subordinate Statement's own max_path_length, naming_constraints -// and allowed_entity_types constraints along the way. See doc.go for -// this release's scope (leaf-entity resolution only, automatic -// registration's own trust model โ€” no server-side federation endpoints -// of this Resolver's own, no trust marks). +// and allowed_entity_types constraints along the way. It resolves a +// leaf entity for automatic registration's trust model. The Trust Marks +// the entity declares come back unverified in ResolvedEntity.TrustMarks, +// for VerifyTrustMark; issuing statements and serving federation +// endpoints is SelfIssuer's and SubordinateIssuer's job, not a +// Resolver's. See doc.go for the package's scope. func (r *Resolver) Resolve(ctx context.Context, subjectID string) (ResolvedEntity, error) { if subjectID == "" { return ResolvedEntity{}, fmt.Errorf("federation: subject entity ID is empty")