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

Delegation

Delegate scoped access to credentials without sharing the raw secret.

Security architecture

Understand the end-to-end encryption model that underpins sharing.

Audit log

Track every share creation, view, and expiry event.