SubjectAssertionClaims identity.subject-assertion-claims@1

Generated at from openvibe-contracts v0.419.0 and openvibe-sdk v0.161.0.

Version
1.0.0
Owner
network
Visibility
public
Status
active
Compatibility
backward
Decision
ADR-003
Schema
https://openvibe.network/contracts/identity/subject-assertion-claims.v1.json

Claims of a short-lived ES256 assertion signed by OpenVibe.Network that says a person allowed one service to act for them on another service (ADR-003, amendment 2026-11-06; plan H1). Minted only from the person's own Network access token (POST /internal/subject-assertions): without that token nothing is signed. Carried on a money call in X-OV-Subject-Assertion. The signature key is published in Network's JWKS (kid ov-network-assertion-1, kty EC, alg ES256) beside the RS256 session key, so a receiver verifies it offline.

Fields

FieldTypeRequiredDescriptionConstraints
issstringyesIssuer; https://openvibe.network in production. The receiver checks it against the issuer it expects.
  • format uri
substringyesThe person the assertion is about: their canonical subject. The receiver refuses an assertion that does not name the person the route acts for.
  • pattern ^usr_[0-9A-HJKMNP-TV-Z]{26}$
audarray of stringyesThe service(s) the assertion is for. A receiver accepts it only when its own audience is in the list. A list is used when the assertion travels a chain (a tip goes from the site to Tips and on to Billing).
  • minItems 1
  • items: pattern ^openvibe\.[a-z0-9][a-z0-9.-]*$
actstringyesThe service the assertion was minted for (the one that held the person's token). It is the only service that may present it, unless `for` names others.
  • pattern ^svc:[a-z][a-z0-9-]{1,39}$
forarray of stringThe services that may carry the assertion (the minting service is always in it). A receiver presented by a service that is not in this list refuses the assertion (403 assertion.actor_mismatch).
  • items: pattern ^svc:[a-z][a-z0-9-]{1,39}$
jtistringyesUnique per assertion. A receiver remembers the jtis it has seen for the replay window and refuses a second use (403 assertion.replayed).
  • minLength 8
iatintegeryesIssued at (seconds since the epoch).
expintegeryesExpires at (seconds since the epoch). The assertion lives 60 seconds.
nbfintegerNot before (seconds since the epoch). Optional; a receiver forgives a small clock difference.

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: chain-tip-to-billing
{
  "iss": "https://openvibe.network",
  "sub": "usr_01JAB2C3D4E5F6G7H8J9K0MNA1",
  "aud": [
    "openvibe.tips",
    "openvibe.billing"
  ],
  "act": "svc:live",
  "for": [
    "svc:live",
    "svc:tips"
  ],
  "jti": "as_abcdef13",
  "iat": 1790000000,
  "exp": 1790000060
}
Valid: live-to-billing
{
  "iss": "https://openvibe.network",
  "sub": "usr_01JAB2C3D4E5F6G7H8J9K0MNA1",
  "aud": [
    "openvibe.billing"
  ],
  "act": "svc:live",
  "jti": "as_abcdef12",
  "iat": 1790000000,
  "exp": 1790000060
}
Rejected: no-jti
{
  "iss": "https://openvibe.network",
  "sub": "usr_01JAB2C3D4E5F6G7H8J9K0MNA1",
  "aud": [
    "openvibe.billing"
  ],
  "act": "svc:live",
  "iat": 1790000000,
  "exp": 1790000060
}
Rejected: no-person
{
  "iss": "https://openvibe.network",
  "sub": "svc:live",
  "aud": [
    "openvibe.billing"
  ],
  "act": "svc:live",
  "jti": "as_abcdef12",
  "iat": 1790000000,
  "exp": 1790000060
}
Rejected: other-audience
{
  "iss": "https://openvibe.network",
  "sub": "usr_01JAB2C3D4E5F6G7H8J9K0MNA1",
  "aud": [
    "media"
  ],
  "act": "svc:live",
  "jti": "as_abcdef12",
  "iat": 1790000000,
  "exp": 1790000060
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('identity.subject-assertion-claims@1', value);   // { valid, errors: [{ path, message }] }