FacturareIntră în aplicație
Integrare în aplicația taAserțiunea · pagina 6 din 10

Aserțiunea

Ce semnează serverul tău, cu ce cheie și cum, în Node.js, Python, PHP și C#.

Actualizat la

Aserțiunea e tot ce trimite serverul tău: cine e utilizatorul, ce firme vede și ce are voie, semnat cu cheia ta privată. O folosesc modul cu utilizatorii tăi și API-ul.

Cheile

Algoritmul este ES256 (ECDSA pe curba P-256, SHA-256), suportat nativ de OpenSSL, Node.js, Python, PHP, Java, .NET și Go. Cheia privată se generează și rămâne doar pe serverul tău (sau într-un KMS/HSM). Nu o pune niciodată în cod de browser sau în aplicații mobile.

openssl ecparam -name prime256v1 -genkey -noout -out chappa-private.pem
openssl pkcs8 -topk8 -nocrypt -in chappa-private.pem -out chappa-private.pk8.pem
openssl ec -in chappa-private.pem -pubout -out chappa-public.pem

Nu trebuie să ne trimiți cheia publică în avans: o pui în antetul fiecărei aserțiuni (jwk). Din ea calculăm amprenta care identifică integrarea.

Câmpurile

Aserțiunea este un JWS compact (format JWT), semnat ES256. Antetul poartă cheia publică:

{ "alg": "ES256", "typ": "JWT", "jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" } }

După prima utilizare poți trimite, în loc de jwk, doar "kid": "<amprenta>"; e opțional, pentru că jwk merge mereu. Orice alt algoritm (none, HS256, RS256…) e refuzat, la fel un antet cu crit sau o cheie care conține partea privată (d).

CâmpObligatoriuSemnificație
auddahttps://facturare.chappa.ro (string sau listă care îl conține).
subdaID-ul stabil al utilizatorului în aplicația ta (1–200 caractere). Nu folosi emailul dacă se poate schimba.
iat, expdaSecunde Unix. exp − iat ≤ 900 (15 minute); recomandat 60–300. Toleranță de ceas: 60 s.
jtidaIdentificator unic (8–128 caractere, ex. UUID). Fiecare aserțiune se poate folosi o singură dată.
orirecomandatOriginea paginii care afișează iframe-ul (ex. https://app.exemplu.ro). Sesiunea se leagă de ea; o aserțiune furată nu poate fi folosită din altă pagină, iar adresele de întoarcere după ANAF trebuie să fie pe ea.
name, emailnuAfișare.
scopenuPermisiuni, separate prin spațiu (listă). Lipsă = toate.
entnuFirmele utilizatorului, cu datele lor (detalii).
ent_locknutrue = utilizatorul folosește doar firmele din ent, iar datele firmelor neverificate sunt dictate de tine.
{
  "aud": "https://facturare.chappa.ro",
  "sub": "user_8842",
  "name": "Ana Popescu",
  "iat": 1791496800, "exp": 1791497100,
  "jti": "5f0c2a9e-0b5e-4d0b-9a62-7f1e8c3f2b11",
  "ori": "https://app.exemplu.ro",
  "ent": [{ "ref": "org_42", "kind": "pj", "cif": "RO18547290", "name": "Alfa SRL", "city": "Cluj-Napoca", "county": "Cluj", "role": "admin" }],
  "ent_lock": true
}

Semnarea pe server

Semnătura ES256 în JWS este r‖s brut, 64 de octeți (nu DER). Exemplele de mai jos nu au dependențe externe, cu excepția celor marcate.

import { createPrivateKey, createPublicKey, sign, randomUUID } from 'node:crypto';
import { readFileSync } from 'node:fs';

const privateKey = createPrivateKey(readFileSync('chappa-private.pem'));
const { kty, crv, x, y } = createPublicKey(privateKey).export({ format: 'jwk' });
const b64u = b => Buffer.from(b).toString('base64url');

export function chappaAssertion(user, origin) {
  const now = Math.floor(Date.now() / 1000);
  const header = { alg: 'ES256', typ: 'JWT', jwk: { kty, crv, x, y } };
  const claims = { aud: 'https://facturare.chappa.ro', sub: String(user.id), name: user.name,
                   iat: now, exp: now + 120, jti: randomUUID(), ori: origin };
  const input = b64u(JSON.stringify(header)) + '.' + b64u(JSON.stringify(claims));
  const sig = sign('sha256', Buffer.from(input), { key: privateKey, dsaEncoding: 'ieee-p1363' });
  return input + '.' + b64u(sig);
}

Poți folosi și o bibliotecă JWT (jose, PyJWT, firebase/php-jwt, jjwt, Nimbus). Important e să pui cheia publică în antet (jwk) și să semnezi ES256.

Exemple complete

  • node-server.mjs: un server Node.js fără dependențe care servește pagina gazdă și semnează aserțiunile.
  • host.html: pagina gazdă cu iframe-ul, produse și evenimente.
  • chappa-sign.mjs: semnează orice set de câmpuri (dovezi pentru portal, revocare).
  • chappa_sign.py: același lucru în Python.
  • assertion.php: endpoint de aserțiuni în PHP.