Skip to main content

Secure Credential Sharing

ID Wispera provides zero-knowledge credential sharing using AES-256-GCM encryption. Sharing creates a payload (encrypted passport data) and a key (256-bit decryption key). The server never sees the key.
The encryption key is derived client-side and is never transmitted to or stored on the server. Only the encrypted payload is persisted.

Share Scopes

ScopeCredential Value IncludedDescription
fullYesComplete passport including the raw credential value
read-onlyYes (flagged immutable)Recipient can view but should not export the value
metadata-onlyNoPassport metadata only, credential value is stripped
import { createShareLink } from '@id-wispera/core';

const { payload, key } = await createShareLink(passport, {
  scope: 'read-only',
  expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
  maxViews: 5,
});

console.log('Payload:', payload);
console.log('Key:', key);

CLI

idw share <passport-id> --scope read-only --expires 24h --max-views 5
import { resolveShareLink } from '@id-wispera/core';

const passport = await resolveShareLink(payload, key);
console.log('Shared passport:', passport.name);

Share Types

Full Share

Includes the complete passport with credential value. Use for transferring credentials to trusted parties.
const { payload, key } = await createShareLink(passport, { scope: 'full' });

Read-Only Share

Includes the credential value but signals that it should not be exported or copied.
const { payload, key } = await createShareLink(passport, { scope: 'read-only' });

Metadata-Only Share

Strips the credential value entirely. Useful for sharing information about a credential without exposing the secret.
import { createMetadataShare } from '@id-wispera/core';

const { payload, key } = await createMetadataShare(passport, {
  expiresAt: '2026-03-01T00:00:00Z',
  maxViews: 10,
});

One-Time Share

Expires after a single view. Defaults to a 60-minute time window.
One-time shares are irrecoverable once viewed. Make sure the intended recipient is ready to receive the credential before generating the link.
import { createOneTimeShare } from '@id-wispera/core';

const { payload, key } = await createOneTimeShare(passport, 60);

Share URL Helpers

For web-based sharing, the key can be placed in the URL fragment (after #), which browsers never send to the server.
Placing the decryption key in the URL fragment ensures it stays client-side. The server only ever receives the payload identifier.
import { createShareUrl, parseShareUrl } from '@id-wispera/core';

const url = createShareUrl('https://vault.example.com', payload, key);
const parsed = parseShareUrl(url);

Inspecting and Validating Shares

import { getShareInfo, isShareValid } from '@id-wispera/core';

const info = getShareInfo(payload);
const validity = isShareValid(payload);

Security Considerations

Always combine time-based expiry and view limits for defence in depth. A share with no constraints remains accessible indefinitely.
  1. The encryption key is never stored on the server. All encryption and decryption happen client-side.
  2. URL fragments are not sent to servers. The #key portion of a share URL stays in the browser.
  3. Audit trail records all share events. Every creation, view, and expiry is logged.
  4. Use metadata-only for visibility without exposure. Share context about a credential without revealing the secret.
  5. Combine expiry and view limits. Layer multiple constraints for the strongest protection.

Next Steps