OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social / hyper-social-web / lib / auth.js

88 lines · javascript ref:
/**
 * Auth adapter for hyper-social-web
 *
 * Wraps hyper-identity's AuthManager with social-specific permission checking.
 * Uses Autobase-backed IdentityRegistry for multi-writer identity consensus.
 */

import { AuthManager, IdentityRegistry, ANProto } from 'hyper-identity'
import Hyperbee from 'hyperbee'

/**
 * Initialize the auth system
 *
 * @param {Corestore} store - Corestore instance (shared with feed/accounts storage)
 * @returns {{ authManager: AuthManager, identityRegistry: IdentityRegistry }}
 */
export async function createAuth (store) {
  // Separate Hyperbee for identity direct-write storage
  const identityBee = new Hyperbee(store.get({ name: 'identity-db' }), {
    keyEncoding: 'utf-8',
    valueEncoding: 'json'
  })
  await identityBee.ready()

  const registry = new IdentityRegistry(store, { db: identityBee })
  await registry.ready()

  const authManager = new AuthManager({
    identityRegistry: registry,
    db: identityBee,
    enableSSH: false,
    autoRegister: false, // Require explicit registration — no auto-create on auth
    checkPermission: checkSocialPermission,
    rateLimits: {
      auth: { tokens: 100, refillRate: 20, refillInterval: 60 * 1000 },
      api: { tokens: 1000, refillRate: 200, refillInterval: 60 * 1000 },
      'identity-generate': { tokens: 5, refillRate: 1, refillInterval: 60 * 60 * 1000 }
    }
  })

  return { authManager, identityRegistry: registry, identityBee }
}

/**
 * Social permission checker — injectable into AuthManager
 *
 * Resources in hyper-social: 'posts', 'accounts', 'settings', '*'
 */
function checkSocialPermission (auth, action, resourceName) {
  if (!auth || !auth.verified) return false
  const perms = auth.permissions
  if (!perms) return false
  if (perms.admin) return true
  if (!perms[action]) return false
  if (resourceName) {
    return perms.resources && (perms.resources.includes('*') || perms.resources.includes(resourceName))
  }
  return true
}

/**
 * Extract auth from request (non-throwing)
 * Returns auth object if authenticated, null if anonymous.
 */
export async function extractAuth (req, authManager) {
  try {
    const result = await authManager.authenticate(req)
    return result || null
  } catch {
    return null
  }
}

/**
 * Require auth — throws if not authenticated
 */
export async function requireAuth (req, authManager) {
  const auth = await extractAuth(req, authManager)
  if (!auth) {
    const err = new Error('Authentication required')
    err.status = 401
    throw err
  }
  return auth
}

export { ANProto }