Skip to content

๐Ÿ” Secure, NIP-compliant magic link authentication for Nostr applications. Features encrypted DMs (NIP-04), multi-relay support, and i18n with RTL languages. Built with TypeScript for type-safe, passwordless authentication flows.

License

Notifications You must be signed in to change notification settings

HumanjavaEnterprises/nostr-dm-magiclink-utils

nostr-dm-magiclink-utils

npm version License: MIT TypeScript Node.js Version Test Coverage Build Status Dependencies Code Style: Prettier Author Bundle Size Downloads Languages Security

A comprehensive Nostr utility library for implementing secure, user-friendly authentication via magic links in direct messages. Built with TypeScript and following Nostr Improvement Proposals (NIPs) for maximum compatibility and security.

Features

  • ๐Ÿ” NIP-04 Compliant: Secure, encrypted direct messages following Nostr standards
  • ๐ŸŒ Rich i18n Support: 9 languages with RTL support
  • ๐Ÿ”„ Multi-Relay Support: Reliable message delivery with automatic failover
  • ๐Ÿ›ก๏ธ Type-Safe: Full TypeScript support with comprehensive types
  • ๐Ÿ“ Flexible Templates: Customizable messages with variable interpolation
  • ๐Ÿš€ Modern API: Promise-based, async/await friendly interface
  • ๐ŸŽฏ Zero Config: Sensible defaults with optional deep customization

Installation

npm install nostr-dm-magiclink-utils

Quick Start

Here's a complete example showing how to set up and use the magic link service:

import { createNostrMagicLink, NostrError } from 'nostr-dm-magiclink-utils'; import { generatePrivateKey } from 'nostr-tools'; // For demo purposes async function setupAuthService() { // Create manager with secure configuration const magicLink = createNostrMagicLink({ nostr: { // In production, load from secure environment variable privateKey: process.env.NOSTR_PRIVATE_KEY || generatePrivateKey(), relayUrls: [ 'wss://relay.damus.io', 'wss://relay.nostr.band', 'wss://nos.lol' ], // Optional: Configure connection timeouts connectionTimeout: 5000 }, magicLink: { verifyUrl: 'https://your-app.com/verify', // Async token generation with expiry token: async () => { const token = await generateSecureToken({ expiresIn: '15m', length: 32 }); return token; }, defaultLocale: 'en', // Optional: Custom message templates templates: { en: { subject: 'Login to {{appName}}', body: 'Click this secure link to log in: {{link}}\nValid for 15 minutes.' } } } }); return magicLink; } // Example usage in an Express route handler app.post('/auth/magic-link', async (req, res) => { try { const { pubkey } = req.body; if (!pubkey) { return res.status(400).json({ error: 'Missing pubkey' }); } const magicLink = await setupAuthService(); const result = await magicLink.sendMagicLink({ recipientPubkey: pubkey, messageOptions: { locale: req.locale, // From i18n middleware variables: { appName: 'YourApp', username: req.body.username } } }); if (result.success) { res.json({ message: 'Magic link sent successfully', expiresIn: '15 minutes' }); } } catch (error) { if (error instanceof NostrError) { // Handle specific Nostr-related errors res.status(400).json({ error: error.message, code: error.code }); } else { // Handle unexpected errors res.status(500).json({ error: 'Failed to send magic link' }); } } });

Advanced Usage

Custom Error Handling

try { const result = await magicLink.sendMagicLink({ recipientPubkey: pubkey, messageOptions: { locale: 'en' } }); if (!result.success) { switch (result.error.code) { case 'RELAY_CONNECTION_FAILED': // Attempt reconnection or use fallback relay await magicLink.reconnect(); break; case 'ENCRYPTION_FAILED': // Log encryption errors for debugging logger.error('Encryption failed:', result.error); break; case 'INVALID_PUBKEY': // Handle invalid recipient public key throw new UserError('Invalid recipient'); break; } } } catch (error) { // Handle other errors }

Multi-Language Support

// Arabic (RTL) example const result = await magicLink.sendMagicLink({ recipientPubkey: pubkey, messageOptions: { locale: 'ar', // Optional: Override default template template: { subject: 'ุชุณุฌูŠู„ ุงู„ุฏุฎูˆู„ ุฅู„ู‰ {{appName}}', body: 'ุงู†ู‚ุฑ ููˆู‚ ู‡ุฐุง ุงู„ุฑุงุจุท ุงู„ุขู…ู† ู„ุชุณุฌูŠู„ ุงู„ุฏุฎูˆู„: {{link}}' }, variables: { appName: 'ุชุทุจูŠู‚ูƒ', username: 'ุงู„ู…ุณุชุฎุฏู…' } } });

Custom Token Generation

const magicLink = createNostrMagicLink({ // ... other config magicLink: { verifyUrl: 'https://your-app.com/verify', token: async (recipientPubkey: string) => { // Generate a secure, short-lived token const token = await generateJWT({ sub: recipientPubkey, exp: Math.floor(Date.now() / 1000) + (15 * 60), // 15 minutes jti: crypto.randomUUID(), iss: 'your-app' }); // Optional: Store token in database for verification await db.tokens.create({ token, pubkey: recipientPubkey, expiresAt: new Date(Date.now() + 15 * 60 * 1000) }); return token; } } });

Relay Management

const magicLink = createNostrMagicLink({ nostr: { privateKey: process.env.NOSTR_PRIVATE_KEY, relayUrls: ['wss://relay1.com', 'wss://relay2.com'], // Advanced relay options relayOptions: { retryAttempts: 3, retryDelay: 1000, timeout: 5000, onError: async (error, relay) => { logger.error(`Relay ${relay} error:`, error); // Optionally switch to backup relay await magicLink.addRelay('wss://backup-relay.com'); } } } }); // Monitor relay status magicLink.on('relay:connected', (relay) => { logger.info(`Connected to relay: ${relay}`); }); magicLink.on('relay:disconnected', (relay) => { logger.warn(`Disconnected from relay: ${relay}`); });

Security Best Practices

  1. Private Key Management
    • Never hardcode private keys
    • Use secure environment variables
    • Rotate keys periodically
// Load private key securely const privateKey = await loadPrivateKeyFromSecureStore(); if (!privateKey) { throw new Error('Missing required private key'); }
  1. Token Security

    • Use short expiration times (15-30 minutes)
    • Include necessary claims (sub, exp, jti)
    • Store tokens securely for verification
  2. Error Handling

    • Never expose internal errors to users
    • Log errors securely
    • Implement rate limiting
  3. Relay Security

    • Use trusted relays
    • Implement connection timeouts
    • Handle connection errors gracefully

Supported Languages

The library includes built-in support for:

  • ๐Ÿ‡บ๐Ÿ‡ธ English (en)
  • ๐Ÿ‡ช๐Ÿ‡ธ Spanish (es)
  • ๐Ÿ‡ซ๐Ÿ‡ท French (fr)
  • ๐Ÿ‡ฏ๐Ÿ‡ต Japanese (ja)
  • ๐Ÿ‡ฐ๐Ÿ‡ท Korean (ko)
  • ๐Ÿ‡จ๐Ÿ‡ณ Chinese (zh)
  • ๐Ÿ‡ง๐Ÿ‡ท Portuguese (pt)
  • ๐Ÿ‡ท๐Ÿ‡บ Russian (ru)
  • ๐Ÿ‡ธ๐Ÿ‡ฆ Arabic (ar) - with RTL support

Contributing

See CONTRIBUTING.md for development setup and guidelines.

License

MIT ยฉ vveerrgg

About

๐Ÿ” Secure, NIP-compliant magic link authentication for Nostr applications. Features encrypted DMs (NIP-04), multi-relay support, and i18n with RTL languages. Built with TypeScript for type-safe, passwordless authentication flows.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published