npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@br.gov.dataprev.inji/wallet-sdk

v1.0.7

Published

Headless Verifiable Credentials SDK for React Native - Pure logic layer with event-based API (no UI components)

Downloads

252

Readme

vc-sdk-headless

Headless Verifiable Credentials SDK for React Native

Pure logic layer with event-based API. No UI components - perfect for integrating into any React Native app with custom UI.


Features

Headless Architecture - Zero UI components, pure business logic ✅ Event-Based API - Listen to events, render your own UI ✅ OpenID4VC Support - Full OpenID4VCI credential issuance ✅ OpenID4VP Support - Complete credential sharing/verification flow ✅ Error Handling - Detailed error events including HTTP 424 handling ✅ Authentication - OAuth 2.0 / OpenID Connect support ✅ Device Registration - CPF + FCM token registration ✅ Notification Support - Push notification handling ✅ TypeScript - Full type safety


Installation

npm install vc-sdk-headless
# or
yarn add vc-sdk-headless

Peer Dependencies

npm install @react-native-async-storage/async-storage \
            @react-native-community/netinfo \
            expo-crypto \
            react-native-device-info \
            react-native-keychain \
            react-native-mmkv

Quick Start

1. Initialize SDK

import { VCSDK } from 'vc-sdk-headless';

await VCSDK.init({
  appId: 'my-wallet-app',
  network: {
    baseUrl: 'https://api.example.com',
    notificationBaseUrl: 'https://notifications.example.com',
    timeout: 30000,
    oauth: {
      authorizationUrl: 'https://sso.staging.acesso.gov.br/authorize',
      tokenUrl: 'https://sso.staging.acesso.gov.br/token',
      userInfoUrl: 'https://sso.staging.acesso.gov.br/userinfo/',
      clientId: 'your-client-id',
      clientSecret: 'your-client-secret',
      redirectUri: 'myapp://oauth/callback',
      scopes: ['openid', 'profile', 'email'],
    },
  },
  storage: {
    encrypted: true,
  },
});

2. Listen to Events

// Credential download events
VCSDK.events.on('credential:downloadStarted', ({ credentialType, issuer }) => {
  console.log('Download started:', credentialType.name);
  // Show loading UI
});

VCSDK.events.on('credential:downloadProgress', ({ current, total, progress }) => {
  console.log(`Progress: ${progress}%`);
  // Update progress bar
});

VCSDK.events.on('credential:downloadComplete', ({ credential }) => {
  console.log('Downloaded:', credential.id);
  // Show success, refresh credential list
});

VCSDK.events.on('credential:downloadError', ({ error, credentialType }) => {
  console.error('Download failed:', error);
  // Show error UI with retry option
});

VCSDK.events.on('credential:notAvailable424', ({ credentialType, issuer }) => {
  console.log('Credential not available from provider');
  // Show "Share" button instead of retry
});

// Authentication required
VCSDK.events.on('auth:required', ({ authUrl, reason }) => {
  console.log('Auth required:', authUrl);
  // Show WebView with authUrl
});

3. Use SDK Methods

// Get all issuers
const issuers = await VCSDK.issuers.getAll();

// Get credential types for an issuer
const credTypes = await VCSDK.issuers.getCredentialTypes(issuer.id);

// Download credential
const credential = await VCSDK.credentials.download(issuer, credentialType);

// Get all downloaded credentials
const credentials = await VCSDK.credentials.getAll();

// Delete credential
await VCSDK.credentials.delete(credentialId);

Configuration

OAuth 2.0 Configuration

The SDK supports configurable OAuth 2.0 endpoints for authentication. All OAuth URLs (authorization, token, userinfo) can be customized during SDK initialization.

Default OAuth Endpoints

If you don't provide OAuth configuration, the SDK uses these defaults:

{
  authorizationUrl: 'https://sso.staging.acesso.gov.br/authorize',
  tokenUrl: 'https://sso.staging.acesso.gov.br/token',
  userInfoUrl: 'https://sso.staging.acesso.gov.br/userinfo/',
  clientId: 'inji-dev',
  clientSecret: '<default-secret>',
  redirectUri: 'http://localhost:3001/redirect',
  scopes: ['openid', 'profile', 'email'],
}

Custom OAuth Configuration

To use custom OAuth endpoints (e.g., for production environment):

await VCSDK.init({
  appId: 'my-wallet-app',
  network: {
    baseUrl: 'https://api.production.example.com',
    oauth: {
      // Production OAuth endpoints
      authorizationUrl: 'https://sso.acesso.gov.br/authorize',
      tokenUrl: 'https://sso.acesso.gov.br/token',
      userInfoUrl: 'https://sso.acesso.gov.br/userinfo/',

      // Your app credentials
      clientId: 'production-client-id',
      clientSecret: 'production-client-secret',
      redirectUri: 'myapp://oauth/callback',

      // Required scopes
      scopes: ['openid', 'profile', 'email', 'govbr_confiabilidades'],
    },
  },
});

Environment-Based Configuration

Recommended pattern for switching between environments:

const environment = process.env.NODE_ENV; // 'production' or 'development'

const config = {
  appId: 'my-wallet-app',
  network: {
    baseUrl: environment === 'production'
      ? 'https://api.production.example.com'
      : 'https://api.staging.example.com',
    oauth: {
      authorizationUrl: environment === 'production'
        ? 'https://sso.acesso.gov.br/authorize'
        : 'https://sso.staging.acesso.gov.br/authorize',
      tokenUrl: environment === 'production'
        ? 'https://sso.acesso.gov.br/token'
        : 'https://sso.staging.acesso.gov.br/token',
      userInfoUrl: environment === 'production'
        ? 'https://sso.acesso.gov.br/userinfo/'
        : 'https://sso.staging.acesso.gov.br/userinfo/',
      clientId: environment === 'production'
        ? process.env.OAUTH_CLIENT_ID_PROD
        : process.env.OAUTH_CLIENT_ID_DEV,
      clientSecret: environment === 'production'
        ? process.env.OAUTH_CLIENT_SECRET_PROD
        : process.env.OAUTH_CLIENT_SECRET_DEV,
      redirectUri: 'myapp://oauth/callback',
      scopes: ['openid', 'profile', 'email'],
    },
  },
};

await VCSDK.init(config);

OAuth Config Reference

| Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | authorizationUrl | string | No | Staging URL | OAuth authorization endpoint | | tokenUrl | string | No | Staging URL | OAuth token endpoint | | userInfoUrl | string | No | Staging URL | OAuth user info endpoint | | clientId | string | No | 'inji-dev' | OAuth client ID | | clientSecret | string | No | Default secret | OAuth client secret | | redirectUri | string | No | 'http://localhost:3001/redirect' | OAuth redirect URI | | scopes | string[] | No | ['openid', 'profile', 'email'] | OAuth scopes |

Security Note

For production apps, never hardcode client secrets in your code. Use environment variables or secure configuration management:

// ❌ Bad: Hardcoded secret
oauth: {
  clientSecret: 'abc123secret',
}

// ✅ Good: Environment variable
oauth: {
  clientSecret: process.env.OAUTH_CLIENT_SECRET,
}

// ✅ Better: Backend token exchange (no secret in app)
// Implement server-side token exchange instead

Event Reference

Credential Events

| Event | Data | Description | |-------|------|-------------| | credential:downloadStarted | { credentialType, issuer } | Credential download initiated | | credential:downloadProgress | { current, total, progress, currentStep } | Download progress update | | credential:downloadComplete | { credential, credentialType, issuer } | Download successful | | credential:downloadError | { error, errorCode, credentialType, issuer } | Download failed | | credential:notAvailable424 | { credentialType, issuer, message } | Provider doesn't have credential (HTTP 424) | | credential:deleted | { credentialId } | Credential deleted | | credential:listUpdated | { credentials[] } | Credential list changed |

Share Events (OpenID4VP)

| Event | Data | Description | |-------|------|-------------| | share:requestParsed | { verifierInfo, requestedCredentials, authRequest } | Share request parsed | | share:downloadStarted | { totalCredentials } | Downloading missing credentials | | share:downloadProgress | { current, total, currentItem } | Download progress | | share:downloadComplete | { successCount, error424Count, realErrorCount } | Downloads finished | | share:credentialNotAvailable424 | { credentialType, credentialName, issuer } | Credential not available (424) | | share:consentRequired | { verifierInfo } | User consent needed | | share:sharingStarted | - | Sharing in progress | | share:sharingProgress | { message } | Sharing progress message | | share:sharingComplete | { success, protocolNumber, transactionId } | Sharing finished | | share:error | { error, step } | Share error occurred | | share:userDeclined | - | User declined sharing |

Authentication Events

| Event | Data | Description | |-------|------|-------------| | auth:required | { authUrl, issuer, reason } | Authentication needed | | auth:started | - | Auth process started | | auth:progress | { message } | Auth progress message | | auth:complete | { success, token } | Auth finished | | auth:error | { error, message } | Auth failed | | auth:sessionExpired | - | Session expired | | auth:logout | - | User logged out |

Issuer Events

| Event | Data | Description | |-------|------|-------------| | issuer:listUpdated | { issuers[] } | Issuer list loaded | | issuer:selected | { issuer } | Issuer selected | | issuer:credentialTypesLoaded | { issuer, credentialTypes[] } | Credential types loaded | | issuer:error | { error, issuer } | Issuer operation failed |

Device Events

| Event | Data | Description | |-------|------|-------------| | device:registrationStarted | - | Device registration started | | device:registrationComplete | { deviceId, cpf } | Registration successful | | device:registrationError | { error } | Registration failed | | device:fcmTokenUpdated | { token } | FCM token updated |

Notification Events

| Event | Data | Description | |-------|------|-------------| | notification:received | { notification, type } | Notification received | | notification:credentialReady | { credentialType, issuer, notificationId } | Credential ready for download |

SDK Events

| Event | Data | Description | |-------|------|-------------| | sdk:initialized | - | SDK initialization complete | | sdk:ready | - | SDK ready for use | | sdk:error | { error, context } | SDK-level error |


API Reference

VCSDK.credentials

download(issuer, credentialType): Promise<VC | null>

Download a credential from an issuer.

Events emitted:

  • credential:downloadStarted
  • credential:downloadProgress
  • credential:downloadComplete (success)
  • credential:downloadError (failure)
  • credential:notAvailable424 (HTTP 424)
  • auth:required (needs authentication)

Example:

const credential = await VCSDK.credentials.download(issuer, credentialType);
if (credential) {
  console.log('Downloaded:', credential.id);
}

getAll(): Promise<VC[]>

Get all stored credentials.

get(credentialId): Promise<VC | null>

Get a single credential by ID.

delete(credentialId): Promise<boolean>

Delete a credential.

Events emitted:

  • credential:deleted
  • credential:listUpdated

VCSDK.issuers

getAll(): Promise<Issuer[]>

Get all available issuers.

Events emitted:

  • issuer:listUpdated

getCredentialTypes(issuerId): Promise<CredentialType[]>

Get credential types offered by an issuer.

Events emitted:

  • issuer:credentialTypesLoaded

get(issuerId): Promise<Issuer>

Get a single issuer by ID.


VCSDK.auth

getAuthUrl(): Promise<string>

Get the authorization URL for OAuth authentication.

Usage:

const authUrl = await VCSDK.auth.getAuthUrl();
// Show WebView with this URL
// When user completes auth, extract token and call:
await VCSDK.auth.completeAuthentication(token);

completeAuthentication(token): Promise<void>

Complete authentication after user finishes OAuth flow.

Events emitted:

  • auth:started
  • auth:complete (success)
  • auth:error (failure)

isAuthenticated(): Promise<boolean>

Check if user is currently authenticated.

logout(): Promise<void>

Logout user.

Events emitted:

  • auth:logout

VCSDK.share

parseRequest(shareUrl): Promise<{ verifierInfo, requestedCredentials, authRequest }>

Parse a share request URL (OpenID4VP).

Events emitted:

  • share:requestParsed
  • share:error

Example:

const { verifierInfo, requestedCredentials, authRequest } =
  await VCSDK.share.parseRequest(shareUrl);

// Show verifier info and requested credentials to user
console.log('Verifier:', verifierInfo.name);
console.log('Requested:', requestedCredentials.map(c => c.name));

downloadCredentials(requestedCredentials): Promise<{ successCount, error424Count, realErrorCount }>

Download missing credentials for sharing.

Events emitted:

  • share:downloadStarted
  • share:downloadProgress
  • share:downloadComplete
  • share:credentialNotAvailable424
  • auth:required

Example:

const result = await VCSDK.share.downloadCredentials(requestedCredentials);

if (result.successCount > 0) {
  // At least one credential downloaded, can proceed to share
} else {
  // All downloads failed, show retry
}

completeShare(authRequest, requestedCredentials): Promise<{ success, protocolNumber }>

Complete the share flow after user grants consent.

Events emitted:

  • share:consentRequired
  • share:sharingStarted
  • share:sharingProgress
  • share:sharingComplete
  • share:error

Example:

// After user clicks "Authorize"
const result = await VCSDK.share.completeShare(authRequest, requestedCredentials);

if (result.success) {
  console.log('Protocol:', result.protocolNumber);
  // Show success screen
}

declineShare()

User declined sharing.

Events emitted:

  • share:userDeclined

VCSDK.device

register(cpf, fcmToken): Promise<void>

Register device with CPF and FCM token for push notifications.

Events emitted:

  • device:registrationStarted
  • device:registrationComplete
  • device:registrationError
  • device:fcmTokenUpdated

VCSDK.notifications

handleNotification(notification)

Handle an incoming push notification.

Events emitted:

  • notification:received
  • notification:credentialReady

Complete Flow Examples

Download Credential Flow

// 1. Get issuers
VCSDK.events.on('issuer:listUpdated', ({ issuers }) => {
  // Display issuer list in UI
});

const issuers = await VCSDK.issuers.getAll();

// 2. Get credential types
VCSDK.events.on('issuer:credentialTypesLoaded', ({ credentialTypes }) => {
  // Display credential types in UI
});

const credTypes = await VCSDK.issuers.getCredentialTypes(selectedIssuer.id);

// 3. Download credential
VCSDK.events.on('credential:downloadStarted', () => {
  // Show loading modal
});

VCSDK.events.on('credential:downloadProgress', ({ progress }) => {
  // Update progress bar: progress%
});

VCSDK.events.on('auth:required', ({ authUrl }) => {
  // Show WebView with authUrl
  // After auth completes:
  // await VCSDK.auth.completeAuthentication(token);
  // Then retry download
});

VCSDK.events.on('credential:notAvailable424', () => {
  // Hide loading, show "Share" button instead of retry
});

VCSDK.events.on('credential:downloadComplete', ({ credential }) => {
  // Hide loading, show success, refresh list
});

VCSDK.events.on('credential:downloadError', ({ error }) => {
  // Hide loading, show error with retry button
});

await VCSDK.credentials.download(issuer, credentialType);

Share Credential Flow (OpenID4VP)

// 1. Parse share request from deep link
const shareUrl = "openid4vp://...";

VCSDK.events.on('share:requestParsed', ({ verifierInfo, requestedCredentials }) => {
  // Show verifier info card
  // Show requested credentials list
  // Show "Download" button
});

const { verifierInfo, requestedCredentials, authRequest } =
  await VCSDK.share.parseRequest(shareUrl);

// 2. Download missing credentials
VCSDK.events.on('share:downloadStarted', ({ totalCredentials }) => {
  // Show download modal: "Downloading 0/4"
});

VCSDK.events.on('share:downloadProgress', ({ current, total, currentItem }) => {
  // Update modal: "Downloading 2/4: CAFCredential"
});

VCSDK.events.on('share:credentialNotAvailable424', ({ credentialName }) => {
  // Log: "Provider doesn't have ${credentialName}"
  // Continue downloading others
});

VCSDK.events.on('auth:required', ({ authUrl }) => {
  // Show WebView, then continue
});

VCSDK.events.on('share:downloadComplete', ({ successCount, error424Count }) => {
  // Hide download modal
  // If successCount > 0: Show "Share" button
  // If successCount === 0: Show "Retry" button
});

const downloadResult = await VCSDK.share.downloadCredentials(requestedCredentials);

// 3. Get consent from user
if (downloadResult.successCount > 0) {
  // Show consent modal: "Authorize sharing with ${verifierInfo.name}?"

  // If user clicks "Authorize":
  VCSDK.events.on('share:sharingStarted', () => {
    // Show sharing modal
  });

  VCSDK.events.on('share:sharingProgress', ({ message }) => {
    // Update modal: message
  });

  VCSDK.events.on('share:sharingComplete', ({ success, protocolNumber }) => {
    // Hide modal, show success screen with protocol number
  });

  await VCSDK.share.completeShare(authRequest, requestedCredentials);

  // If user clicks "Decline":
  VCSDK.share.declineShare();
  // Show decline screen
}

Error Handling

HTTP 424 - Credential Not Available

When a credential provider doesn't have a requested credential:

VCSDK.events.on('credential:notAvailable424', ({ credentialType, issuer }) => {
  // Option 1: Change button from "Download" to "Share"
  setButtonText('Share');
  setButtonColor('purple');

  // Option 2: Show message
  Alert.alert(
    'Credential Not Available',
    `${credentialType.name} is not available from ${issuer.name}. You can share your existing credentials.`
  );
});

Authentication Errors

VCSDK.events.on('auth:required', ({ authUrl, reason }) => {
  // Show WebView
  setShowAuthWebView(true);
  setAuthWebViewUrl(authUrl);
});

VCSDK.events.on('auth:error', ({ error, message }) => {
  Alert.alert('Authentication Error', message);
});

VCSDK.events.on('auth:sessionExpired', () => {
  // Prompt re-authentication
});

TypeScript Support

Full TypeScript support with type definitions for all events and methods:

import { VCSDK, VC, Issuer, CredentialType } from 'vc-sdk-headless';

// Events are typed
VCSDK.events.on('credential:downloadComplete', (data) => {
  // data.credential is typed as VC
  // data.credentialType is typed as CredentialType
  // data.issuer is typed as Issuer
});

// Methods have return types
const credentials: VC[] = await VCSDK.credentials.getAll();
const issuer: Issuer = await VCSDK.issuers.get(issuerId);

Architecture

┌─────────────────────────────────────────┐
│         Your React Native App          │
│  (UI Screens, Components, Navigation)   │
└────────────────┬────────────────────────┘
                 │
                 │ Events & Method Calls
                 ▼
┌─────────────────────────────────────────┐
│         vc-sdk-headless                 │
│  ┌───────────────────────────────────┐  │
│  │  VCSDKCoreHeadless (Event API)    │  │
│  └───────────────┬───────────────────┘  │
│                  │                       │
│  ┌───────────────▼───────────────────┐  │
│  │  Services Layer                   │  │
│  │  • CredentialService              │  │
│  │  • IssuerService                  │  │
│  │  • ShareVCService                 │  │
│  │  • AuthService                    │  │
│  └───────────────┬───────────────────┘  │
│                  │                       │
│  ┌───────────────▼───────────────────┐  │
│  │  Platform Adapters                │  │
│  │  • Storage (AsyncStorage/MMKV)    │  │
│  │  • Crypto (Expo Crypto)           │  │
│  │  • Network (Fetch API)            │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

License

MIT


Support

For issues, questions, or contributions: