eleven / messenger Public

home: serve the Apple app-site-association for native passkeys (#357)

The native Eleven apps bind passkeys to home.elevenmessenger.com via the
`webcredentials` associated domain, which requires this RP to publish an
apple-app-site-association naming the apps' team+bundle ids. Content-blind —
nothing user-identifying — matching the home server's posture.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Paul Campbell e263054f57501fc0bd05382923230ae98d151e1f parent a768599
1 file changed, +17 −0
  • home/main.go +17 −0
diff --git a/home/main.go b/home/main.go
index b1cf89f..cafb43f 100644
--- a/home/main.go
+++ b/home/main.go
@@ -160,6 +160,11 @@ func (s *Server) handler() http.Handler {
mux.HandleFunc("GET /restore", s.serveRestore)
mux.Handle("GET /static/", noCache(http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))))
+ // Apple app-site-association: lets the native Eleven apps run passkey
+ // ceremonies against this RP (the webcredentials service). Content-blind —
+ // it names only the apps' team+bundle ids, nothing user-specific.
+ mux.HandleFunc("GET /.well-known/apple-app-site-association", s.serveAASA)
+
// WebAuthn ceremonies. begin endpoints are rate-limited: they're the open,
// anonymous surface (finish requires a signed response, which is its own gate).
mux.HandleFunc("POST /api/register/begin", s.rateLimited(s.registerBegin))
@@ -220,6 +225,18 @@ func (s *Server) servePage(w http.ResponseWriter, r *http.Request) {
w.Write(s.page)
}
+// serveAASA publishes the Apple app-site-association so the native Eleven apps
+// can bind passkeys to this relying party (the `webcredentials` service). Only
+// team+bundle ids appear — nothing user-identifying, consistent with this
+// server's content-blind posture. Served at the exact well-known path Apple
+// fetches, as application/json with no auth.
+func (s *Server) serveAASA(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"webcredentials":{"apps":[` +
+ `"J5NDF5PCD8.com.elevenmessenger.messenger",` +
+ `"J5NDF5PCD8.com.elevenmessenger.messenger.mac"]}}`))
+}
+
// serveEnroll serves the enrolment popup. Same CSP as the main page — no inline
// scripts, no cross-origin fetches; the only cross-origin move is a postMessage
// back to the opener, which CSP doesn't govern.