Skip to content

Commit e7b561e

Browse files
tcheericclaude
andcommitted
fix: enforce auth requirement for REQ messages when require-auth=true
- REQ messages now properly rejected before authentication when require-auth=true (previously only protected kinds were checked) - Updated unit test to explicitly set requireAuth=false for the "allow REQ without auth" test case - Added "Message Authentication Requirements" section to README documenting which messages require auth and protected kinds - Updated pom.xml: version 0.1.0, distribution management, repositories 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent db6d837 commit e7b561e

4 files changed

Lines changed: 54 additions & 9 deletions

File tree

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,33 @@ mvn spring-boot:run
8383
5. On success: `["OK", "<event-id>", true, "authenticated as <pubkey>..."]`
8484
6. On failure: `["OK", "<event-id>", false, "<error>"]` and connection closes
8585

86+
## Message Authentication Requirements
87+
88+
The proxy enforces authentication based on message type and the `require-auth` setting:
89+
90+
| Message Type | `require-auth=true` | `require-auth=false` |
91+
|--------------|---------------------|----------------------|
92+
| **AUTH** | Allowed (for authentication) | Allowed |
93+
| **CLOSE** | Allowed | Allowed |
94+
| **REQ** | Requires auth | Allowed, unless querying protected kinds |
95+
| **EVENT** | Requires auth | Requires auth |
96+
| **Other** | Requires auth | Requires auth |
97+
98+
### Protected Kinds
99+
100+
When `require-auth=false`, REQ messages are still rejected if they query privacy-sensitive event kinds:
101+
102+
- **NIP-04**: kind `4` (Legacy encrypted DMs)
103+
- **NIP-17**: kinds `14`, `15` (Private direct messages)
104+
- **NIP-29**: kind `39002` (Group members)
105+
- **NIP-37**: kinds `10013`, `31234` (Draft events)
106+
- **NIP-46**: kind `24133` (Nostr Remote Signing/Bunker)
107+
- **NIP-47**: kinds `23194`, `23195`, `23196`, `23197` (Wallet Connect)
108+
- **NIP-51**: kinds `10000`, `10050` (Mute list, DM relay preferences)
109+
- **NIP-59**: kinds `13`, `1059` (Gift Wraps/Seals)
110+
- **NIP-60**: kinds `7374`, `7375`, `7376`, `17375` (Cashu Wallet)
111+
- **NIP-61**: kinds `9321`, `10019` (Nutzaps)
112+
86113
## Access Control
87114

88115
### OPEN Mode (default)

pom.xml

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<groupId>xyz.tcheeric</groupId>
1616
<artifactId>nostr-auth-proxy</artifactId>
17-
<version>1.0.0</version>
17+
<version>0.1.0</version>
1818
<packaging>jar</packaging>
1919

2020
<name>Nostr Auth Proxy</name>
@@ -28,17 +28,31 @@
2828
<awaitility.version>4.3.0</awaitility.version>
2929
</properties>
3030

31+
<distributionManagement>
32+
<repository>
33+
<id>reposilite-releases</id>
34+
<url>https://maven.398ja.xyz/releases</url>
35+
</repository>
36+
<snapshotRepository>
37+
<id>reposilite-snapshots</id>
38+
<url>https://maven.398ja.xyz/snapshots</url>
39+
</snapshotRepository>
40+
</distributionManagement>
41+
3142
<repositories>
3243
<repository>
33-
<id>398ja-releases</id>
44+
<id>central</id>
45+
<url>https://repo.maven.apache.org/maven2</url>
46+
<releases><enabled>true</enabled></releases>
47+
<snapshots><enabled>false</enabled></snapshots>
48+
</repository>
49+
<repository>
50+
<id>reposilite-releases</id>
3451
<url>https://maven.398ja.xyz/releases</url>
3552
</repository>
3653
<repository>
37-
<id>398ja-snapshots</id>
54+
<id>reposilite-snapshots</id>
3855
<url>https://maven.398ja.xyz/snapshots</url>
39-
<snapshots>
40-
<enabled>true</enabled>
41-
</snapshots>
4256
</repository>
4357
</repositories>
4458

src/main/java/xyz/tcheeric/nostr/authproxy/handler/AuthProxyWebSocketHandler.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
204204
ensureUpstreamConnected(proxySession);
205205
proxyToUpstream(proxySession, message);
206206
} else if ("REQ".equals(messageType)) {
207-
// REQ requires auth if querying protected kinds
208-
if (reqContainsProtectedKinds(payload)) {
207+
// REQ requires auth if require-auth is enabled or if querying protected kinds
208+
if (properties.isRequireAuth()) {
209+
sendAuthError(proxySession, "auth-required",
210+
"AUTH required for REQ messages");
211+
} else if (reqContainsProtectedKinds(payload)) {
209212
sendAuthError(proxySession, "auth-required",
210213
"Authentication required to query protected kinds (DMs, wallet data)");
211214
} else {

src/test/java/xyz/tcheeric/nostr/authproxy/handler/AuthProxyWebSocketHandlerTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,12 @@ void shouldRejectEventMessageBeforeAuth() throws Exception {
232232
}
233233

234234
/**
235-
* Tests that REQ (read) messages are allowed without authentication.
235+
* Tests that REQ (read) messages are allowed without authentication when require-auth is disabled.
236236
*/
237237
@Test
238238
void shouldAllowReqMessageWithoutAuth() throws Exception {
239239
// Given
240+
properties.setRequireAuth(false);
240241
Nip42Challenge challenge = new Nip42Challenge(TEST_CHALLENGE, Instant.now().plusSeconds(300));
241242
ProxySession proxySession = new ProxySession(SESSION_ID, clientSession, challenge);
242243
WebSocketSession upstreamSession = mock(WebSocketSession.class);

0 commit comments

Comments
 (0)