From daeae1f443324ed39a757d0cfb9f560568b7012c Mon Sep 17 00:00:00 2001 From: pat-s Date: Sat, 20 Dec 2025 23:45:47 +0100 Subject: [PATCH] feat(webfinger): support desktop and mobile specific OIDC client_id --- services/webfinger/pkg/command/server.go | 16 +++++- services/webfinger/pkg/config/config.go | 5 +- .../pkg/relations/openid_discovery.go | 56 +++++++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/services/webfinger/pkg/command/server.go b/services/webfinger/pkg/command/server.go index ee770ca4b5..61b08db0ef 100644 --- a/services/webfinger/pkg/command/server.go +++ b/services/webfinger/pkg/command/server.go @@ -124,7 +124,12 @@ func getRelationProviders(cfg *config.Config) (map[string]service.RelationProvid case relations.OpenIDConnectDesktopRel: // Handled below - can also be auto-enabled via DesktopIDP config if cfg.DesktopIDP != "" { - rels[relationURI] = relations.OpenIDDiscoveryDesktop(cfg.DesktopIDP) + rels[relationURI] = relations.OpenIDDiscoveryDesktop(cfg.DesktopIDP, cfg.DesktopClientID) + } + case relations.OpenIDConnectMobileRel: + // Handled below - can also be auto-enabled via MobileIDP config + if cfg.MobileIDP != "" { + rels[relationURI] = relations.OpenIDDiscoveryMobile(cfg.MobileIDP, cfg.MobileClientID) } case relations.OpenCloudInstanceRel: var err error @@ -143,7 +148,14 @@ func getRelationProviders(cfg *config.Config) (map[string]service.RelationProvid // See: https://github.com/opencloud-eu/desktop/issues/246 if cfg.DesktopIDP != "" { if _, exists := rels[relations.OpenIDConnectDesktopRel]; !exists { - rels[relations.OpenIDConnectDesktopRel] = relations.OpenIDDiscoveryDesktop(cfg.DesktopIDP) + rels[relations.OpenIDConnectDesktopRel] = relations.OpenIDDiscoveryDesktop(cfg.DesktopIDP, cfg.DesktopClientID) + } + } + + // Auto-enable mobile OIDC issuer when MobileIDP is configured + if cfg.MobileIDP != "" { + if _, exists := rels[relations.OpenIDConnectMobileRel]; !exists { + rels[relations.OpenIDConnectMobileRel] = relations.OpenIDDiscoveryMobile(cfg.MobileIDP, cfg.MobileClientID) } } diff --git a/services/webfinger/pkg/config/config.go b/services/webfinger/pkg/config/config.go index 2449b73442..c67bf0c679 100644 --- a/services/webfinger/pkg/config/config.go +++ b/services/webfinger/pkg/config/config.go @@ -20,7 +20,10 @@ type Config struct { Instances []Instance `yaml:"instances"` Relations []string `yaml:"relations" env:"WEBFINGER_RELATIONS" desc:"A list of relation URIs or registered relation types to add to webfinger responses. See the Environment Variable Types description for more details." introductionVersion:"1.0.0"` IDP string `yaml:"idp" env:"OC_URL;OC_OIDC_ISSUER;WEBFINGER_OIDC_ISSUER" desc:"The identity provider href for the openid-discovery relation." introductionVersion:"1.0.0"` - DesktopIDP string `yaml:"desktop_idp" env:"WEBFINGER_OIDC_ISSUER_DESKTOP" desc:"The identity provider href for desktop clients. When set, desktop clients will use this issuer instead of the default IDP. This allows configuring separate OIDC clients for web and desktop applications." introductionVersion:"%%NEXT%%"` + DesktopIDP string `yaml:"desktop_idp" env:"WEBFINGER_OIDC_ISSUER_DESKTOP" desc:"The identity provider href for desktop clients. When set, desktop clients will use this issuer instead of the default IDP. This allows configuring separate OIDC clients for web and desktop applications." introductionVersion:"%%NEXT%%"` + DesktopClientID string `yaml:"desktop_client_id" env:"WEBFINGER_OIDC_CLIENT_ID_DESKTOP" desc:"The OIDC client ID for desktop clients. When set along with WEBFINGER_OIDC_ISSUER_DESKTOP, this client ID will be provided to desktop clients via webfinger properties." introductionVersion:"%%NEXT%%"` + MobileIDP string `yaml:"mobile_idp" env:"WEBFINGER_OIDC_ISSUER_MOBILE" desc:"The identity provider href for mobile clients. When set, mobile clients will use this issuer instead of the default IDP. This allows configuring separate OIDC clients for web and mobile applications." introductionVersion:"%%NEXT%%"` + MobileClientID string `yaml:"mobile_client_id" env:"WEBFINGER_OIDC_CLIENT_ID_MOBILE" desc:"The OIDC client ID for mobile clients. When set along with WEBFINGER_OIDC_ISSUER_MOBILE, this client ID will be provided to mobile clients via webfinger properties." introductionVersion:"%%NEXT%%"` OpenCloudURL string `yaml:"opencloud_url" env:"OC_URL;WEBFINGER_OPENCLOUD_SERVER_INSTANCE_URL" desc:"The URL for the legacy OpenCloud server instance relation (not to be confused with the product OpenCloud Server). It defaults to the OC_URL but can be overridden to support some reverse proxy corner cases. To shard the deployment, multiple instances can be configured in the configuration file." introductionVersion:"1.0.0"` Insecure bool `yaml:"insecure" env:"OC_INSECURE;WEBFINGER_INSECURE" desc:"Allow insecure connections to the WEBFINGER service." introductionVersion:"1.0.0"` diff --git a/services/webfinger/pkg/relations/openid_discovery.go b/services/webfinger/pkg/relations/openid_discovery.go index 69eab052a9..ea63dae336 100644 --- a/services/webfinger/pkg/relations/openid_discovery.go +++ b/services/webfinger/pkg/relations/openid_discovery.go @@ -10,6 +10,7 @@ import ( const ( OpenIDConnectRel = "http://openid.net/specs/connect/1.0/issuer" OpenIDConnectDesktopRel = "http://openid.net/specs/connect/1.0/issuer/desktop" + OpenIDConnectMobileRel = "http://openid.net/specs/connect/1.0/issuer/mobile" ) type openIDDiscovery struct { @@ -33,17 +34,23 @@ func (l *openIDDiscovery) Add(_ context.Context, jrd *webfinger.JSONResourceDesc }) } +// ClientIDProperty is the property URI for the OIDC client ID +const ClientIDProperty = "http://openid.net/specs/connect/1.0/client_id" + type openIDDiscoveryDesktop struct { - Href string + Href string + ClientID string } // OpenIDDiscoveryDesktop adds the OpenID Connect issuer relation for desktop clients. // This allows identity providers that require separate OIDC clients per application type // (like Authentik, Kanidm, Zitadel) to provide a distinct issuer URL for desktop clients. +// If clientID is provided, it will be included as a property in the link. // See: https://github.com/opencloud-eu/desktop/issues/246 -func OpenIDDiscoveryDesktop(href string) service.RelationProvider { +func OpenIDDiscoveryDesktop(href string, clientID string) service.RelationProvider { return &openIDDiscoveryDesktop{ - Href: href, + Href: href, + ClientID: clientID, } } @@ -51,8 +58,47 @@ func (l *openIDDiscoveryDesktop) Add(_ context.Context, jrd *webfinger.JSONResou if jrd == nil { jrd = &webfinger.JSONResourceDescriptor{} } - jrd.Links = append(jrd.Links, webfinger.Link{ + link := webfinger.Link{ Rel: OpenIDConnectDesktopRel, Href: l.Href, - }) + } + if l.ClientID != "" { + link.Properties = map[string]string{ + ClientIDProperty: l.ClientID, + } + } + jrd.Links = append(jrd.Links, link) +} + +type openIDDiscoveryMobile struct { + Href string + ClientID string +} + +// OpenIDDiscoveryMobile adds the OpenID Connect issuer relation for mobile clients. +// This allows identity providers that require separate OIDC clients per application type +// (like Authentik, Kanidm, Zitadel) to provide a distinct issuer URL for mobile clients. +// If clientID is provided, it will be included as a property in the link. +// See: https://github.com/opencloud-eu/desktop/issues/246 +func OpenIDDiscoveryMobile(href string, clientID string) service.RelationProvider { + return &openIDDiscoveryMobile{ + Href: href, + ClientID: clientID, + } +} + +func (l *openIDDiscoveryMobile) Add(_ context.Context, jrd *webfinger.JSONResourceDescriptor) { + if jrd == nil { + jrd = &webfinger.JSONResourceDescriptor{} + } + link := webfinger.Link{ + Rel: OpenIDConnectMobileRel, + Href: l.Href, + } + if l.ClientID != "" { + link.Properties = map[string]string{ + ClientIDProperty: l.ClientID, + } + } + jrd.Links = append(jrd.Links, link) }