diff --git a/scripts/authentik-audiobookshelf-sso.sh b/scripts/authentik-audiobookshelf-sso.sh
index 2f3df52..d7b73de 100755
--- a/scripts/authentik-audiobookshelf-sso.sh
+++ b/scripts/authentik-audiobookshelf-sso.sh
@@ -11,15 +11,17 @@
# dist-server/server/auth/OidcAuthStrategy.js + objects/settings/ServerSettings.js)
# rather than a forum, because each of these is a dead-login trap:
#
-# * The browser callback URI /auth/openid/callback for EVERY origin ABS is
-# reached at, plus /auth/openid/mobile-redirect on ABS_BASE only. ABS SWAPS
-# the app's own audiobookshelf://oauth for the mobile-redirect URL before the IdP
-# ever sees a redirect_uri, so registering the app scheme with authentik — the
-# obvious move — is what makes phone logins fail. The app scheme belongs in ABS's
-# authOpenIDMobileRedirectURIs list (its built-in default), not here. The mobile
-# URI is registered on the LAN origin only because the phone is configured with
-# the LAN address: it is a non-browser client and cannot solve the guard
-# challenge that sits in front of the vhost.
+# * Two URIs per origin ABS is reached at — /auth/openid/callback and
+# /auth/openid/mobile-redirect — for EVERY entry in BASES (the LAN address
+# and the public vhost). ABS SWAPS the app's own audiobookshelf://oauth for the
+# mobile-redirect URL before the IdP ever sees a redirect_uri, so registering the
+# app scheme with authentik — the obvious move — is what makes phone logins fail.
+# The app scheme belongs in ABS's authOpenIDMobileRedirectURIs list (its built-in
+# default), not here. The mobile URI exists on every origin because the app sends
+# the one belonging to the door it was configured with: the vhost started serving
+# non-browser clients on 2026-11-03 (it left the guard chain), so a phone pointed
+# at the public name sends the public mobile-redirect URI, and STRICT matching
+# answers an unregistered one with "Redirect URI Error".
# * CARRIES THE BASE PATH. ABS builds redirect_uri as
# /auth/openid/callback, and that subfolder is
# the ROUTER_BASE_PATH the container is mounted at — /audiobookshelf, which is also the only
@@ -157,12 +159,12 @@ _ns = {}
exec(_src, _ns)
# --- provider --------------------------------------------------------------------
-# One browser callback per origin (LAN address and the guarded vhost), because ABS
-# derives redirect_uri from the request origin; the mobile URI stays on the LAN origin.
-uris = [RedirectURI(matching_mode=RedirectURIMatchingMode.STRICT,
- url="%s/auth/openid/callback" % b) for b in BASES]
-uris.append(RedirectURI(matching_mode=RedirectURIMatchingMode.STRICT,
- url="%s/auth/openid/mobile-redirect" % BASE))
+# One callback pair per origin (LAN address and the public vhost): ABS derives the
+# browser callback from the request origin and the mobile callback from the door the
+# app was configured with, so each origin needs both or that door cannot sign in.
+uris = [RedirectURI(matching_mode=RedirectURIMatchingMode.STRICT, url="%s%s" % (b, p))
+ for b in BASES
+ for p in ("/auth/openid/callback", "/auth/openid/mobile-redirect")]
authz_flow = Flow.objects.get(slug="default-provider-authorization-implicit-consent")
# Three things beyond the name, each one a silent dead login: the grant-type list (2026.8