@siax/mobile-attest (0.1.1)
Installation
@siax:registry=https://git.cloud.siax.io/api/packages/sax3l/npm/npm install @siax/mobile-attest@0.1.1"@siax/mobile-attest": "0.1.1"About this package
@siax/mobile-attest
Klientsidan av enhetsattestering: App Attest (iOS) och Play Integrity (Android). API-lagret ska avvisa känsliga anrop som saknar giltigt bevis (MSEC-05) — och en legitim men äldre enhet ska degradera begripligt i stället för att låsas ute (MSEC-06).
Noll beroenden. Paketet använder bara plattformens fetch och WebCrypto,
och allt utanför paketets ansvar injiceras.
Kärnbussgränsen (MOB-02)
Detta paket är en yta, inte en tjänst:
| Ansvar | Ägs av | Här |
|---|---|---|
| Utfärda nonce | services/device-registry (server) |
NonceSource — ett gränssnitt |
| Verifiera bevis mot Apple/Google | services/device-registry (server) |
— |
| Enhetsregister, spärrlista | server + AUD0 | — |
| Native App Attest / Play Integrity | apprepots Expo-modul | AppAttestAdapter / PlayIntegrityAdapter |
| Säker lagring av nyckel-id | @siax/mobile-auth / Keychain |
KeyIdStore — ett gränssnitt |
| Hämta utmaning, bygga bevis, bifoga det, degradera begripligt | detta paket | ✅ |
Vad som FAKTISKT är byggt
createAttestationClient()— hela klientflödet: stödkontroll → nyckel → nonce → hash → plattformsbevis → cache. Kastar aldrig; varje väg slutar i ettAttestationOutcome.- Utfallsmodellen
attested | unsupported-device | failed | unavailable, där varje icke-lyckat utfall bär enUserExplanationmed stabil kod, text och en handling (retry,update-os,continue-limited, …). DEFAULT_EXPLANATIONS— svensk text för alla 16 förklaringskoder, med partiell överskrivning viabuildExplanationCatalog().- Nonce-livscykel (
createNonceManager): cachning, skevmarginal, engångsbruk och vägran att bygga bevis på ett nonce-id servern återutfärdat. createHttpNonceSource()— HTTP-klient mot enhetsregistret som översätter 429/503/408/nätfel tilloffline | rate-limited | service-unavailable | timeout.attachAttestationHeaders()— bifogar beviset, eller vid degradering endast status + kod. Ett utgånget bevis bifogas aldrig (status blirexpired).evaluateAccess()— klientsidigt beslut per operation, med prefixmönster (payment.*). Icke-känsliga operationer släpps igenom degraderat; att blockera hela appen på attestering är uttryckligen förbjudet (M9).createWebCryptoSha256()+ egen base64url-kodare (ingenBuffer, ingenbtoa).- Telemetri via
onOutcomesom bara innehåller koder — aldrig token eller nonce.
Beviset binds till
siax-attest-v1|<nonce>|<purpose>|<keyId> → SHA-256 → base64url. Servern måste
räkna om exakt samma sträng. Fältordningen är fast och tomma fält utelämnas inte,
så att gränserna inte går att förfalska.
Huvuden som skickas
x-siax-attestation, -platform, -format, -nonce, -key-id, -hash,
-status, -reason. Namnen finns som konstanter i ATTESTATION_HEADERS så att
klient och server delar exakt samma strängar.
Vad som INTE är byggt (medvetet)
- Ingen native kod.
AppAttestAdapterochPlayIntegrityAdapterär gränssnitt. Expo-modulerna som anroparDCAppAttestServicerespektiveStandardIntegrityManagerskrivs i apprepot/app-generatorn (M3). - Ingen serversidig verifiering.
services/device-registryfinns inte i detta repo ännu — M9:s serverdel är öppen. Klienten är komplett mot kontraktet. - Ingen assertion-flöde (iOS
generateAssertion) för löpande anrop efter första attesteringen. Endast attestation-vägen är byggd; assertion är nästa steg när enhetsregistret finns. - Ingen spärrlista/enhetsåterkallelse på klienten — det hör hemma i servern.
- Ingen AUD0-koppling. Utfall exponeras via
onOutcome; att skicka dem till AUD0 görs av appen eller@siax/mobile-api, inte här (MOB-02).
Användning
import {
createAttestationClient,
createHttpNonceSource,
} from '@siax/mobile-attest';
const attestation = createAttestationClient({
platform: 'ios',
// Endast ett env-NAMN — aldrig ett värde i kod (SEC-01).
nonceSource: createHttpNonceSource({ endpoint: process.env.DEVICE_REGISTRY_NONCE_URL! }),
appAttest: nativeAppAttestModule, // Expo-modul från apprepot
keyIdStore: keychainKeyIdStore, // @siax/mobile-auth
policy: { sensitiveOperations: ['payment.*', 'profile.delete'] },
});
const outcome = await attestation.attest({ purpose: 'payment.authorize' });
const decision = attestation.evaluateAccess('payment.authorize', outcome);
if (!decision.allowed) {
showExplanation(decision.explanation); // titel + text + knapp, aldrig ett tyst 403
} else {
await fetch(url, { headers: attestation.attachHeaders(baseHeaders, outcome) });
}
Test
cd packages/mobile-attest && node --test
38 tester, noll beroenden, node:test + node:assert. Testfilerna importerar
src/*.ts direkt, vilket kräver Node med typavskalning påslagen (Node 22.18+
eller 24; på 22.6–22.17 med --experimental-strip-types). Verifierat på Node
v24.14.0: 38/38 gröna.
Typkontroll av den publicerade koden:
cd packages/mobile-attest && tsc --noEmit -p tsconfig.json
strict: true + erasableSyntaxOnly + verbatimModuleSyntax, rent.
tsconfig.json täcker src/ — testfilerna kan typkontrolleras först när
@types/node finns installerat i repot (inga beroenden installeras i denna omgång).