feat(mobile-sso): serve assetlinks.json + App Links redirect allowlist
Add the server side of Android App Links (M9 follow-up, docs/android/APP_LINKS.md): - GET /.well-known/assetlinks.json at the web root, gated by the new admin setting `mobile_app_links_enabled` (default off -> 404; on-but-no-fingerprint -> 404). Emits the Digital Asset Links statement for the fixed published package (MOBILE_APP_PACKAGE) + MOBILE_APP_CERT_SHA256 fingerprint(s). - mobileSso `/start` additionally accepts this shard's own self-origin https://<host>/mobile/callback when App Links are enabled — one additive exact-match entry, derived from APP_BASE_URL/request origin, never client input; the custom-scheme allowlist is never narrowed. The settings lookup is short-circuited for non-https redirects so custom-scheme rejections stay fast. - settings.isMobileAppLinksEnabled() (fail-closed) + getPublic().mobileAppLinks; admin updateSettings validates the boolean; seed default off. Tests: test/appLinks.test.js (route gating + allowlist). Full suite 284 pass. Swagger unchanged (web-root verification file is #swagger.ignore'd). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
This commit is contained in:
@@ -54,6 +54,22 @@ async function isGameAccountSignupEnabled() {
|
||||
return GAME_SIGNUP_OFFER.includes(await getGameSignupMode())
|
||||
}
|
||||
|
||||
// Android App Links opt-in (M9 follow-up). When on, the shard auto-serves
|
||||
// /.well-known/assetlinks.json and the mobile SSO bridge additionally accepts the
|
||||
// self-origin https://<host>/mobile/callback redirect. Stored as the string
|
||||
// 'true'/'false'; default off. See docs/android/APP_LINKS.md.
|
||||
const MOBILE_APP_LINKS_KEY = 'mobile_app_links_enabled'
|
||||
|
||||
// Fail-closed: any read error (e.g. DB unavailable) reports "disabled" so a
|
||||
// transient fault can never open the https redirect path or serve assetlinks.json.
|
||||
async function isMobileAppLinksEnabled() {
|
||||
try {
|
||||
return String(await settingsDb.get(MOBILE_APP_LINKS_KEY)) === 'true'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function get(key) {
|
||||
return settingsDb.get(key)
|
||||
}
|
||||
@@ -113,6 +129,11 @@ async function getPublic() {
|
||||
// that we use the first NTFY_ALLOWED_ORIGINS entry (a device endpoint must sit
|
||||
// on an allowed origin anyway). Never NTFY_BASE_URL — it may be internal-only.
|
||||
out.push = { ntfyUrl: publicNtfyUrl() }
|
||||
// Whether this shard has opted into Android App Links (M9 follow-up). Lets a
|
||||
// native client tell whether it may request the https App Link redirect_uri
|
||||
// before doing so (the server would otherwise reject an unallowlisted one). The
|
||||
// custom-scheme callback works regardless of this flag.
|
||||
out.mobileAppLinks = String(all[MOBILE_APP_LINKS_KEY]) === 'true'
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -142,4 +163,6 @@ module.exports = {
|
||||
GAME_SIGNUP_MODES,
|
||||
getGameSignupMode,
|
||||
isGameAccountSignupEnabled,
|
||||
MOBILE_APP_LINKS_KEY,
|
||||
isMobileAppLinksEnabled,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user