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

@protocol-01/auth-sdk

v0.1.0

Published

Protocol 01 Authentication SDK - Login with blockchain subscriptions

Downloads

13

Readme

@protocol-01/auth-sdk

Authentication SDK for "Login with Protocol 01". Users scan a QR code with the P01 mobile app, confirm with biometrics, and authenticate using their blockchain wallet and subscription status. Includes both a client SDK (for frontends) and a server SDK (for backend verification).

Installation

npm install @protocol-01/auth-sdk @solana/web3.js

Quick Start

Client Side (Frontend)

import { P01AuthClient } from '@protocol-01/auth-sdk/client';

const auth = new P01AuthClient({
  serviceId: 'my-service',
  serviceName: 'My Streaming Service',
  callbackUrl: 'https://myservice.com/auth/callback',
  logoUrl: 'https://myservice.com/logo.png',
  subscriptionMint: 'SUBSxxxx...', // Optional: require active subscription
  sessionTtl: 300_000, // 5 minutes
});

// Create a login session with QR code
const session = await auth.createSession();

// Display the QR code in your UI
document.getElementById('qr').innerHTML = session.qrCodeSvg;
// Or use as an image: <img src={session.qrCodeDataUrl} />

// Wait for the user to scan and confirm
const result = await auth.waitForCompletion(session.sessionId, {
  pollInterval: 2000,
  timeout: 300_000,
});

if (result.success) {
  console.log('Authenticated wallet:', result.wallet);
  console.log('Subscription active:', result.subscriptionActive);
}

// Listen for session events
const unsubscribe = auth.onSessionEvent(session.sessionId, (event) => {
  if (event.type === 'session_completed') {
    console.log('Login successful:', event.wallet);
  }
});

Server Side (Backend)

import { P01AuthServer } from '@protocol-01/auth-sdk/server';

const auth = new P01AuthServer({
  serviceId: 'my-service',
  subscriptionMint: 'SUBSxxxx...', // Optional
  rpcUrl: 'https://api.mainnet-beta.solana.com',
  maxTimestampAge: 60_000, // 60 seconds
});

// In your callback endpoint (Express/Fastify)
app.post('/auth/callback', async (req, res) => {
  const result = await auth.verifyCallback(req.body);

  if (result.success) {
    // Create a session for the authenticated user
    req.session.wallet = result.wallet;
    req.session.subscriptionActive = result.subscriptionActive;
    res.json({ success: true });
  } else {
    res.status(401).json({ error: result.error });
  }
});

// Or use as Express middleware
app.use(auth.middleware());
// Adds req.p01Auth = { wallet, subscriptionActive } when valid

// Direct subscription check
const status = await auth.checkSubscription('wallet_address');
console.log('Active:', status.active, 'Balance:', status.balance);

Devnet Testing

Use the network shorthand to connect to devnet without looking up the RPC URL:

import { P01AuthServer } from '@protocol-01/auth-sdk/server';

const auth = new P01AuthServer({
  serviceId: 'my-service',
  network: 'devnet', // auto-selects https://api.devnet.solana.com
});

Or pass the URL explicitly:

const auth = new P01AuthServer({
  serviceId: 'my-service',
  rpcUrl: 'https://api.devnet.solana.com',
});

Available networks: 'mainnet-beta', 'devnet', 'testnet', 'localnet'.

Production Deployment

Custom Session Storage

The default in-memory session store works for single-server development. For production, provide a persistent store via sessionStore (server) or sessionStorage (client):

import { createClient } from 'redis';
import { P01AuthServer, type ServerSessionStore } from '@protocol-01/auth-sdk/server';

const redis = createClient();
await redis.connect();

const sessionStore: ServerSessionStore = {
  async get(sessionId) {
    const data = await redis.get(`p01:session:${sessionId}`);
    return data ? JSON.parse(data) : null;
  },
  async set(session) {
    const ttl = Math.max(0, Math.floor((session.expiresAt - Date.now()) / 1000));
    await redis.set(
      `p01:session:${session.sessionId}`,
      JSON.stringify(session),
      { EX: ttl }
    );
  },
  async delete(sessionId) {
    await redis.del(`p01:session:${sessionId}`);
  },
};

const auth = new P01AuthServer({
  serviceId: 'my-service',
  sessionStore,
});

HTTPS Requirement

Auth callbacks contain wallet signatures. Always serve your callback endpoint over HTTPS in production. The x-p01-auth header used by the middleware is base64-encoded -- HTTPS ensures it cannot be intercepted.

CORS Setup

If your frontend and backend are on different origins, configure CORS to allow the callback:

import cors from 'cors';

app.use(cors({
  origin: ['https://myservice.com'],
  methods: ['POST'],
  allowedHeaders: ['Content-Type', 'x-p01-auth'],
}));

Complete Express.js Example

import express from 'express';
import cors from 'cors';
import { P01AuthServer } from '@protocol-01/auth-sdk/server';

const app = express();
app.use(express.json());
app.use(cors({
  origin: ['https://myservice.com'],
  allowedHeaders: ['Content-Type', 'x-p01-auth'],
}));

const auth = new P01AuthServer({
  serviceId: 'my-service',
  subscriptionMint: 'SUBSxxxx...',
  network: 'mainnet-beta',
  maxTimestampAge: 60_000,
});

// Option 1: Explicit callback endpoint
app.post('/auth/callback', async (req, res) => {
  const result = await auth.verifyCallback(req.body);
  if (result.success) {
    // Issue your own session token / JWT here
    res.json({ token: createJwt(result.wallet!), wallet: result.wallet });
  } else {
    res.status(401).json({ error: result.error });
  }
});

// Option 2: Middleware on protected routes
app.use('/api', auth.middleware());

app.get('/api/profile', (req, res) => {
  if (!req.p01Auth) {
    return res.status(401).json({ error: 'Not authenticated' });
  }
  res.json({ wallet: req.p01Auth.wallet });
});

app.listen(3000);

Error Handling

verifyCallback() never throws. It returns { success: false, error: string } on failure. Common errors:

| Error | Cause | Fix | |---|---|---| | "Timestamp expired or invalid" | Auth response is older than maxTimestampAge (default 60s) | Ensure client and server clocks are roughly synced. Increase maxTimestampAge if needed. | | "Invalid signature" | Ed25519 signature does not match the challenge | The signing wallet does not match publicKey, or the challenge was tampered with. | | "Subscription not active" | Wallet does not hold the required SPL token | User needs to purchase/renew their subscription token. | | "Session not found" | Session ID does not exist in the store | Session may have expired or been cancelled. Create a new session. | | "Session expired" | Session TTL has elapsed | Default TTL is 5 minutes. User needs to scan a fresh QR code. | | "Invalid callback: missing required fields ..." | Callback body is missing sessionId, wallet, signature, or publicKey | Ensure the mobile app is sending the complete AuthResponse object. |

Network errors during on-chain subscription verification are caught internally and result in "Subscription not active". Check your RPC URL if verification consistently fails.

API Reference

P01AuthClient (Frontend)

| Method | Description | |---|---| | constructor(config: P01AuthClientConfig) | Create a client with service configuration | | createSession(options?) | Create a new auth session with QR code (returns sessionId, qrCodeSvg, qrCodeDataUrl, deepLink, expiresAt) | | getSession(sessionId) | Get a session by ID | | updateSession(sessionId, updates) | Update session status | | handleCallback(body) | Process the auth callback from the mobile app | | waitForCompletion(sessionId, options?) | Poll or wait for session completion | | onSessionEvent(sessionId, callback) | Subscribe to session events (returns unsubscribe function) | | cancelSession(sessionId) | Cancel and clean up a session |

P01AuthServer (Backend)

| Method | Description | |---|---| | constructor(config: P01AuthServerConfig) | Create with service ID, subscription mint, and RPC URL or network | | verifyCallback(response, session?) | Verify an authentication callback | | verifySignature(response, challenge?) | Verify a wallet signature | | verifySubscription(wallet, proof?) | Verify on-chain subscription status | | validateSubscriptionProof(proof) | Validate a subscription proof structure | | middleware() | Express/Fastify middleware for auth header verification | | checkSubscription(wallet) | Directly check if a wallet has an active subscription |

Auth Flow Types

  • AuthSession -- Session state (sessionId, challenge, status, walletAddress, signature)
  • SessionStatus -- 'pending' | 'scanned' | 'confirmed' | 'completed' | 'expired' | 'rejected' | 'failed'
  • AuthQRPayload -- QR code payload (protocol version, service, session, challenge, callback)
  • AuthResponse -- Response from the mobile app (wallet, signature, publicKey, subscriptionProof)
  • VerificationResult -- Verification outcome (success, wallet, subscriptionActive, error)
  • SubscriptionProof -- On-chain subscription proof (mint, balance, expiresAt, slot)
  • AuthEvent -- Session lifecycle events
  • SolanaNetwork -- 'mainnet-beta' | 'devnet' | 'testnet' | 'localnet'

Sub-path Imports

import { P01AuthClient } from '@protocol-01/auth-sdk/client';
import { P01AuthServer } from '@protocol-01/auth-sdk/server';

License

MIT