feat(base): SimpleLogin's forwarding page and Learn's App Links statement (ADR 0020, step 1)

SimpleLogin takes only https redirect addresses, each registered exactly,
so the instance's SimpleLogin app registers one page on the register's
web server, /simplelogin-callback, which sends the browser on to the app
that began the sign-in: a 303 to the desktop's loopback (127.0.0.1, a port
per app) or the phone's scheme, chosen by the marker the state begins with
(`<app>-<platform>.<rest>`, the rest 1 to 512 characters of base64url),
from a fixed table, so it sends nobody anywhere else. It forwards the code
or SimpleLogin's error (`deny`) with the state and nothing else, refuses
anything malformed, repeated or not a GET with a page that repeats nothing
it was given, and keeps and logs nothing (no-store, no referrer). It is
built ahead of the sign-in itself so development can sign in through
learn.halkyon.eu's page, which the development app registers too.

Android's assetlinks.json for Halkyon Learn signed with the release key
is answered by the Caddyfile on learn.halkyon.eu alone: the one domain the
project's builds name, and the server knows no app's key. A root test
holds its fingerprint to tool/release/android-certificate.sha256. The
app's intent filter comes with the sign-in; iOS's association waits for
an Apple team ID.

The profile page's language choice and look move to common/web_page.dart,
shared by both pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-23 15:27:56 +02:00
commit 8d6785f5c6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1,5 +1,6 @@
# Caddy forwards to the API server, and profile pages (ADR 0011) and the dictionaries
# devices download (ADR 0012) to the web server (8082), which serves nothing else. Never the calls between the instance's
# Caddy forwards to the API server, and profile pages (ADR 0011), the dictionaries
# devices download (ADR 0012) and SimpleLogin's forwarding page (ADR 0020) to the web
# server (8082), which serves nothing else. Never the calls between the instance's
# services (`/halkyon-base/…`, ADR 0016): they reach each other on its own network. Insights (8081) doesn't run in production;
# if it is ever re-enabled, reach it through an SSH tunnel, never through this file.
#
@ -24,6 +25,28 @@
respond 404
}
# SimpleLogin's forwarding page (ADR 0020): where SimpleLogin sends a sign-in back,
# which sends it on to the app that began it, on the desktop or a phone.
@simplelogin path /simplelogin-callback
handle @simplelogin {
reverse_proxy server:8082
}
# Android's statement that Halkyon Learn, signed with the project's key
# (tool/release/android-certificate.sha256), opens learn.halkyon.eu's links
# itself (App Links, ADR 0020's D9), so SimpleLogin's way back reaches the app
# without the forwarding page. On learn.halkyon.eu alone, the one instance the
# project's builds name: another operator's domain is no link of the project's
# app. Written here rather than by the server, which knows no app's key.
@assetlinks {
host learn.halkyon.eu
path /.well-known/assetlinks.json
}
handle @assetlinks {
header Content-Type application/json
respond `[{"relation":["delegate_permission/common.handle_all_urls"],"target":{"namespace":"android_app","package_name":"app.halkyon.learn","sha256_cert_fingerprints":["27:E2:43:FC:BD:C8:26:FF:98:AB:7D:BB:10:43:6C:98:C3:BE:A2:4B:2A:01:5E:94:E4:CF:C7:D1:FC:2A:00:6C"]}}]` 200
}
# The dictionaries devices download to look Japanese up by themselves, named by
# their hashes; already compressed.
handle /device-dictionaries/* {