summaryrefslogtreecommitdiffstats
path: root/internal/auth/validation.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-14 12:03:21 -0800
committerbndw <ben@bdw.to>2026-02-14 12:03:21 -0800
commit4fc493e6d8cc20137f920f8647e39fc5051bb245 (patch)
tree69055d7da89ca909e33c66de7a883fdbe2ccbb97 /internal/auth/validation.go
parent3e0ddc90c8f4ae7658cc07fb183aa0a7ecc338b7 (diff)
refactor: remove frivolous comments from auth validation/credentials
Also removed internal/nostr package - now using northwest.io/nostr library.
Diffstat (limited to 'internal/auth/validation.go')
-rw-r--r--internal/auth/validation.go33
1 files changed, 5 insertions, 28 deletions
diff --git a/internal/auth/validation.go b/internal/auth/validation.go
index 11435ee..8b9d8a1 100644
--- a/internal/auth/validation.go
+++ b/internal/auth/validation.go
@@ -7,52 +7,37 @@ import (
7 "strings" 7 "strings"
8 "time" 8 "time"
9 9
10 "northwest.io/muxstr/internal/nostr" 10 "northwest.io/nostr"
11) 11)
12 12
13// ValidationOptions configures how NIP-98 events are validated.
14type ValidationOptions struct { 13type ValidationOptions struct {
15 // TimestampWindow is the maximum age of events in seconds
16 TimestampWindow int64 14 TimestampWindow int64
17
18 // ValidatePayload checks the payload hash if present
19 ValidatePayload bool 15 ValidatePayload bool
20 16 ExpectedURI string
21 // ExpectedURI is the URI that should match the 'u' tag 17 ExpectedMethod string
22 ExpectedURI string 18 PayloadHash string
23
24 // ExpectedMethod is the method that should match the 'method' tag
25 ExpectedMethod string
26
27 // PayloadHash is the expected payload hash (if ValidatePayload is true)
28 PayloadHash string
29} 19}
30 20
31// ParseAuthHeader extracts and decodes a NIP-98 event from an Authorization header. 21// ParseAuthHeader parses "Nostr <base64-encoded-event-json>" format.
32// Expected format: "Nostr <base64-encoded-event-json>"
33func ParseAuthHeader(header string) (*nostr.Event, error) { 22func ParseAuthHeader(header string) (*nostr.Event, error) {
34 if header == "" { 23 if header == "" {
35 return nil, fmt.Errorf("empty authorization header") 24 return nil, fmt.Errorf("empty authorization header")
36 } 25 }
37 26
38 // Check for "Nostr " prefix
39 if !strings.HasPrefix(header, "Nostr ") { 27 if !strings.HasPrefix(header, "Nostr ") {
40 return nil, fmt.Errorf("invalid authorization header: must start with 'Nostr '") 28 return nil, fmt.Errorf("invalid authorization header: must start with 'Nostr '")
41 } 29 }
42 30
43 // Extract base64 part
44 encoded := strings.TrimPrefix(header, "Nostr ") 31 encoded := strings.TrimPrefix(header, "Nostr ")
45 if encoded == "" { 32 if encoded == "" {
46 return nil, fmt.Errorf("empty authorization token") 33 return nil, fmt.Errorf("empty authorization token")
47 } 34 }
48 35
49 // Decode base64
50 decoded, err := base64.StdEncoding.DecodeString(encoded) 36 decoded, err := base64.StdEncoding.DecodeString(encoded)
51 if err != nil { 37 if err != nil {
52 return nil, fmt.Errorf("invalid base64 encoding: %w", err) 38 return nil, fmt.Errorf("invalid base64 encoding: %w", err)
53 } 39 }
54 40
55 // Unmarshal event
56 var event nostr.Event 41 var event nostr.Event
57 if err := json.Unmarshal(decoded, &event); err != nil { 42 if err := json.Unmarshal(decoded, &event); err != nil {
58 return nil, fmt.Errorf("invalid event JSON: %w", err) 43 return nil, fmt.Errorf("invalid event JSON: %w", err)
@@ -61,19 +46,15 @@ func ParseAuthHeader(header string) (*nostr.Event, error) {
61 return &event, nil 46 return &event, nil
62} 47}
63 48
64// ValidateAuthEvent validates a NIP-98 auth event according to the spec.
65func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error { 49func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error {
66 // Check event kind
67 if event.Kind != 27235 { 50 if event.Kind != 27235 {
68 return fmt.Errorf("invalid event kind: expected 27235, got %d", event.Kind) 51 return fmt.Errorf("invalid event kind: expected 27235, got %d", event.Kind)
69 } 52 }
70 53
71 // Verify signature
72 if !event.Verify() { 54 if !event.Verify() {
73 return fmt.Errorf("invalid event signature") 55 return fmt.Errorf("invalid event signature")
74 } 56 }
75 57
76 // Check timestamp (prevent replay attacks)
77 now := time.Now().Unix() 58 now := time.Now().Unix()
78 age := now - event.CreatedAt 59 age := now - event.CreatedAt
79 60
@@ -85,7 +66,6 @@ func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error {
85 return fmt.Errorf("event too old: %d seconds (max %d)", age, opts.TimestampWindow) 66 return fmt.Errorf("event too old: %d seconds (max %d)", age, opts.TimestampWindow)
86 } 67 }
87 68
88 // Validate 'u' tag (URL)
89 if opts.ExpectedURI != "" { 69 if opts.ExpectedURI != "" {
90 uTag := event.Tags.Find("u") 70 uTag := event.Tags.Find("u")
91 if uTag == nil { 71 if uTag == nil {
@@ -98,7 +78,6 @@ func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error {
98 } 78 }
99 } 79 }
100 80
101 // Validate 'method' tag
102 if opts.ExpectedMethod != "" { 81 if opts.ExpectedMethod != "" {
103 methodTag := event.Tags.Find("method") 82 methodTag := event.Tags.Find("method")
104 if methodTag == nil { 83 if methodTag == nil {
@@ -111,7 +90,6 @@ func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error {
111 } 90 }
112 } 91 }
113 92
114 // Validate payload hash if requested
115 if opts.ValidatePayload && opts.PayloadHash != "" { 93 if opts.ValidatePayload && opts.PayloadHash != "" {
116 payloadTag := event.Tags.Find("payload") 94 payloadTag := event.Tags.Find("payload")
117 if payloadTag == nil { 95 if payloadTag == nil {
@@ -127,7 +105,6 @@ func ValidateAuthEvent(event *nostr.Event, opts ValidationOptions) error {
127 return nil 105 return nil
128} 106}
129 107
130// ExtractPubkey returns the pubkey from a validated auth event.
131func ExtractPubkey(event *nostr.Event) string { 108func ExtractPubkey(event *nostr.Event) string {
132 return event.PubKey 109 return event.PubKey
133} 110}