Skip to content

OpenVTC/rp-sdk-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@openvtc/rp-sdk

Server-side SDK for Relying Parties (RPs) consuming SIOPv2 id_tokens from the OpenVTC browser plugin (window.vtaWallet.login).

What this package does

window.vtaWallet.login() POSTs a SIOPv2 self-issued id_token to your /auth/ endpoint. The token is a compact EdDSA JWS signed by the wallet's holder did:key. The signature is not optional — without verifying it, any page can forge a login as any DID.

This SDK is the audited verification path. verifyIdToken:

  • pins alg to EdDSA (no algorithm substitution),
  • enforces SIOPv2 iss === sub,
  • pins aud to your RP DID (no leniency),
  • pins nonce to the challenge you issued (constant-time match),
  • checks iat / exp within a configurable clock-skew window,
  • resolves the issuer DID and verifies the JWS signature against the resolved Ed25519 verification method.

Failure modes surface as IdTokenVerificationError with a typed reason — log it so operators can distinguish misconfigured audience from a forged token.

Why this exists

The browser-plugin demo skips verification — it trusts whatever the wallet POSTs. The demo is widely copy-pasted into production code, inheriting the gap. The May 2026 OpenVTC security review flagged this as a high-severity issue (H2). This SDK is the fix.

Install

npm install @openvtc/rp-sdk

Usage

Verify an id_token

import { verifyIdToken, KeyResolver } from "@openvtc/rp-sdk";

const resolver = new KeyResolver(); // did:key only; see below

const verified = await verifyIdToken({
  idToken: req.body.id_token,
  audience: process.env.RP_DID!,
  nonce: sessionStore.challengeFor(req.body.session_id),
  resolver,
});

// verified.subject is the wallet's holder DID; bind your session to it.
console.log(`logged in: ${verified.subject}`);

Establish a session cookie

import { establishSession } from "@openvtc/rp-sdk";

const accessToken = await myJwtMinter.mint({ sub: verified.subject });
const { subject, cookie } = establishSession(verified, accessToken);

res.cookie(cookie.name, cookie.value, cookie.options);
// SDK sets HttpOnly + Secure + SameSite=Strict by default.

DID resolvers

The bundled KeyResolver handles did:key:z6Mk… (Ed25519 multikey) in-process — no network round-trip, no cache concerns.

For did:peer:2 (the wallet's default for inbound RP-initiated flows), did:webvh, or did:web — implement the DidResolver interface against your preferred resolver. A thin wrapper around affinidi-did-resolver-cache-sdk covers all of them.

import type { DidResolver } from "@openvtc/rp-sdk";

class MultiMethodResolver implements DidResolver {
  async resolveAuthenticationKey(did: string): Promise<Uint8Array> {
    if (did.startsWith("did:key:")) return keyResolver.resolveAuthenticationKey(did);
    // ... did:peer / did:webvh / did:web cases
  }
}

Roadmap

Planned for follow-up minor versions:

  • requireStepUp() middleware — gates routes behind acr=aal2.
  • refreshProxy() middleware — drop-in /auth/refresh proxy.
  • Express + Fastify + Hono framework adapters.
  • DIDComm-transport variant for RPs that prefer the wallet's authcrypt flow over the REST SIOPv2 flow.

License

Apache-2.0

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors