@spruceid/opencred-dc-api
v0.2.0
Published
TypeScript/Node.js wrapper for OpenCred DC API WASM binary
Readme
DC API WASM - Node.js/TypeScript Package
A WebAssembly-based Node.js/TypeScript package for the DC API, providing digital credential operations with OpenID4VP support and session management through wasm-bindgen generated bindings.
Installation
npm install @spruceid/opencred-dc-apiQuick Start
import { DcApi, JsOid4VpSessionStore } from '@spruceid/opencred-dc-api';
// Prepare your PKCS8 PEM encoded private key for signing
const privateKeyPem = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----`;
// Create session stores
const oid4vpStore = JsOid4VpSessionStore.createMemoryStore();
const dcApiStore = {
async newSession(sessionId: string, session: any) {
// Implementation
return { id: sessionId, secret: 'generated-secret' };
},
async getSession(id: string, clientSecret: string) {
// Implementation
return null;
},
async getSessionUnauthenticated(id: string) {
// Implementation
return null;
},
async updateSession(sessionId: string, session: any) {
// Implementation
},
async removeSession(sessionId: string) {
// Implementation
}
};
// Create and initialize DC API instance
const dcApi = await DcApi.new(
privateKeyPem, // PKCS8 PEM encoded private key for signing
'https://api.example.com',
'https://api.example.com/submit',
'https://api.example.com/reference',
new Uint8Array([/* your cert chain */]),
oid4vpStore,
dcApiStore
);
// Create a session
const session = await dcApi.create_new_session();
// Make a request with the session
const request = {
// Your DC API request data
};
const result = await dcApi.initiate_request(
session.id,
session.secret,
request,
'my-app/1.0.0'
);
console.log(result);
// Submit a response
const response = {
// Your response data
};
const submitResult = await dcApi.submit_response(
session.id,
session.secret,
response
);
// Clean up when done
dcApi.free();Building from Source
This package wraps a Rust WASM binary. To build the complete package:
Build the WASM binary:
npm run build:wasmThis runs the build script that uses wasm-bindgen to generate the JavaScript bindings.
Build the TypeScript wrapper:
npm run build:tsBuild everything:
npm run build
API Reference
DcApi Class
The main WebAssembly class generated by wasm-bindgen that provides DC API functionality.
import { DcApi } from '@spruceid/opencred-dc-api';Constructor
static new(key: string, base_url: string, submission_endpoint: string, reference_endpoint: string, cert_chain_pem: Uint8Array, oid4vp_session_store: JsOid4VpSessionStore, js_dc_api_session_store: DcApiSessionStore): Promise<DcApi>
Create and initialize a new DC API instance.
Parameters:
key: PKCS8 PEM encoded private key used for signing operationsbase_url: Base URL for the DC APIsubmission_endpoint: Endpoint for submitting responsesreference_endpoint: Endpoint for referencescert_chain_pem: Certificate chain in PEM format as Uint8Arrayoid4vp_session_store: OID4VP session storage implementationjs_dc_api_session_store: DC API session storage implementation
const privateKeyPem = `-----BEGIN PRIVATE KEY-----
MIGH...
-----END PRIVATE KEY-----`;
const dcApi = await DcApi.new(
privateKeyPem,
'https://api.example.com',
'https://api.example.com/submit',
'https://api.example.com/reference',
certChainPem,
oid4vpStore,
dcApiStore
);Methods
create_new_session(): Promise<any>
Create a new DC API session.
const session = await dcApi.create_new_session();
console.log('Session:', session);initiate_request(session_id: string, session_secret: string, request: any, user_agent?: string | null): Promise<any>
Initiate a DC API request with session credentials.
const result = await dcApi.initiate_request(
sessionId,
sessionSecret,
request,
'my-app/1.0.0'
);submit_response(session_id: string, session_secret: string, response: any): Promise<any>
Submit a response for a DC API session.
const result = await dcApi.submit_response(
sessionId,
sessionSecret,
response
);free(): void
Free the WebAssembly memory used by this instance.
dcApi.free();JsOid4VpSessionStore Class
WebAssembly-compatible session store that delegates to JavaScript storage implementations.
Constructor
constructor(store: Oid4VpSessionStore)Creates a new WebAssembly session store with JavaScript storage implementation.
class MySessionStore {
async initiate(session) {
// Store session in your preferred storage
localStorage.setItem(`session_${session.uuid}`, JSON.stringify(session));
}
async updateStatus(uuid, status) {
// Update session status
const session = JSON.parse(localStorage.getItem(`session_${uuid}`));
session.status = status;
localStorage.setItem(`session_${uuid}`, JSON.stringify(session));
}
async getSession(uuid) {
// Get session from storage
const sessionData = localStorage.getItem(`session_${uuid}`);
if (!sessionData) throw new Error('Session not found');
return JSON.parse(sessionData);
}
async removeSession(uuid) {
// Remove session from storage
localStorage.removeItem(`session_${uuid}`);
}
}
const sessionStore = new JsOid4VpSessionStore(new MySessionStore());Static Methods
createMemoryStore(): JsOid4VpSessionStore
Create a simple in-memory session store for testing purposes.
const sessionStore = JsOid4VpSessionStore.createMemoryStore();generateSessionUuid(): string
Generate a new UUID for session identification.
const uuid = JsOid4VpSessionStore.generateSessionUuid();parseUuid(uuid_str: string): string
Parse and validate a UUID string.
const parsed = JsOid4VpSessionStore.parseUuid('550e8400-e29b-41d4-a716-446655440000');Status Creation Helpers
// Create different status types
const sentRequestByRef = JsOid4VpSessionStore.createStatusSentRequestByReference();
const sentRequest = JsOid4VpSessionStore.createStatusSentRequest();
const receivedResponse = JsOid4VpSessionStore.createStatusReceivedResponse();
const success = JsOid4VpSessionStore.createStatusCompleteSuccess({ data: 'result' });
const failure = JsOid4VpSessionStore.createStatusCompleteFailure('reason');
const error = JsOid4VpSessionStore.createStatusCompleteError('cause');Session Storage Interfaces
OID4VP Session Store Interface
interface Oid4VpSessionStore {
initiate(session: any): Promise<void>;
updateStatus(uuid: string, status: any): Promise<void>;
getSession(uuid: string): Promise<any>;
removeSession(uuid: string): Promise<void>;
}DC API Session Store Interface
interface DcApiSessionStore {
newSession(sessionId: string, session: any): Promise<SessionCreationResponse>;
getSession(id: string, clientSecret: string): Promise<any | null>;
getSessionUnauthenticated(id: string): Promise<any | null>;
updateSession(sessionId: string, session: any): Promise<void>;
removeSession(sessionId: string): Promise<void>;
}JsDcApiSessionDriver Class
A supporting class for DC API session management (generated by wasm-bindgen).
class JsDcApiSessionDriver {
free(): void;
}This class is primarily used internally by the DC API implementation.
Error Handling
The WASM functions may throw errors that are propagated as JavaScript exceptions:
try {
const result = await dcApi.initiate_request(
sessionId,
sessionSecret,
request
);
} catch (error) {
console.error('DC API Error:', error);
}CI/CD & Releases
This package uses automated CI/CD workflows for testing, validation, and publishing.
Automated Testing
Every push and pull request triggers comprehensive testing:
- WASM Build Validation: Ensures the Rust/WASM build succeeds
- Package Structure: Validates correct file locations and package.json structure
- Integration Tests: Tests that generated bindings load correctly
- Security Audits: npm and Cargo dependency vulnerability scanning
- Code Quality: Rust formatting and Clippy linting
Automated Publishing
Releases are triggered by pushing git tags in the format npm/v[version]:
# Create and push a release
npm run release:patch # Creates tag npm/v1.0.1
git push origin npm/v1.0.1The release workflow will:
- Validate the package version matches the tag
- Set up Rust toolchain and wasm-bindgen
- Build the WASM binary and JS bindings
- Publish to npm with public access
Release Process
For detailed release instructions, see RELEASE.md.
Quick release workflow:
# Increment version and create tag
npm run release:minor # or release:patch, release:major
# Push the tag to trigger publishing
git push origin npm/v$(node -p "require('./package.json').version")Manual Development
For local development without CI:
Prerequisites
- Node.js 16+
- Rust toolchain with
wasm32-unknown-unknowntarget - wasm-bindgen-cli
Setup
Clone the repository
Install dependencies:
npm installBuild the project:
npm run build
Scripts
npm run build- Build only WASM (for publishing)npm run build:dev- Build WASM + TypeScript (for development)npm run build:wasm- Build only the WASM binarynpm run build:ts- Build only TypeScript wrappernpm run clean- Clean build artifactsnpm run prerelease- Validate package before release
Testing
Run the test suite:
npm testTests include:
- Unit tests for all API classes
- Session management tests
- WASM loader tests
- Error handling tests
- Storage implementation tests
Package Structure
npm-package/
├── dist/ # Built output (published to npm)
│ ├── dc_api_wasm.js # wasm-bindgen generated JavaScript
│ ├── dc_api_wasm.d.ts # wasm-bindgen generated TypeScript definitions
│ ├── dc_api_wasm_bg.wasm # WebAssembly binary
│ └── dc_api_wasm_bg.wasm.d.ts # WebAssembly TypeScript definitions
├── src/ # TypeScript source files (if any)
├── tests/ # Test files (excluded from npm)
├── build-wasm.sh # WASM build script (excluded from npm)
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration (excluded from npm)
├── README.md # Package documentation
└── RELEASE.md # Release process documentation (excluded from npm)Example: Complete Flow
import { DcApi, JsOid4VpSessionStore } from '@spruceid/opencred-dc-api';
async function main() {
// Create session stores
const oid4vpStore = JsOid4VpSessionStore.createMemoryStore();
const dcApiStore = {
async newSession(sessionId: string, session: any) {
console.log('Creating new session:', sessionId);
return { id: sessionId, secret: 'generated-secret' };
},
async getSession(id: string, clientSecret: string) {
console.log('Getting session:', id);
return null;
},
async getSessionUnauthenticated(id: string) {
return null;
},
async updateSession(sessionId: string, session: any) {
console.log('Updating session:', sessionId);
},
async removeSession(sessionId: string) {
console.log('Removing session:', sessionId);
}
};
// Initialize the API
const privateKeyPem = process.env.PRIVATE_KEY_PEM!; // PKCS8 PEM private key
const dcApi = await DcApi.new(
privateKeyPem,
process.env.DC_API_URL!,
process.env.DC_API_SUBMIT_URL!,
process.env.DC_API_REFERENCE_URL!,
new Uint8Array(Buffer.from(process.env.CERT_CHAIN!, 'base64')),
oid4vpStore,
dcApiStore
);
try {
// Create a session
const session = await dcApi.create_new_session();
console.log('Created session:', session);
// Prepare a request
const request = {
type: 'credential_request',
data: {
// Request data
}
};
// Make the request
const result = await dcApi.initiate_request(
session.id,
session.secret,
request,
'my-app/1.0.0'
);
console.log('Request result:', result);
// Submit a response if needed
const response = {
// Response data
};
const submitResult = await dcApi.submit_response(
session.id,
session.secret,
response
);
console.log('Submit result:', submitResult);
} catch (error) {
console.error('Error:', error);
} finally {
// Clean up
dcApi.free();
}
}
main().catch(console.error);Requirements
- Node.js: >= 16.0.0
- WASM Support: Node.js has built-in WebAssembly support
License
MIT
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Run
npm test - Submit a pull request
Troubleshooting
WASM Module Not Found
If you get errors about missing WASM files:
npm run build:wasmMake sure wasm-bindgen-cli is installed:
cargo install wasm-bindgen-cliBuild Failures
If the WASM build fails, ensure you have the correct environment:
# Ensure the correct target is available
rustup target add wasm32-unknown-unknown
# Install wasm-bindgen-cli if not already installed
cargo install wasm-bindgen-cliRuntime Errors
If you encounter runtime errors:
- Ensure
dcApi.init(config)was called with valid configuration before using other methods - Check that the WASM binary is properly included in your bundle
- Verify Node.js version compatibility (>= 16.0.0)
- Make sure session credentials are valid when making requests
Session Management Issues
- Sessions have limited lifetimes; create new sessions as needed
- Store session credentials securely
- Don't reuse sessions across different security contexts
Support
For issues and questions:
- GitHub Issues: Create an issue
- Documentation: Check the inline TypeScript documentation
- Examples: See the
tests/directory for usage examples
