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

@injpass/connector

v2.1.8

Published

Lightweight SDK for embedding INJ Pass wallet in dApps - Now with popup-based authentication

Readme

@injpass/connector

Lightweight SDK for embedding INJ Pass wallet in your dApp via iframe.

⚡ What's New in v2.0

Popup-based Authentication Architecture - Completely redesigned to work around browser restrictions:

  • No Storage Access API needed - Works seamlessly in Safari/Chrome without user prompts
  • Bypasses iframe WebAuthn restrictions - Authentication happens in popup windows
  • Full Storage Partitioning compatibility - Future-proof for Chrome's third-party cookie deprecation
  • Better security - Origin validation and isolated authentication flow

Read the full architecture guide →

Features

  • 🔐 Passkey Authentication - Secure, passwordless wallet access using WebAuthn
  • 📱 Mobile Support - Works on iOS/Android browsers
  • 🎨 Flexible UI - Floating, modal, or inline modes
  • 🔒 Secure - Private keys never leave the iframe, authentication via popup
  • Lightweight - Zero dependencies, < 5KB gzipped
  • 🔄 Cross-dApp - One wallet works across all dApps
  • 🌐 Universal Browser Support - No more Storage Access API prompts!

How It Works (v2.0 Architecture)

sequenceDiagram
    participant DApp
    participant Iframe as INJ Pass iframe
    participant Popup as Auth Popup
    
    DApp->>Iframe: Load /embed in iframe
    User->>Iframe: Click "Connect"
    Iframe->>Popup: Open popup window
    Popup->>User: Request Passkey
    User->>Popup: Authenticate (biometrics)
    Popup->>Iframe: Send wallet info
    Popup->>Popup: Close
    Iframe->>DApp: Forward wallet info
    DApp->>DApp: Connected! ✅

Why popup windows?

  • ✅ Popup windows have full access to localStorage (no partitioning)
  • ✅ WebAuthn works perfectly in top-level popup windows
  • ✅ No browser prompts or permission requests needed
  • ✅ Works identically across all browsers

Installation

npm install @injpass/connector

or

pnpm add @injpass/connector

Quick Start

1. Configure Environment Variables

Add to your .env file:

# Development
VITE_INJPASS_EMBED_URL=http://localhost:3001/embed

# Production (after deployment to Vercel)
# VITE_INJPASS_EMBED_URL=https://inj-pass-frontend-test.vercel.app/embed

2. Configure CSP Headers (Recommended)

Add to your HTML or HTTP headers to allow the iframe:

<meta 
  http-equiv="Content-Security-Policy" 
  content="frame-src http://localhost:3001 'self'; script-src 'self' 'unsafe-inline'"
/>

Note: In v2.0, you don't need to configure allow="publickey-credentials-get" anymore - authentication happens in popup windows, not in the iframe!

3. Use the SDK

import { InjPassConnector } from '@injpass/connector';

// Create connector instance - embedUrl is REQUIRED
const connector = new InjPassConnector({
  embedUrl: import.meta.env.VITE_INJPASS_EMBED_URL  // Vite
  // or
  // embedUrl: process.env.REACT_APP_INJPASS_EMBED_URL  // Create React App
  // embedUrl: process.env.NEXT_PUBLIC_INJPASS_EMBED_URL  // Next.js
});

// Connect to wallet
const wallet = await connector.connect();
console.log('Connected:', wallet.address);

// Sign message
const signature = await wallet.signer.signMessage('Hello World');
console.log('Signature:', signature);

// Disconnect
connector.disconnect();

Important:

  • embedUrl is now REQUIRED - you must provide it in the config
  • Use environment variables to manage URLs across environments
  • The SDK automatically adds Passkey permissions to the iframe

Configuration Options

All options:

const connector = new InjPassConnector({
  // REQUIRED: Embed URL
  embedUrl: import.meta.env.VITE_INJPASS_EMBED_URL,

  // Position (for floating mode)
  position: {
    bottom: '20px',
    right: '20px'
  },

  // Size
  size: {
    width: '400px',
    height: '300px'
  },

  // Display mode: 'floating' | 'modal' | 'inline'
  mode: 'floating',

  // Container ID (for inline mode)
  containerId: 'wallet-container',

  // Auto-hide after connection
  autoHide: true
});

Environment Variables by Framework

Vite:

VITE_INJPASS_EMBED_URL=http://localhost:3001/embed
embedUrl: import.meta.env.VITE_INJPASS_EMBED_URL

Create React App:

REACT_APP_INJPASS_EMBED_URL=http://localhost:3001/embed
embedUrl: process.env.REACT_APP_INJPASS_EMBED_URL

Next.js:

NEXT_PUBLIC_INJPASS_EMBED_URL=http://localhost:3001/embed
embedUrl: process.env.NEXT_PUBLIC_INJPASS_EMBED_URL

Display Modes

Floating Mode (Default)

Fixed position overlay that appears on top of your dApp:

const connector = new InjPassConnector({
  mode: 'floating',
  position: { bottom: '20px', right: '20px' }
});

Modal Mode

Full-screen modal with backdrop:

const connector = new InjPassConnector({
  mode: 'modal',
  size: { width: '500px', height: '400px' }
});

Inline Mode

Embedded within a container element:

<div id="wallet-container" style="width: 100%; height: 500px;"></div>
const connector = new InjPassConnector({
  mode: 'inline',
  containerId: 'wallet-container'
});

API Reference

InjPassConnector

connect(): Promise<ConnectedWallet>

Displays the wallet iframe and waits for user to authenticate with Passkey.

Returns:

{
  address: string;           // Injective wallet address (inj1...)
  walletName?: string;       // User-defined wallet name
  signer: InjPassSigner;     // Signer instance for signing messages
}

Throws:

  • Error('Already connected') - If already connected
  • Error('Connection timeout') - If user doesn't authenticate within 60s
  • Error('Connection failed') - If authentication fails

disconnect(): void

Disconnects wallet and removes iframe from DOM.

show(): void

Shows the iframe (if hidden).

hide(): void

Hides the iframe.

InjPassSigner

signMessage(message: string): Promise<Uint8Array>

Signs a message using the wallet's private key (secp256k1).

Parameters:

  • message: String message to sign

Returns:

  • Promise<Uint8Array>: 64-byte signature (r + s)

Throws:

  • Error('Signing timeout') - If signing takes > 30s
  • Error(...) - If user rejects or signing fails

Security Considerations

1. Content Security Policy (CSP)

Recommended: Add frame-src directive to your CSP:

<!-- Development -->
<meta http-equiv="Content-Security-Policy" 
      content="frame-src http://localhost:3001 'self'" />

<!-- Production -->
<meta http-equiv="Content-Security-Policy" 
      content="frame-src https://your-injpass-domain.com 'self'" />

2. Popup Windows (New in v2.0)

The SDK uses popup windows for authentication:

  • ✅ Popups run in top-level browsing context (full storage access)
  • ✅ WebAuthn works without restrictions
  • ✅ Origin validation prevents unauthorized access
  • ⚠️ Users must allow popups for your site

3. Origin Validation

The SDK validates message origins automatically:

  • Development: Allows localhost and same-origin
  • Production: Can be configured with whitelist (contact support)

4. Private Key Isolation

  • Private keys are encrypted and stored in the INJ Pass domain only
  • Keys never leave the secure context
  • Only signatures are transmitted via postMessage
  • Popup window closes immediately after authentication

5. HTTPS Only

The wallet works on:

  • https:// origins
  • http://localhost (development only)
  • ❌ Other http:// origins are blocked

Troubleshooting

❌ "Popup blocked"

Error: Popup window doesn't open when clicking connect.

Solution:

  1. Click "Always allow popups from this site" in browser
  2. Ensure connect() is called directly from user interaction (not in async callback)
  3. Check browser settings to allow popups

❌ "Connection timeout"

Causes:

  1. CSP blocks iframe loading
  2. User doesn't have platform authenticator
  3. Network issues
  4. User closed popup without authenticating

Solutions:

  • Check CSP allows the iframe origin
  • Verify user device has Touch ID/Face ID/Windows Hello
  • Check browser console for iframe load errors
  • Ensure embedUrl is correct

❌ "No wallet found"

Solution:

  1. Create wallet at your INJ Pass instance's /welcome page
  2. For local development: http://localhost:3001/welcome
  3. The embed page will guide users to create wallet if none exists

❌ "Origin mismatch" in console

Normal in development: You may see origin warnings in development mode - these are informational only and won't block functionality.

In production: If you need strict origin whitelisting, contact support.

Browser Support

| Browser | Version | Status | Notes | |---------|---------|--------|-------| | Chrome | 67+ | ✅ Full support | Desktop & Android | | Safari | 16+ | ✅ Full support | macOS & iOS - No Storage Access prompt needed! | | Edge | 79+ | ✅ Full support | Chromium-based | | Firefox | 60+ | ✅ Full support | Desktop & Android |

Requirements:

  • ✅ WebAuthn platform authenticator (Touch ID/Face ID/Windows Hello)
  • ✅ Browser must support window.open() for popups
  • ✅ JavaScript enabled

Version History

v2.0.0 (2026-02-24) - Major Architecture Update

  • 🎉 Complete rewrite: Popup-based authentication architecture
  • No more Storage Access API prompts in Safari
  • Bypasses iframe WebAuthn restrictions
  • Future-proof for Chrome's third-party cookie deprecation
  • ✅ Relaxed origin validation (allows localhost in dev mode)
  • 📝 Comprehensive documentation update
  • 🔧 Improved error handling and timeout management

v0.1.4 (2026-02-23)

  • 🐛 Bug fixes and stability improvements
  • 📝 Documentation updates

v0.1.1 (2026-02-23)

  • ✅ Added Permissions Policy support for WebAuthn
  • ✅ Automatically adds allow attribute to iframe
  • 📝 Updated documentation with troubleshooting guide

v0.1.0 (2026-02-23)

  • 🎉 Initial release
  • ✅ Passkey-based authentication
  • ✅ Floating, modal, and inline modes
  • ✅ Cross-dApp wallet sharing

Migration from v0.x to v2.0

Good news: The API is 100% backward compatible! Just update:

npm update @injpass/connector

What changed internally:

  • Authentication now happens in popup windows instead of directly in iframe
  • No code changes needed in your dApp
  • Better browser compatibility automatically

Optional improvements:

  • Remove any Storage Access API handling code
  • Update CSP to remove unnecessary permissions-policy directives

Best Practices

1. Environment-based Configuration

const connector = new InjPassConnector({
  embedUrl: process.env.NODE_ENV === 'production'
    ? 'https://your-production-injpass.com/embed'
    : 'http://localhost:3001/embed'
});

2. Handle Popup Blockers

try {
  const wallet = await connector.connect();
  setWallet(wallet);
} catch (error) {
  if (error.message.includes('Popup blocked')) {
    alert('Please allow popups for this site to connect your wallet.');
  } else if (error.message.includes('timeout')) {
    alert('Connection timed out. Please try again.');
  } else {
    alert('Connection failed: ' + error.message);
  }
}

3. User Feedback

setLoading(true);
setStatus('Opening authentication window...');
try {
  const wallet = await connector.connect();
  setStatus('Connected successfully!');
  setConnected(true);
} catch (error) {
  setStatus('Connection failed');
} finally {
  setLoading(false);
}

4. Test Before Production

  • Test on multiple browsers (Chrome, Safari, Firefox, Edge)
  • Test on mobile devices (iOS Safari, Android Chrome)
  • Test with different popup blocker settings
  • Ensure popups work on first user interaction

Framework Examples

React

import { InjPassConnector } from '@injpass/connector';
import { useState } from 'react';

function WalletButton() {
  const [connector] = useState(() => new InjPassConnector({
    embedUrl: import.meta.env.VITE_INJPASS_EMBED_URL
  }));
  const [wallet, setWallet] = useState(null);
  const [loading, setLoading] = useState(false);

  const connect = async () => {
    setLoading(true);
    try {
      const w = await connector.connect();
      setWallet(w);
    } catch (error) {
      if (error.message.includes('Popup blocked')) {
        alert('Please allow popups for this site');
      }
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  const sign = async () => {
    if (!wallet) return;
    
    try {
      const sig = await wallet.signer.signMessage('Hello INJ Pass!');
      console.log('Signature:', sig);
    } catch (error) {
      console.error('Signing failed:', error);
    }
  };

  return (
    <div>
      {!wallet ? (
        <button onClick={connect} disabled={loading}>
          {loading ? 'Connecting...' : 'Connect Wallet'}
        </button>
      ) : (
        <>
          <p>Connected: {wallet.address}</p>
          <button onClick={sign}>Sign Message</button>
          <button onClick={() => connector.disconnect()}>Disconnect</button>
        </>
      )}
    </div>
  );
}

Vue 3

<script setup>
import { InjPassConnector } from '@injpass/connector';
import { ref } from 'vue';

const connector = new InjPassConnector({
  embedUrl: import.meta.env.VITE_INJPASS_EMBED_URL
});

const wallet = ref(null);
const loading = ref(false);

async function connect() {
  loading.value = true;
  try {
    wallet.value = await connector.connect();
  } catch (error) {
    console.error(error);
    alert('Connection failed: ' + error.message);
  } finally {
    loading.value = false;
  }
}

async function signMessage() {
  if (!wallet.value) return;
  
  try {
    const signature = await wallet.value.signer.signMessage('Hello!');
    console.log('Signature:', signature);
  } catch (error) {
    console.error(error);
  }
}
</script>

<template>
  <div>
    <button v-if="!wallet" @click="connect" :disabled="loading">
      {{ loading ? 'Connecting...' : 'Connect Wallet' }}
    </button>
    <div v-else>
      <p>Connected: {{ wallet.address }}</p>
      <button @click="signMessage">Sign Message</button>
    </div>
  </div>
</template>

Next.js (App Router)

'use client';

import { InjPassConnector } from '@injpass/connector';
import { useState } from 'react';

export default function WalletConnect() {
  const [connector] = useState(() => new InjPassConnector({
    embedUrl: process.env.NEXT_PUBLIC_INJPASS_EMBED_URL!
  }));
  const [wallet, setWallet] = useState(null);

  const connect = async () => {
    try {
      const w = await connector.connect();
      setWallet(w);
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <button onClick={connect}>
      {wallet ? `Connected: ${wallet.address}` : 'Connect'}
    </button>
  );
}

Vanilla JavaScript

<!DOCTYPE html>
<html>
<head>
  <title>INJ Pass Demo</title>
</head>
<body>
  <button id="connect-btn">Connect Wallet</button>
  <div id="status"></div>

  <script type="module">
    import { InjPassConnector } from '@injpass/connector';

    const connector = new InjPassConnector({
      embedUrl: 'http://localhost:3001/embed'
    });

    document.getElementById('connect-btn').addEventListener('click', async () => {
      try {
        const wallet = await connector.connect();
        document.getElementById('status').textContent = 
          `Connected: ${wallet.address}`;
      } catch (error) {
        alert('Failed: ' + error.message);
      }
    });
  </script>
</body>
</html>

License

MIT

Support

  • 📖 Documentation: https://docs.injpass.com
  • 🐛 GitHub Issues: https://github.com/injective/inj-pass/issues
  • 💬 Discord: https://discord.gg/injective
  • 📦 NPM Package: https://www.npmjs.com/package/@injpass/connector

Made with ❤️ by the INJ Pass Team