YeriaUI & YeriaApp
Les deux points d'entrée du SDK : construire, puis signer.
Description
Le SDK expose deux objets aux fournisseurs. Le partage se fait selon qui détient le secret.
| Objet | Détient une clé ? | Rôle |
|---|---|---|
YeriaUI | Non | Fabrique de vues. Construit des vues typées. Pure, sans état, jamais instanciée — utilisée comme Math ou JSON. |
YeriaApp | Oui (Ed25519) | Tout ce qui exige la clé privée : signer les vues, vérifier les jetons utilisateurs entrants, notifier, effectuer la rotation de clé. |
Construire ne demande pas de clé ; signer, si. Vous construisez une vue avec YeriaUI, puis vous la passez à app.serve(view) — un transfert de rôle, pas un aller-retour sur un même objet.
Tout le reste (YeriaSigner, YeriaPublicKeys, le client de plateforme, les vérificateurs) est interne. Ce n'est pas exporté et vous n'avez pas à y toucher.
1import { YeriaUI, YeriaApp } from '@numerum-tech/yeriasdk';
2
3const app = new YeriaApp({
4 appId: process.env.YERIA_APP_ID,
5 baseUrl: process.env.YERIA_BASE_URL,
6 privateKey: process.env.SERVICE_ED25519_PRIVATE_KEY,
7});
8
9const view = YeriaUI.createFormView('registration', 'User Registration')
10 .addTextField('name', 'Name', true)
11 .submitButton('Register', 'POST');
12
13return app.serve(view); // → { payload, signature }Les deux systèmes de clés
Ne les confondez pas : ils couvrent deux sens de circulation différents.
| Clé | Algorithme | Détenteur | Rôle |
|---|---|---|---|
| Clé de plateforme Yeria | RSA (RS256) | Yeria | Signe les jetons utilisateurs que reçoit votre backend. Vous ne faites que vérifier avec elle — le SDK la récupère et la met en cache pour vous. |
| Clé de votre service | Ed25519 | Vous | Signe les enveloppes de vue que vous renvoyez, ainsi que vos appels vers Yeria. La moitié publique est enregistrée sur votre service ; la privée ne quitte jamais votre backend. |
app.verifyUserToken() rejette tout jeton dont l'en-tête alg n'est pas RS256. Les enveloppes de vue et les enveloppes signées par un fournisseur sont toujours en Ed25519.
YeriaUI — la fabrique de vues
Sans clé. Importez et utilisez directement : il n'y a pas de constructeur.
Méthodes de fabrique
Chacune renvoie un nouveau constructeur de vue typée.
createFormView(formId: string, title: string, processId?: string): FormViewcreateReaderView(viewId: string, title: string, processId?: string): ReaderViewcreateActionListView(viewId: string, title: string, processId?: string): ActionListViewcreateActionGridView(viewId: string, title: string, processId?: string): ActionGridViewcreateIconGridView(viewId: string, title: string, processId?: string): IconGridViewcreateQRScanView(viewId: string, title: string, processId?: string): QRScanViewcreateQRDisplayView(viewId: string, title: string, processId?: string): QRDisplayViewcreateMessageView(viewId: string, title: string, processId?: string): MessageViewcreateCardView(viewId: string, title: string, processId?: string): CardViewcreateCarouselView(viewId: string, title: string, processId?: string): CarouselViewcreateTimelineView(viewId: string, title: string, processId?: string): TimelineViewcreateMediaView(viewId: string, title: string, processId?: string): MediaViewcreateMapView(viewId: string, title: string, processId?: string): MapView
Reconstituer une vue stockée
1fromJson(json: Record<string, unknown>): BaseViewTransforme du JSON de transport (un gabarit statique, ou une vue que vous avez persistée dans votre base) en une instance de vue typée et validée. À utiliser quand vous stockez vos écrans en JSON plutôt que de les reconstruire champ par champ :
1const view = YeriaUI.fromJson(await db.screens.get('home'));
2return app.serve(view);Corps d'erreur non signé
1error(spec: ProviderErrorSpec): ProviderErrorBodyConstruit un corps d'erreur byte-identique à celui de la plateforme, mais sans signature. À utiliser quand aucune clé n'est disponible (échec au démarrage, erreur de configuration). Si vous avez une clé, préférez app.serveError() : une erreur signée est une erreur à laquelle le mobile peut se fier.
YeriaApp — le détenteur de la clé
Configuration
appIdstringrequisprivateKeystringoptionnelpublicKeystringoptionnelprivateKey si omise.baseUrlstringoptionnelhttps://yeria.app. Nécessaire pour verifyUserToken, notify, fetchUserDetails, rotateKey.allowedDomainsstring[]optionnel[]).viewExpirationMinutesnumberoptionnelverifyIntegrity (par défaut 60).notificationTimeoutnumberoptionnel5000).Servir des vues
1serve(view: BaseView): SignedEnvelopeSigne une vue dans une enveloppe v3. C'est l'unique chemin de signature — renvoyez le résultat tel quel :
1res.json(app.serve(view));1serveError(spec: ProviderErrorSpec): SignedEnvelopeMême enveloppe, portant une erreur au lieu d'une vue. Le mobile vérifie la signature avant d'afficher quoi que ce soit : une erreur signée ne peut donc pas être falsifiée par un attaquant sur le réseau. Le champ status est indicatif — le mobile ne se fie pas au code HTTP.
SignedEnvelope
1{
2 payload: string, // JSON string: {"appId":…,"timestamp":…,"view":{…}}
3 signature: string // Ed25519 signature over the payload STRING BYTES, base64
4}La signature porte sur les octets exacts de payload. Un vérificateur doit contrôler ces octets avant d'analyser le JSON : re-sérialiser d'abord modifierait les octets et invaliderait la signature.
Vérification et clés
verifyIntegrity(envelope: SignedEnvelope)→ booleangetServicePublicKey()→ stringJetons utilisateurs entrants
1async verifyUserToken(
2 bearerToken: string,
3 expectedAudience?: string | number,
4): Promise<YeriaTokenClaims>Vérifie un jeton utilisateur émis par Yeria (RS256). Résout le kid du jeton contre les clés publiques de Yeria via un magasin interne à cache TTL — vous n'avez ni résolveur à câbler ni PEM à détenir. Impose iss='yeria' et exp > now ; passez expectedAudience pour épingler le jeton sur l'identifiant de votre service.
Lève YeriaPlatformUnreachableError quand Yeria est injoignable (renvoyez 503 — le jeton est peut-être valide). Une clé inconnue ou expirée remonte comme une erreur de vérification (renvoyez 401).
Profil utilisateur
1async fetchUserDetails(opts: {
2 userServiceToken: string;
3 fetch?: typeof fetch;
4}): Promise<UserDetails>Récupère le profil d'un utilisateur Yeria, autorisé par le jeton de service vivant de l'utilisateur lui-même — ni par vos préférences de notification, ni par un jeton porteur placé dans le corps. Le justificatif transmis est la signature Ed25519 de l'enveloppe.
Notifications
signNotification(notification: Notification)→ SecureNotificationResponseasync notify(notification: Notification)→ Promise<void>Voir Notifications pour les règles de distribution et les erreurs d'abonnement.
Rotation de clé
1async rotateKey(...)Enregistre une nouvelle clé publique Ed25519 pour votre service. L'ancienne reste valide pendant une courte période de grâce, pour ne pas casser les requêtes en cours.
Échappatoires statiques
Préférez les méthodes d'instance. Celles-ci existent pour le cas rare où vous détenez déjà le PEM exact, ou ne voulez aucun appel réseau.
static verifySignature(publicKey, payload, signature, onError?)static signView(view, appId, privateKey, timestamp?)SignedEnvelope à partir d'une clé ponctuelle.static verifyYeriaToken(jwt, yeriaPublicKey, expectedAudience?)static async verifyYeriaTokenWithResolver(jwt, resolver, expectedAudience?)kid.Exemple complet
1import express from 'express';
2import { YeriaUI, YeriaApp } from '@numerum-tech/yeriasdk';
3
4const app = new YeriaApp({
5 appId: process.env.YERIA_APP_ID,
6 baseUrl: process.env.YERIA_BASE_URL,
7 privateKey: process.env.SERVICE_ED25519_PRIVATE_KEY,
8});
9
10const server = express();
11
12server.get('/screens/registration', async (req, res) => {
13 const bearer = (req.headers.authorization ?? '').replace(/^Bearer /i, '');
14
15 try {
16 const claims = await app.verifyUserToken(bearer, process.env.YERIA_SERVICE_ID);
17
18 const form = YeriaUI.createFormView('registration', 'User Registration')
19 .setIntro(`Welcome, ${claims.sub}`)
20 .addTextField('name', 'Name', true)
21 .addEmailField('email', 'Email', true)
22 .submitButton('Register', 'POST');
23
24 return res.json(app.serve(form));
25 } catch (err) {
26 return res.status(401).json(app.serveError({
27 code: 'auth.invalid_token',
28 message: 'Session expirée, reconnectez-vous.',
29 status: 401,
30 }));
31 }
32});La charge utile reçue par le mobile :
1{
2 "payload": "{\"appId\":\"my-app\",\"timestamp\":1706443200000,\"view\":{\"id\":\"registration\",\"type\":\"Form\",\"content\":{…}}}",
3 "signature": "MEUCIQD…"
4}Voir aussi
- Intégration provider — clés, authentification, profils utilisateurs, de bout en bout
- Spécifications des composants — tous les types de vue