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

@ldclabs/ic-auth

v0.5.2

Published

TypeScript client SDK for IC-Auth identities, deterministic CBOR signing, and compact envelope types.

Readme

@ldclabs/ic-auth

License Test NPM version

The TypeScript signing SDK for IC-Auth. It provides deterministic CBOR, compact envelope and delegation types, SHA3-256 message digests, Base64URL helpers, and identity signing through @icp-sdk/core. Signature verification is provided by the Rust verifier or HTTP service.

Installation

npm install @ldclabs/ic-auth @icp-sdk/core @noble/hashes cborg

The package ships ES modules and TypeScript declarations. It supports browser applications and declares Node.js >=20.0.0. Its peer dependency ranges are:

| Dependency | Range | | --------------- | --------- | | @icp-sdk/core | >=5.0.0 | | @noble/hashes | >=1.8.0 | | cborg | >=4.5.0 |

The examples use top-level await, so run them as ES modules. The package has an independent release version from the Rust workspace; see package.json.

Sign a structured message

import {
  Ed25519KeyIdentity,
  bytesToBase64Url,
  deterministicEncode,
  signMessage,
  toDelegationIdentity
} from '@ldclabs/ic-auth'

const identity = toDelegationIdentity(Ed25519KeyIdentity.generate())
const message = { challenge: 'login-123', origin: 'https://example.com' }
const envelope = await signMessage(identity, message)
const token = bytesToBase64Url(deterministicEncode(envelope))
console.log(`ICP ${token}`)

toDelegationIdentity preserves an existing DelegationIdentity or wraps a plain SignIdentity with an empty delegation chain. Pass an existing delegated identity to retain the user's principal and include its delegations in the envelope. Generating a fresh Ed25519 identity, as above, creates a new principal.

signMessage returns a SignedEnvelopeCompact containing p, s, and h, plus d when the identity has delegations. Send the token as Authorization: ICP <token> to your own endpoint using the Rust parser. The standalone service takes the token in a JSON/CBOR request body instead; see its complete client example.

Sign bytes or a precomputed digest

import {
  Ed25519KeyIdentity,
  sha3_256,
  signArbitrary,
  toDelegationIdentity
} from '@ldclabs/ic-auth'

const identity = toDelegationIdentity(Ed25519KeyIdentity.generate())
const requestBytes = new TextEncoder().encode('login-challenge-123')
const digest = sha3_256(requestBytes)
const envelope = await signArbitrary(identity, digest)

| Helper | Operation | | -------------------------------- | ------------------------------------------------------ | | digestMessage(value) | SHA3-256 of deterministic CBOR for value | | signMessage(identity, value) | Signs digestMessage(value) | | signArbitrary(identity, bytes) | Signs the supplied bytes directly, storing them in h |

Calling signMessage with a Uint8Array hashes its CBOR byte-string encoding, including the CBOR prefix. To match Rust's SignedEnvelope::sign_message(identity, raw_bytes), hash the raw bytes with sha3_256 and call signArbitrary, as above. Alternatively, deterministically CBOR-encode the same structured value in Rust before calling sign_message.

The SDK does not define the application's challenge schema. The verifier should independently compute the expected digest and enforce challenge expiry, one-time use, target, and authorization rules.

Deterministic CBOR and binary fields

deterministicEncode wraps cborg's encode with RFC 8949 options and explicitly selects the shortest floating-point representation. This keeps encoded bytes stable with cborg versions before 5.1.4, whose preset forced float64. The package also exports encode, decode, rfc8949EncodeOptions, and compareBytes; the re-exported options are cborg's own preset.

Use Uint8Array for binary fields and bigint for delegation expiration in nanoseconds. Match the exact field names, value types, and optional-field presence on both sides when signing across languages. The default encode function is available for ordinary CBOR; use deterministicEncode for signed or hashed data.

JSON.stringify(envelope) is not the IC-Auth JSON wire encoding: it does not turn Uint8Array into Base64URL and cannot serialize bigint. Transport an envelope as CBOR, or Base64URL-encode its CBOR bytes for an HTTP token or the service's JSON request body.

Compact types and conversions

| Type | Compact keys | | ------------------------------- | --------------------------------------------------------------------------------------------------- | | SignedEnvelopeCompact | p: public key; s: signature; h: optional digest; d: optional delegation chain | | DelegationCompact | p: delegated key; e: nanosecond expiration; t: optional targets; perm: optional permissions | | SignedDelegationCompact | d: delegation; s: signature | | DeepLinkSignInRequestCompact | s: session key; m: maximum lifetime in milliseconds | | DeepLinkSignInResponseCompact | u: user key; d: delegations; a: authentication method; o: origin |

DelegationPermissions is 'queries' | 'all'. Full-name types use Uint8Array, bigint, and Principal values. Compact delegation targets are Uint8Array[], and compact deep-link responses contain SignedDelegationCompact[]. The converters restore Principal objects and bigint timestamps/lifetimes when expanding decoded compact payloads; compact integer fields accept number | bigint because CBOR decodes safe integers as numbers. The toDelegation, toSignedDelegation, toSignedEnvelope, and deep-link converters each have a corresponding ...Compact conversion.

When upgrading callers that construct compact payloads themselves, replace Principal targets with target.toUint8Array() and use compact nested delegations. Prefer the conversion helpers to build these payloads. Full-name types containing Principal objects should be converted to compact form before CBOR transport.

import { toSignedEnvelope, toSignedEnvelopeCompact } from '@ldclabs/ic-auth'

// Illustrative bytes for shape conversion, not a valid signed envelope.
const compact = { p: new Uint8Array([1, 2, 3]), s: new Uint8Array([4, 5, 6]) }
const full = toSignedEnvelope(compact)
const converted = toSignedEnvelopeCompact(full)

Converters map between typed shapes; they do not verify signatures or validate untrusted input. They may return the original object when it is already in the requested form. Envelope converters also accept the explicitly typed LegacySignedEnvelope shape with public_key. The TypeScript deep-link exports describe payloads and convert their fields; Rust provides the URL construction/parsing helpers.

Base64 helpers

| Helper | Encoding | | ------------------------- | ---------------------------------------------------------------------------------- | | bytesToBase64Url(bytes) | Unpadded Base64URL, suitable for envelope tokens | | base64ToBytes(text) | Same as fromBase64 | | toBase64(bytes) | Padded standard Base64 | | fromBase64(text) | Standard Base64 or Base64URL decoding using native helpers, or atob without them |

Decoding accepts padded or unpadded input in either alphabet, and removes the b64: prefix that the Rust types write for byte fields in JSON. Malformed input throws in every runtime. The encoders return unprefixed values, which Rust also accepts.

Development

From this directory, with Node.js and pnpm installed:

pnpm install --frozen-lockfile
pnpm format:check
pnpm typecheck
pnpm build
pnpm test
pnpm coverage

pnpm build emits JavaScript and declarations into dist. pnpm typecheck checks source and test types. Tests cover CBOR fixtures shared with Rust, wire conversions, delegation permissions, identity signing, and native/Node/browser Base64 handling. CI also runs the SDK with the minimum supported cborg version. pnpm format formats sources, configuration and this README with Prettier.

Related packages

License

Copyright © 2024-2026 LDC Labs.

Licensed under the MIT License. See LICENSE.