From 3edee485dd37817a66c1c7e329a35b750f0e6b5a Mon Sep 17 00:00:00 2001
From: micolous <micolous+gh@gmail.com>
Date: Tue, 25 Feb 2025 12:53:53 +1000
Subject: [PATCH] address webfinger doc feedbacks (#3446)

---
 book/src/integrations/oauth2.md | 101 ++++++++++++++++++++------------
 1 file changed, 63 insertions(+), 38 deletions(-)

diff --git a/book/src/integrations/oauth2.md b/book/src/integrations/oauth2.md
index 9a9bbad6c..5c395293f 100644
--- a/book/src/integrations/oauth2.md
+++ b/book/src/integrations/oauth2.md
@@ -85,7 +85,7 @@ URL **(recommended)**
 
 <dt>
 
-[WebFinger URL **(discouraged)**](#webfinger)
+[WebFinger URL](#webfinger) **(discouraged)**
 
 </dt>
 
@@ -162,7 +162,7 @@ Token endpoint
 
 <dt>
 
-OpenID Connect issuer URI
+OpenID Connect Issuer URL
 
 </dt>
 
@@ -458,58 +458,83 @@ Each client has unique signing keys and access secrets, so this is limited to ea
 
 ## WebFinger
 
-[WebFinger](https://datatracker.ietf.org/doc/html/rfc7033) provides a mechanism
-for discovering information about people or other entities. It can be used by an
-identity provider to supply OpenID Connect discovery information.
+[WebFinger][webfinger] provides a mechanism for discovering information about
+entities at a well-known URL (`https://{hostname}/.well-known/webfinger`).
 
-Kanidm provides
-[an Identity Provider Discovery for OIDC URL](https://datatracker.ietf.org/doc/html/rfc7033#section-3.1)
-response to all incoming WebFinger requests, using a user's SPN as their account
-ID. This does not match on email addresses as they are not guaranteed to be
-unique.
+It can be used by a WebFinger client to
+[discover the OIDC Issuer URL][webfinger-oidc] of an identity provider from the
+hostname alone, and seems to be intended to support dynamic client registration
+flows for large public identity providers.
 
-However, WebFinger has a number of flaws which make it difficult to use with
-Kanidm:
+Kanidm v1.5.1 and later can respond to WebFinger requests, using a user's SPN as
+part of [an `acct` URI][rfc7565] (eg: `acct:user@idm.example.com`). While SPNs
+and `acct` URIs look like email addresses, [as per RFC 7565][rfc7565s4], there
+is no guarantee that it is valid for any particular application protocol, unless
+an administrator explicitly provides for it.
 
-* WebFinger assumes that the identity provider will give the same `iss`
-  (Issuer) for every OAuth 2.0/OIDC client, and there is no standard way for a
-  WebFinger client to report its client ID.
+When setting up an application to authenticate with Kanidm, WebFinger **does not
+add any security** over configuring an OIDC Discovery URL directly. In an OIDC
+context, the specification makes a number of flawed assumptions which make it
+difficult to use with Kanidm:
 
-  Kanidm uses a *different* `iss` (Issuer) value for each client.
+* WebFinger assumes that an identity provider will use the same Issuer URL and
+  OIDC Discovery document (which contains endpoint URLs and token signing keys)
+  for *all* OAuth 2.0/OIDC clients.
 
-* WebFinger requires that this be served at the *root* of the domain of a user's
+  Kanidm uses *client-specific* Issuer URLs, endpoint URLs and token signing
+  keys. This ensures that tokens can only be used with their intended service.
+
+* WebFinger endpoints must be served at the *root* of the domain of a user's
   SPN (ie: information about the user with SPN `user@idm.example.com` is at
-  `https://idm.example.com/.well-known/webfinger`).
+  `https://idm.example.com/.well-known/webfinger?resource=acct%3Auser%40idm.example.com`).
 
-  Kanidm *does not* provide a WebFinger endpoint at its root URL, because it has
-  no way to know *which* OAuth 2.0/OIDC client a WebFinger request is associated
-  with, so could report an incorrect `iss` (Issuer).
+  Unlike OIDC Discovery, WebFinger clients do not report their OAuth 2.0/OIDC
+  client ID in the request, so there is no way to tell them apart.
 
-  You will need a load balancer in front of Kanidm's HTTPS server to redirect
-  requests to the appropriate `/oauth2/openid/:client_id:/.well-known/webfinger`
-  URL. If the client does not follow redirects, you may need to rewrite the
-  request in the load balancer instead.
+  As a result, Kanidm *does not* provide a WebFinger endpoint at its root URL,
+  because it could report an incorrect Issuer URL and lead the client to an
+  incorrect OIDC Discovery document.
+
+  You will need a load balancer in front of Kanidm's HTTPS server to send a HTTP
+  307 redirect to the appropriate
+  `/oauth2/openid/:client_id:/.well-known/webfinger` URL, *while preserving all
+  query parameters*. For example, with Caddy:
+
+  ```caddy
+  # Match on a prefix, and use {uri} to preserve all query parameters.
+  # This only supports *one* client.
+  example.com {
+    redir /.well-known/webfinger https://idm.example.com/oauth2/openid/:client_id:{uri} 307
+  }
+  ```
 
   If you have *multiple* WebFinger clients, it will need to map some other
   property of the request (such as a source IP address or `User-Agent` header)
   to a client ID, and redirect to the appropriate WebFinger URL for that client.
 
 * Kanidm responds to *all* WebFinger queries with
-  [an Identity Provider Discovery for OIDC URL](https://datatracker.ietf.org/doc/html/rfc7033#section-3.1),
-  **regardless** of what
-  [`rel` parameter](https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.4.1)
-  was specified.
-
-  This is to work around
-  [a broken client](https://tailscale.com/kb/1240/sso-custom-oidc) which doesn't
-  send a `rel` parameter, but expects an Identity Provider Discovery issuer URL
-  in response.
+  [an Identity Provider Discovery for OIDC URL][webfinger-oidc], **ignoring**
+  [`rel` parameter(s)][webfinger-rel].
 
   If you want to use WebFinger in any *other* context on Kanidm's hostname,
   you'll need a load balancer in front of Kanidm which matches on some property
   of the request.
 
-Because of the flaws of the WebFinger specification and the deployment
-difficulties they introduce, we recommend that applications use OpenID Connect
-Discovery or OAuth 2.0 Authorisation Server Metadata for client configuration
-instead of WebFinger.
+  WebFinger clients *may* omit the `rel=` parameter, so if you host another
+  service with relations for a Kanidm [`acct:` entity][rfc7565s4] and a client
+  *does not* supply the `rel=` parameter, your load balancer will need to merge
+  JSON responses from Kanidm and the other service(s).
+
+Because of these issues, we recommend that applications support *directly*
+configuring OIDC using a Discovery URL or OAuth 2.0 Authorisation Server
+Metadata URL instead of WebFinger.
+
+If a WebFinger client only checks WebFinger once during setup, you may wish to
+temporarily serve an appropriate static WebFinger document for that client
+instead.
+
+[rfc7565]: https://datatracker.ietf.org/doc/html/rfc7565
+[rfc7565s4]: https://datatracker.ietf.org/doc/html/rfc7565#section-4
+[webfinger]: https://datatracker.ietf.org/doc/html/rfc7033
+[webfinger-oidc]: https://datatracker.ietf.org/doc/html/rfc7033#section-3.1
+[webfinger-rel]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.3