@siax/mobile-auth (0.1.0)
Installation
@siax:registry=https://git.cloud.siax.io/api/packages/sax3l/npm/npm install @siax/mobile-auth@0.1.0"@siax/mobile-auth": "0.1.0"About this package
@siax/mobile-auth
Tunn ID0-klient: OIDC Authorization Code + PKCE, tokenlivscykel och säker lagring för K6-appar. Reimplementerar INTE identitet — ID0/Zitadel äger den (MOB-02, IAM-01).
Status: kärnan är implementerad och testad. Paketet är ännu inte publicerat till Giteas npm-registry, och inte kört mot en riktig ID0 — ingen ID0-container finns (M15). Kontraktet är byggt mot OIDC/OAuth-specarna, se "Vad som INTE är gjort" längst ned.
Vad paketet gör
| Kapabilitet | Var | Kontroll |
|---|---|---|
| PKCE S256 (kryptografiskt säker verifier + challenge) | src/pkce.ts |
RFC 7636 |
Auktoriserings-URL med state + nonce |
src/authorize.ts |
RFC 6749, OIDC Core |
Callback-tolkning: state-kontroll, iss-kontroll, felmappning |
src/authorize.ts |
RFC 9207 |
| OIDC-discovery med issuer-verifiering | src/discovery.ts |
RFC 8414 |
| Token-utbyte och refresh-token-rotation | src/session.ts |
MSEC-04, RFC 6749 §6 |
| Utgångskontroll med klockskev-marginal | src/tokens.ts |
— |
| Återkallning + "logga ut överallt" | src/session.ts |
RFC 7009 |
| ID-token: avkodning + anspråkskontroll | src/id-token.ts |
OIDC Core §3.1.3.7 |
Tokens i Keychain/Keystore via injicerbart SecureStore |
src/secure-store.ts |
MSEC-03 |
| Expo-adaptrar (secure-store, crypto, web-browser) | src/react-native.ts |
RFC 8252 |
Kärnan (allt utom react-native.ts) är fri från React Native och körs i
Node. Det är därför testerna kan bevisa beteendet utan en enhet.
Använda paketet
import * as SecureStore from 'expo-secure-store';
import * as Crypto from 'expo-crypto';
import * as WebBrowser from 'expo-web-browser';
import { AuthSession } from '@siax/mobile-auth';
import {
createExpoCryptoProvider,
createExpoSecureStore,
runBrowserLogin,
} from '@siax/mobile-auth/react-native';
const session = new AuthSession({
config: {
issuer: process.env.EXPO_PUBLIC_ID0_ISSUER!, // env-NAMN, aldrig ett värde i koden
clientId: process.env.EXPO_PUBLIC_ID0_CLIENT_ID!,
redirectUri: 'se.siax.demo:/oauthredirect',
postLogoutRedirectUri: 'se.siax.demo:/loggedout',
},
store: createExpoSecureStore(SecureStore, {
nativeOptions: { keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY },
}),
crypto: createExpoCryptoProvider(Crypto),
fetch: globalThis.fetch,
});
await runBrowserLogin(session, WebBrowser);
const accessToken = await session.getAccessToken(); // förnyar automatiskt
Koppla ihop med @siax/mobile-api:
const transport = createTransport({
baseUrl: process.env.EXPO_PUBLIC_API_BASE_URL!,
client: clientInfo,
getAuthorization: async () => `Bearer ${await session.getAccessToken()}`,
});
Designbeslut värda att känna till
- Ingen client secret. En publik klient kan inte hålla en hemlighet
(MSEC-01). PKCE ersätter den;
client_secretskickas aldrig. - Endast
S256.code_challenge_method=plainerbjuds inte — att erbjuda valet är att erbjuda en nedgraderingsattack. Saknas SHA-256 i runtimen kastar paketetcrypto_unavailablei stället för att falla tillbaka. - Single-flight refresh. Samtidiga anrop delar ETT refresh-anrop. Med roterande refresh-tokens skulle parallella anrop se ut som tokenstöld för servern och få hela grant-familjen återkallad.
- Asymmetrisk felhantering vid refresh.
invalid_grant⇒ sessionen raderas lokalt. Transport-/serverfel ⇒ lagringen lämnas byte-identisk, så ett tappat mobilnät inte loggar ut användaren. Det finns ett test som hävdar att inte ens en skrivning sker i det fallet. - En nyckel, ett värde. Hela uppsättningen skrivs som en enda post i den säkra lagringen — två skrivningar kan avbrytas mellan varandra och lämna en halv session.
- Ingen filbaserad lagring. Paketet levererar avsiktligt ingen
AsyncStorage-/filadapter (MSEC-03). Enda medföljande implementationerna är
InMemorySecureStoreochFailingSecureStore, båda för tester.
Tester
node --test "packages/mobile-auth/test/*.test.ts"
86 tester, alla gröna (körning 2026-08-09, Node 24.14). Inga testberoenden:
node:test + node:assert, inget npm install krävs.
Bland det som faktiskt hävdas: RFC 7636 Appendix B:s S256-vektor · att
code_verifier aldrig läcker ut i auktoriserings-URL:en · att extraParams
inte kan skriva över code_challenge_method · att fel state aldrig leder till
ett token-utbyte · att rotation ersätter respektive behåller refresh-token
korrekt · att ett misslyckat refresh inte lämnar halvt tillstånd · att tio
samtidiga getAccessToken() ger exakt ett token-anrop · att utloggning
återkallar hos servern och rapporterar ärligt när återkallningen inte kunde
bevisas.
Runtime-krav för att köra testerna: Node 22.18+ eller 24 — källkoden är
ren TypeScript som körs via Nodes inbyggda type stripping. erasableSyntaxOnly
i tsconfig.json är det som håller koden inom vad strippningen klarar.
Vad som INTE är gjort (AR-5)
- ID-tokenens signatur verifieras inte. Tillåtet enligt OIDC Core §3.1.3.7 punkt 6 eftersom token hämtas direkt från token-endpointen över TLS. Tar en app emot en ID-token på annan väg krävs JWKS-baserad validering — den finns inte här.
- "Logga ut överallt" är inte fullständig.
logoutEverywhere()återkallar refresh- och access-token (RFC 7009, vilket i Zitadel dödar hela grant-familjen) och returnerarend_session-URL:en, men avslutar inte sessioner på ANDRA enheter. Det kräver ID0:s sessions-API (Zitadel v2), som inte kan kontraktstestas innan ID0 är rest (M15). - Aldrig kört mot en riktig ID0. Alla tester går mot en injicerad transport. Ett integrationstest mot en levande issuer återstår och hör till M15.
- DPoP / sender-constrained tokens (RFC 9449) är inte implementerat. Bearer används. Attestering (MSEC-05, M9) är den planerade enhetsbindningen.
- Ingen
tsc-körning.typescriptär inte ett beroende i repot i denna omgång, såtsconfig.jsonär ett kontrakt som ännu inte upprätthålls av CI. - Inte publicerat till Giteas npm-registry och inte konsumerat av något apprepo ännu.
Kärnbussgränsen (MOB-02)
Detta paket är en yta, inte en tjänst. Om du står i begrepp att implementera
något som ID0, INF0, ST0RE, N0D, AUD0, N0TIFY, B00K, CL0UD
eller 1P redan gör — bygg ytan, inte tjänsten.