{"_id":"@amsemnat/expo-sdk","_rev":"2-38c972241d9d993e8ff420d6d5bae1ad","name":"@amsemnat/expo-sdk","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@amsemnat/expo-sdk","version":"0.1.0","keywords":["expo","nfc","eid","romania","pades","signing","amsemnat"],"author":{"name":"am-semnat contributors"},"license":"Apache-2.0","_id":"@amsemnat/expo-sdk@0.1.0","maintainers":[{"name":"andithemudkip","email":"andithemudkip@gmail.com"}],"homepage":"https://github.com/am-semnat/am-semnat-expo-sdk","bugs":{"url":"https://github.com/am-semnat/am-semnat-expo-sdk/issues"},"dist":{"shasum":"0ac0d813577a07a52cb6239d0c32f3f3353549c3","tarball":"https://registry.npmjs.org/@amsemnat/expo-sdk/-/expo-sdk-0.1.0.tgz","fileCount":56,"integrity":"sha512-QqujX2yhGygWwIy2Il10yESvUvgX0ccdvhUx5FfrPzakH8jb0ZK2xCPrK6B9ctSNs3AcsbqXTm8LyRLWNDMAKg==","signatures":[{"sig":"MEYCIQCwOuW4rB65baQsDKjLx0PCCwWm1PghDCpaaTFsv5wDmQIhAK/ldXXv+RKcJisShiimdAPW6IT0JzTftMcqaDu0/P/M","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":122507},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/__tests__/**/*.test.ts"],"testEnvironment":"node"},"main":"build/index.js","types":"build/index.d.ts","gitHead":"43b7cdaec1988d08bcb22d014fa1f4837fb796db","scripts":{"lint":"tsc --noEmit","test":"jest","build":"tsc --build","clean":"rm -rf build plugin/build","build:plugin":"tsc --build plugin","prepublishOnly":"npm run clean && npm run build && npm run build:plugin"},"_npmUser":{"name":"andithemudkip","email":"andithemudkip@gmail.com"},"repository":{"url":"git+https://github.com/am-semnat/am-semnat-expo-sdk.git","type":"git"},"_npmVersion":"11.4.2","description":"Expo module that wraps the am-semnat iOS and Android SDKs — NFC read + PAdES sign for Romanian CEI eID cards.","directories":{},"sideEffects":false,"_nodeVersion":"24.4.1","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.1.2","typescript":"^5.3.3","@types/jest":"^29.5.12","@types/react":"^19.1.1","expo-modules-core":"^2.0.0","expo-module-scripts":"^4.0.4"},"peerDependencies":{"expo":">=52","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-sdk_0.1.0_1776985976431_0.21381645519091474","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@amsemnat/expo-sdk","version":"0.1.1","description":"Expo module that wraps the am-semnat iOS and Android SDKs — NFC read + PAdES sign for Romanian CEI eID cards.","main":"build/index.js","types":"build/index.d.ts","sideEffects":false,"scripts":{"build":"tsc --build","build:plugin":"tsc --build plugin","clean":"rm -rf build plugin/build","lint":"tsc --noEmit","test":"jest","prepublishOnly":"npm run clean && npm run build && npm run build:plugin"},"keywords":["expo","nfc","eid","romania","pades","signing","amsemnat"],"repository":{"type":"git","url":"git+https://github.com/am-semnat/am-semnat-expo-sdk.git"},"bugs":{"url":"https://github.com/am-semnat/am-semnat-expo-sdk/issues"},"author":{"name":"am-semnat contributors"},"license":"Apache-2.0","homepage":"https://github.com/am-semnat/am-semnat-expo-sdk","peerDependencies":{"expo":">=52","react":"*","react-native":"*"},"devDependencies":{"@types/jest":"^29.5.12","@types/react":"^19.1.1","expo-module-scripts":"^4.0.4","expo-modules-core":"^2.0.0","jest":"^29.7.0","ts-jest":"^29.1.2","typescript":"^5.3.3"},"jest":{"preset":"ts-jest","testEnvironment":"node","testMatch":["<rootDir>/__tests__/**/*.test.ts"]},"_id":"@amsemnat/expo-sdk@0.1.1","gitHead":"f4ce67b628409c627047f4530617f2ca42f2fd01","_nodeVersion":"24.4.1","_npmVersion":"11.4.2","dist":{"integrity":"sha512-s926vly3Lm7gzaRPBTPiL4+8sG4dhVi84oU4cj7PjQVgisd6R5XDCnnlFLRNDH6ARE0nA1baM1sxFBNR79P1wg==","shasum":"7bcbd297a8a64d8f6633a6ceef7365195e4d5543","tarball":"https://registry.npmjs.org/@amsemnat/expo-sdk/-/expo-sdk-0.1.1.tgz","fileCount":58,"unpackedSize":125569,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBka99382A+bQ/N6iN9fZnOZc1X+BB6ikbWax9r2FoWzAiBNc1sdZptMkPAobHNzH2Ga9txKeo7nNuEmumO7krFmQQ=="}]},"_npmUser":{"name":"andithemudkip","email":"andithemudkip@gmail.com"},"directories":{},"maintainers":[{"name":"andithemudkip","email":"andithemudkip@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/expo-sdk_0.1.1_1777217530667_0.2387278443698153"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-23T23:12:56.326Z","modified":"2026-04-26T15:32:10.944Z","0.1.0":"2026-04-23T23:12:56.577Z","0.1.1":"2026-04-26T15:32:10.812Z"},"bugs":{"url":"https://github.com/am-semnat/am-semnat-expo-sdk/issues"},"author":{"name":"am-semnat contributors"},"license":"Apache-2.0","homepage":"https://github.com/am-semnat/am-semnat-expo-sdk","keywords":["expo","nfc","eid","romania","pades","signing","amsemnat"],"repository":{"type":"git","url":"git+https://github.com/am-semnat/am-semnat-expo-sdk.git"},"description":"Expo module that wraps the am-semnat iOS and Android SDKs — NFC read + PAdES sign for Romanian CEI eID cards.","maintainers":[{"name":"andithemudkip","email":"andithemudkip@gmail.com"}],"readme":"# @amsemnat/expo-sdk\n\nExpo Module for reading and signing with Romanian electronic identity\ncards (CEI / eID) over NFC. Wraps the `AmSemnatSDK` iOS pod and\n`ro.amsemnat:am-semnat-sdk` Android library behind a single TypeScript\nsurface.\n\n## Status\n\n`0.1.0` — pre-stable. Ships in lockstep with the iOS and Android sibling\nSDKs. Public surface is frozen; non-breaking additions only through 0.x.\n\n## Requirements\n\n- Expo SDK ≥ 52 (managed or bare — both work via `expo prebuild`)\n- iOS 15.0+ / Android API 24+\n- Device with NFC hardware (`NFCTagReaderSession` on iOS, `NfcAdapter` on\n  Android)\n\n## Installation\n\n```bash\nnpm install @amsemnat/expo-sdk\nnpx expo prebuild --clean\n```\n\nAdd the config plugin to `app.json` / `app.config.ts`:\n\n```jsonc\n{\n  \"expo\": {\n    \"plugins\": [\"@amsemnat/expo-sdk\"]\n  }\n}\n```\n\nOn iOS you must also supply `NFCReaderUsageDescription` in your app's\n`ios.infoPlist` block — Expo does not generate this for you:\n\n```jsonc\n{\n  \"expo\": {\n    \"ios\": {\n      \"infoPlist\": {\n        \"NFCReaderUsageDescription\": \"Tap your ID card to sign in.\"\n      }\n    }\n  }\n}\n```\n\nThe plugin handles the rest:\n\n- iOS — NFC reader-session formats entitlement + the two Romanian CEI\n  applet AIDs in Info.plist\n- Android — `<uses-feature android:name=\"android.hardware.nfc\">` +\n  BouncyCastle META-INF exclusion in the app's `packagingOptions`\n\n## Using in a plain React Native app (no Expo)\n\nThis package is an Expo Module, but Expo Modules run in any React Native\napp once `expo-modules-core` is installed — the managed workflow isn't\nrequired. From a `react-native init` project:\n\n```bash\nnpx install-expo-modules@latest\nnpm install @amsemnat/expo-sdk\ncd ios && pod install\n```\n\n`install-expo-modules` wires `expo-modules-core` into the iOS `AppDelegate`\nand Android `MainApplication`; no other source changes are needed. The\nTypeScript surface (`AmSemnat.readIdentity`, `.sign`, events, errors)\nbehaves identically.\n\nThe config plugin still runs if you keep an `app.json` and invoke\n`npx expo prebuild`. Without `expo prebuild`, apply the NFC entitlement /\nInfo.plist / AndroidManifest edits from [Installation](#installation)\nmanually — the plugin's job is to generate those, not to be required at\nruntime.\n\n## Quick start\n\n### Read identity\n\n```ts\nimport { AmSemnat, AmSemnatError } from '@amsemnat/expo-sdk';\n\ntry {\n  const identity = await AmSemnat.readIdentity({\n    can: '123456',    // 6-digit CAN from the card\n    pin1: '1234',     // optional PIN1 for eDATA (empty = skip)\n    onProgress: (step) => console.log('step:', step),\n  });\n  // identity.cnp, identity.firstName, identity.lastName, …\n  // identity.chipAuthenticated (UX signal only)\n  // identity.rawSodBase64 / .rawDg1Base64 / .rawDg2Base64\n} catch (err) {\n  if (err instanceof AmSemnatError) {\n    if (err.code === 'PIN_VERIFY_FAILED') {\n      console.log(`PIN1 wrong, ${err.retriesRemaining} retries left`);\n    } else if (err.code === 'PACE_AUTH_FAILED') {\n      console.log('Wrong CAN — prompt user');\n    }\n  }\n  throw err;\n}\n```\n\n### Sign a PDF byte-range hash (PAdES)\n\n```ts\nimport { AmSemnat } from '@amsemnat/expo-sdk';\n\nconst sig = await AmSemnat.sign({\n  can: '123456',\n  pin2: '123456',\n  pdfHashBase64,                            // 48-byte SHA-384, base64\n  signingTime: new Date().toISOString(),\n  onProgress: (step) => console.log('step:', step),\n});\n// sig.signatureBase64         — 96 bytes, raw ECDSA P-384 r‖s\n// sig.certificateBase64       — DER-encoded signing cert\n// sig.signedAttributesBase64  — DER-encoded SET of CMS signed attributes\n```\n\n### Offline passive authentication\n\n```ts\nimport { AmSemnat } from '@amsemnat/expo-sdk';\n\nconst result = AmSemnat.verifyPassiveOffline({\n  rawSodBase64: identity.rawSodBase64!,\n  dataGroups: {\n    DG1: identity.rawDg1Base64!,\n    DG2: identity.rawDg2Base64!,\n    DG14: identity.rawDg14Base64!,\n  },\n  trustAnchorsBase64: [/* `CSCA Romania`, DER → base64 */],\n});\nif (!result.valid) console.warn(result.errors);\n```\n\nServer-side verification against the official Romanian trust list is\ndelegated to `@amsemnat/verifier-node` (shipped separately).\n\nThe SDK doesn't bundle any certificates. Two Romanian authorities\npublish the certs the SDK interacts with, one per PKI:\n\n- **DGP — `CSCA Romania`**, published at\n  <https://pasapoarte.mai.gov.ro/csca.html>. Self-signed ICAO CSCA that\n  issues the Document Signer embedded in the eMRTD SOD. This is the\n  trust anchor for `AmSemnat.verifyPassiveOffline(...)`. Use the\n  self-signed certificate; the link certificates on that page are only\n  useful when migrating trust from a prior CSCA key.\n- **DGEP — `RO CEI MAI Root-CA` / `Sub-CA`**, published at\n  <https://hub.mai.gov.ro/cei/info/descarca-cert>. Issues the\n  per-citizen signing certificates stored in the CEI applet and used by\n  `AmSemnat.sign(...)`; those are the anchors for verifying the PAdES\n  signatures the SDK produces.\n\nYour app owns freshness and revocation — re-fetch on a cadence\nappropriate for your trust window.\n\n## Localizing the NFC sheet\n\niOS owns the NFC reader-session sheet; the SDK writes phase-specific\nstrings into it via an optional `messages` argument. Defaults are\nneutral English — production apps should pass a localized\n`NfcMessages`.\n\n```ts\nawait AmSemnat.readIdentity({\n  can, pin1,\n  messages: {\n    readyToScan:    t('nfc.holdCard'),\n    authenticating: t('nfc.authenticating'),\n    scanning:       t('nfc.reading'),\n    progressFormat: t('nfc.progressFormat'),  // e.g. '{phase} — {percent}%'\n    success:        t('nfc.done'),\n    tagLost:        t('nfc.cardMoved'),\n  },\n  onProgress,\n});\n```\n\n`progressFormat` composes the live per-DG read percentage into the\nsheet. Two tokens are substituted:\n\n- `{phase}`   → your `scanning` string\n- `{percent}` → the reader's 0-100 progress, rendered as an integer\n\nThe English default `'{phase} — {percent}%'` renders as\n`'Reading your card… — 40%'`. Pass `progressFormat: ''` to suppress\nthe percentage and show `scanning` verbatim.\n\n**Android has no system NFC sheet in reader mode** — render your own\nprogress UI from the `onProgress` callback. The `messages` argument\nis accepted for payload symmetry but discarded on Android.\n\n## Cancelling on navigation away\n\nOn Android, `NfcAdapter.enableReaderMode` is Activity-scoped — an\nin-app navigation away from a reading screen does **not** tear the\nsession down, so a follow-up `readIdentity` call on the same Activity\nfails with `SESSION_CANCELLED`-style conflicts. On iOS the reader\nsheet auto-invalidates when the app backgrounds but not on in-app\nscreen transitions.\n\n`AmSemnat.cancelCurrentOp()` handles both. Typical wiring with\nexpo-router:\n\n```ts\nimport { useFocusEffect } from 'expo-router';\nimport { useCallback } from 'react';\nimport { AmSemnat } from '@amsemnat/expo-sdk';\n\nuseFocusEffect(\n  useCallback(() => {\n    return () => {\n      AmSemnat.cancelCurrentOp().catch(() => {});\n    };\n  }, []),\n);\n```\n\nThe in-flight `readIdentity` / `sign` promise rejects with\n`AmSemnatError` code `'SESSION_CANCELLED'`. No-op when nothing is in\nflight. Safe to call repeatedly.\n\n## Progress events\n\n`ReadProgress`:\n`paceEstablishing → readingDg14 → chipAuthenticating → readingDg1 →\nreadingDg2 → readingDg7 → readingEData → complete`\n\n`SignProgress`:\n`paceEstablishing → verifyingPin → readingCertificate → signing → complete`\n\nDG14 fires before the other DGs because its keys are needed for Chip\nAuthentication. Consumers should localize each value independently rather\nthan depending on the exact order.\n\n## Logging\n\n```ts\nAmSemnat.setLogger({\n  debug: console.log,\n  info: console.log,\n  error: (msg, err) => console.error(msg, err),\n});\n\n// Detach:\nAmSemnat.setLogger(null);\n```\n\nMessages flow from the native `AmSemnatLogger` protocols through an\n`onLog` event; CAN and PIN bytes are redacted by the native SDKs before\nthey reach you.\n\n## What's not in 0.x\n\n- Active Authentication (DG15) — the iOS fork supports it but the API\n  surface deliberately omits it for 0.x; pass the `rawDg*Base64` fields\n  to `@amsemnat/verifier-node` for transferable proof instead.\n- Low-level `tag:` overloads from the native SDKs — no safe equivalent\n  across React Native's threading model.\n\n## License\n\nApache-2.0 for this package's own code. See [`LICENSE`](LICENSE),\n[`NOTICE`](NOTICE), and [`ATTRIBUTION.md`](ATTRIBUTION.md). Third-party\nattribution obligations flow through the sibling native SDKs.\n","readmeFilename":"README.md"}