Files
c-relay/docs/NIP-42_Authentication.md
2025-09-13 08:49:09 -04:00

295 lines
8.5 KiB
Markdown

# NIP-42 Authentication Implementation
## Overview
This relay implements NIP-42 (Authentication of clients to relays) providing granular authentication controls for event submission and subscription operations. The implementation supports both challenge-response authentication and per-connection state management.
## Architecture
### Core Components
1. **Per-Session Authentication State** (`struct per_session_data`)
- `authenticated`: Boolean flag indicating authentication status
- `authenticated_pubkey[65]`: Hex-encoded public key of authenticated user
- `active_challenge[65]`: Current authentication challenge
- `challenge_created`: Timestamp when challenge was generated
- `challenge_expires`: Challenge expiration timestamp
- `nip42_auth_required_events`: Whether auth is required for EVENT submission
- `nip42_auth_required_subscriptions`: Whether auth is required for REQ operations
- `auth_challenge_sent`: Flag indicating if challenge has been sent
2. **Challenge Management** (via `request_validator.c`)
- `nostr_nip42_generate_challenge()`: Generates cryptographically secure challenges
- `nostr_nip42_verify_auth_event()`: Validates signed authentication events
- Challenge storage and cleanup with expiration handling
3. **WebSocket Protocol Integration**
- AUTH message handling in `nostr_relay_callback()`
- Challenge generation and transmission
- Authentication verification and session state updates
## Configuration Options
### Event-Based Configuration
NIP-42 authentication is configured using kind 33334 configuration events with the following tags:
| Tag | Description | Default | Values |
|-----|-------------|---------|--------|
| `nip42_auth_required_events` | Require auth for EVENT submission | `false` | `true`/`false` |
| `nip42_auth_required_subscriptions` | Require auth for REQ operations | `false` | `true`/`false` |
### Example Configuration Event
```json
{
"kind": 33334,
"content": "C Nostr Relay Configuration",
"tags": [
["d", "<relay_pubkey>"],
["nip42_auth_required_events", "true"],
["nip42_auth_required_subscriptions", "false"],
["relay_description", "Authenticated Nostr Relay"]
],
"created_at": 1640995200,
"pubkey": "<admin_pubkey>",
"id": "<event_id>",
"sig": "<signature>"
}
```
## Authentication Flow
### 1. Challenge Generation
When authentication is required and client is not authenticated:
```
Client -> Relay: ["EVENT", <event>] (unauthenticated)
Relay -> Client: ["AUTH", <challenge>]
```
The challenge is a 64-character hex string generated using cryptographically secure random numbers.
### 2. Authentication Response
Client creates and signs an authentication event (kind 22242):
```json
{
"kind": 22242,
"content": "",
"tags": [
["relay", "ws://relay.example.com"],
["challenge", "<challenge_from_relay>"]
],
"created_at": <current_timestamp>,
"pubkey": "<client_pubkey>",
"id": "<event_id>",
"sig": "<signature>"
}
```
Client sends this event back to relay:
```
Client -> Relay: ["AUTH", <signed_auth_event>]
```
### 3. Verification and Session Update
The relay:
1. Validates the authentication event signature
2. Verifies the challenge matches the one sent
3. Checks challenge expiration (default: 10 minutes)
4. Updates session state with authenticated public key
5. Sends confirmation notice
```
Relay -> Client: ["NOTICE", "NIP-42 authentication successful"]
```
## Granular Authentication Controls
### Separate Controls for Events vs Subscriptions
The implementation provides separate authentication requirements:
- **Event Submission**: Control whether clients must authenticate to publish events
- **Subscription Access**: Control whether clients must authenticate to create subscriptions
This allows flexible relay policies:
- **Public Read, Authenticated Write**: `events=true, subscriptions=false`
- **Fully Authenticated**: `events=true, subscriptions=true`
- **Public Access**: `events=false, subscriptions=false` (default)
- **Authenticated Read Only**: `events=false, subscriptions=true`
### Per-Connection State
Each WebSocket connection maintains its own authentication state:
- Authentication persists for the lifetime of the connection
- Challenges expire after 10 minutes
- Session cleanup on connection close
## Security Features
### Challenge Security
- 64-character hexadecimal challenges (256 bits of entropy)
- Cryptographically secure random generation
- Challenge expiration to prevent replay attacks
- One-time use challenges
### Event Validation
- Complete signature verification using secp256k1
- Event ID validation
- Challenge-response binding verification
- Timestamp validation with configurable tolerance
### Session Management
- Thread-safe per-session state management
- Automatic cleanup on disconnection
- Challenge expiration handling
## Client Integration
### Using nak Client
```bash
# Generate keypair
PRIVKEY=$(nak key --gen)
PUBKEY=$(nak key --pub $PRIVKEY)
# Connect and authenticate automatically
nak event -k 1 --content "Authenticated message" --sec $PRIVKEY --relay ws://localhost:8888
# nak handles NIP-42 authentication automatically when required
```
### Manual WebSocket Integration
```javascript
const ws = new WebSocket('ws://localhost:8888');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message[0] === 'AUTH') {
const challenge = message[1];
// Create auth event (kind 22242)
const authEvent = {
kind: 22242,
content: "",
tags: [
["relay", "ws://localhost:8888"],
["challenge", challenge]
],
created_at: Math.floor(Date.now() / 1000),
pubkey: clientPubkey,
// ... calculate id and signature
};
// Send auth response
ws.send(JSON.stringify(["AUTH", authEvent]));
}
};
// Send event (may trigger AUTH challenge)
ws.send(JSON.stringify(["EVENT", myEvent]));
```
## Administration
### Enabling Authentication
1. **Get Admin Private Key**: Extract from relay startup logs (shown once)
2. **Create Configuration Event**: Use nak or custom tooling
3. **Publish Configuration**: Send to relay with admin signature
```bash
# Enable auth for events only
nak event -k 33334 \
--content "C Nostr Relay Configuration" \
--tag "d=$RELAY_PUBKEY" \
--tag "nip42_auth_required_events=true" \
--tag "nip42_auth_required_subscriptions=false" \
--sec $ADMIN_PRIVKEY \
--relay ws://localhost:8888
```
### Monitoring Authentication
- Check relay logs for authentication events
- Monitor `NOTICE` messages for auth status
- Use `get_settings.sh` script to view current configuration
```bash
./get_settings.sh
```
## Troubleshooting
### Common Issues
1. **Challenge Expiration**
- Default: 10 minutes
- Client must respond within expiration window
- Generate new challenge for expired attempts
2. **Signature Verification Failures**
- Verify event structure matches NIP-42 specification
- Check challenge value matches exactly
- Ensure proper secp256k1 signature generation
3. **Configuration Not Applied**
- Verify admin private key is correct
- Check configuration event signature
- Ensure relay pubkey in 'd' tag matches relay
### Debug Commands
```bash
# Check supported NIPs
curl -H "Accept: application/nostr+json" http://localhost:8888 | jq .supported_nips
# View current configuration
nak req -k 33334 ws://localhost:8888 | jq .
# Test authentication flow
./tests/42_nip_test.sh
```
## Performance Considerations
- Challenge generation: ~1ms overhead per unauthenticated connection
- Authentication verification: ~2-5ms per auth event
- Memory overhead: ~200 bytes per connection for auth state
- Database impact: Configuration events cached, minimal query overhead
## Integration with Other NIPs
### NIP-01 (Basic Protocol)
- AUTH messages integrated into standard WebSocket flow
- Compatible with existing EVENT/REQ/CLOSE message handling
### NIP-11 (Relay Information)
- NIP-42 advertised in `supported_nips` array
- Authentication requirements reflected in relay metadata
### NIP-20 (Command Results)
- OK responses include authentication-related error messages
- NOTICE messages provide authentication status updates
## Future Extensions
### Potential Enhancements
- Role-based authentication (admin, user, read-only)
- Time-based access controls
- Rate limiting based on authentication status
- Integration with external authentication providers
### Configuration Extensions
- Per-kind authentication requirements
- Whitelist/blacklist integration
- Custom challenge expiration times
- Authentication logging and metrics