feat(basic auth): Allowing basic authentication for API access

This commit is contained in:
Chris Troutner
2025-11-23 09:58:08 -08:00
parent a23c4d9328
commit ca666f7c56
5 changed files with 125 additions and 12 deletions
+16 -4
View File
@@ -1,13 +1,25 @@
# START INFRASTRUCTURE SETUP
# Full Node Connection
RPC_BASEURL=http://172.17.0.1:8332
RPC_USERNAME=bitcoin
RPC_PASSWORD=password
# x402 payments required to access this API?
X402_ENABLED=false
# Fulcrum Indexer
FULCRUM_API=http://192.168.2.127:3001
# SLP Indexer
SLP_INDEXER_API=http://192.168.2.127:5010
SLP_INDEXER_API=http://192.168.2.127:5010
# END INFRASTRUCTURE SETUP
# START ACCESS CONTROL
# x402 payments required to access this API?
X402_ENABLED=false
# Basic Authentication required to access this API?
USE_BASIC_AUTH=false
# END ACCESS CONTROL
+34 -8
View File
@@ -15,7 +15,8 @@ import { dirname, join } from 'path'
import config from '../src/config/index.js'
import Controllers from '../src/controllers/index.js'
import wlogger from '../src/adapters/wlogger.js'
import { buildX402Routes, getX402Settings } from '../src/config/x402.js'
import { buildX402Routes, getX402Settings, getBasicAuthSettings } from '../src/config/x402.js'
import { basicAuthMiddleware } from '../src/middleware/basic-auth.js'
// Load environment variables
dotenv.config()
@@ -60,6 +61,7 @@ class Server {
const app = express()
const x402Settings = getX402Settings()
const basicAuthSettings = getBasicAuthSettings()
// MIDDLEWARE START
app.use(express.json())
@@ -72,22 +74,46 @@ class Server {
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}))
// Wrap all endpoints in x402 middleware. This handles payments for the API calls.
if (x402Settings.enabled) {
// Apply basic auth middleware if enabled
// This must run before x402 middleware to set req.locals.basicAuthValid
if (basicAuthSettings.enabled) {
wlogger.info('Basic auth middleware enabled')
app.use(basicAuthMiddleware)
}
// Apply x402 middleware based on configuration
// Logic:
// - If X402_ENABLED=false OR USE_BASIC_AUTH=false: Don't apply x402 (no rate limits)
// - If X402_ENABLED=true AND USE_BASIC_AUTH=true: Apply x402 conditionally (bypass if basic auth valid)
// Only apply x402 if both are enabled
if (x402Settings.enabled && basicAuthSettings.enabled) {
// X402_ENABLED=true AND USE_BASIC_AUTH=true: Apply x402 conditionally
const routes = buildX402Routes(this.config.apiPrefix)
const facilitatorOptions = x402Settings.facilitatorUrl
? { url: x402Settings.facilitatorUrl }
: undefined
wlogger.info(`x402 middleware enabled; enforcing ${x402Settings.priceSat} satoshis per request`)
app.use(
x402PaymentMiddleware(
wlogger.info(`x402 middleware enabled with basic auth bypass; enforcing ${x402Settings.priceSat} satoshis per request (unless basic auth provided)`)
// Create conditional x402 middleware that bypasses if basic auth is valid
const conditionalX402Middleware = (req, res, next) => {
// If basic auth is valid, bypass x402
if (req.locals?.basicAuthValid === true) {
return next()
}
// Otherwise, apply x402 middleware
return x402PaymentMiddleware(
x402Settings.serverAddress,
routes,
facilitatorOptions
)
)
)(req, res, next)
}
app.use(conditionalX402Middleware)
} else {
// X402_ENABLED=false OR USE_BASIC_AUTH=false: No x402 middleware
wlogger.info('x402 middleware disabled via configuration')
}
+7
View File
@@ -38,6 +38,11 @@ const x402Defaults = {
priceSat
}
const basicAuthDefaults = {
enabled: normalizeBoolean(process.env.USE_BASIC_AUTH, false),
token: process.env.BASIC_AUTH_TOKEN || ''
}
export default {
// Server port
port: process.env.PORT || 5942,
@@ -80,6 +85,8 @@ export default {
x402: x402Defaults,
basicAuth: basicAuthDefaults,
// Version
version
}
+7
View File
@@ -41,3 +41,10 @@ export function getX402Settings () {
priceSat: config.x402?.priceSat
}
}
export function getBasicAuthSettings () {
return {
enabled: Boolean(config.basicAuth?.enabled),
token: config.basicAuth?.token || ''
}
}
+61
View File
@@ -0,0 +1,61 @@
/*
Basic Authentication Middleware
This middleware validates Bearer tokens from the Authorization header.
When a valid token is provided, it sets req.locals.basicAuthValid = true
to allow bypassing x402 middleware.
*/
import config from '../config/index.js'
import wlogger from '../adapters/wlogger.js'
/**
* Middleware function that validates Bearer token authentication
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @param {Function} next - Express next middleware function
*/
export function basicAuthMiddleware (req, res, next) {
// Initialize req.locals if it doesn't exist
if (!req.locals) {
req.locals = {}
}
// Default to false
req.locals.basicAuthValid = false
// Get the configured token
const configuredToken = config.basicAuth?.token
// If no token is configured, skip validation
if (!configuredToken) {
wlogger.warn('Basic auth enabled but no BASIC_AUTH_TOKEN configured')
return next()
}
// Get the Authorization header
const authHeader = req.headers.authorization
// If no Authorization header, continue (x402 will handle unauthorized requests)
if (!authHeader) {
return next()
}
// Check if it's a Bearer token
const parts = authHeader.split(' ')
if (parts.length !== 2 || parts[0] !== 'Bearer') {
return next()
}
const providedToken = parts[1]
// Compare tokens
if (providedToken === configuredToken) {
req.locals.basicAuthValid = true
wlogger.verbose(`Basic auth validated for request to ${req.path}`)
}
// Always continue to next middleware
// If auth failed, x402 middleware will handle the request
next()
}