Skip to content

Commit b715e65

Browse files
committed
feat: Add session verification and profile caching
- Add session verification for browser and server environments - Implement profile caching with automatic invalidation - Add development mode logging for debugging - Update documentation with session management details - Add COLAB-DOCS for session management - Bump version to 0.3.4
1 parent c895f39 commit b715e65

7 files changed

Lines changed: 279 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.3.4] - 2025-02-09
9+
10+
### Added
11+
- Session verification support for browser environments
12+
- TypeScript definitions for window.nostr interface
13+
- Improved error handling for session verification
14+
15+
### Changed
16+
- Enhanced browser compatibility checks
17+
- Better error messages for session-related operations
18+
819
## [0.2.6] - 2023-12-05
920

1021
### Added

COLAB-DOCS/SESSION-MANAGEMENT.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Session Management in Nostr Auth Middleware
2+
3+
This document outlines the session management capabilities of the Nostr Auth Middleware, including verification, caching, and development tools.
4+
5+
## Overview
6+
7+
Session management in Nostr applications requires careful handling of user authentication state and profile information. The middleware provides tools for:
8+
- Session verification
9+
- Profile caching
10+
- Development logging
11+
- Graceful error handling
12+
13+
## Session Verification
14+
15+
### Browser Environment
16+
17+
In browser environments, session verification works by checking if:
18+
1. The Nostr extension is still available
19+
2. The user's public key matches the stored session
20+
3. The connection is active and valid
21+
22+
```javascript
23+
const auth = new NostrAuthMiddleware();
24+
25+
// Verify session
26+
const isValid = await auth.verifySession(userPubkey);
27+
if (!isValid) {
28+
// Handle invalid session
29+
await auth.logout();
30+
redirectToLogin();
31+
}
32+
```
33+
34+
### Server Environment
35+
36+
In server environments, session verification:
37+
1. Validates the pubkey format
38+
2. Checks token expiration
39+
3. Verifies signature if applicable
40+
41+
```javascript
42+
const auth = new NostrAuthMiddleware();
43+
44+
app.use(async (req, res, next) => {
45+
const pubkey = req.session.pubkey;
46+
if (!(await auth.verifySession(pubkey))) {
47+
return res.status(401).json({ error: 'Invalid session' });
48+
}
49+
next();
50+
});
51+
```
52+
53+
## Profile Caching
54+
55+
The middleware includes profile caching to improve performance:
56+
57+
### Cache Operations
58+
```javascript
59+
// Fetch profile (uses cache if available)
60+
const profile = await auth.fetchNostrProfile(pubkey);
61+
62+
// Manually clear cache
63+
auth.clearProfileCache(pubkey);
64+
```
65+
66+
### Cache Configuration
67+
- Default cache duration: 1 hour
68+
- Automatic cache invalidation
69+
- Cache clearing on logout
70+
- Error handling for failed cache operations
71+
72+
## Development Mode
73+
74+
When `import.meta.env.MODE === 'development'`, the middleware provides detailed logging:
75+
76+
### Profile Operations
77+
```javascript
78+
🔵 Cached Profile: {
79+
name: "user123",
80+
about: "Nostr enthusiast",
81+
picture: "https://..."
82+
}
83+
84+
🟢 Fresh Profile: {
85+
profile: { /* profile data */ },
86+
event: { /* raw nostr event */ }
87+
}
88+
```
89+
90+
### Cache Operations
91+
```javascript
92+
🔵 Profile Cache Hit: {
93+
pubkey: "npub...",
94+
cacheAge: "30 minutes"
95+
}
96+
97+
🔴 Profile Cache Expired: {
98+
pubkey: "npub...",
99+
cacheAge: "65 minutes"
100+
}
101+
102+
💾 Profile Cached: {
103+
pubkey: "npub...",
104+
profile: { /* profile data */ }
105+
}
106+
107+
🗑️ Profile Cache Cleared: {
108+
pubkey: "npub..."
109+
}
110+
```
111+
112+
## Best Practices
113+
114+
1. **Regular Verification**
115+
- Verify sessions on sensitive operations
116+
- Implement periodic verification for long-lived sessions
117+
- Handle verification failures gracefully
118+
119+
2. **Cache Management**
120+
- Use appropriate cache duration for your use case
121+
- Clear cache on profile updates
122+
- Handle cache misses gracefully
123+
124+
3. **Error Handling**
125+
- Always handle verification errors
126+
- Provide clear user feedback
127+
- Implement proper fallback mechanisms
128+
129+
4. **Development**
130+
- Use development mode logs for debugging
131+
- Monitor cache performance
132+
- Test session edge cases
133+
134+
## Security Considerations
135+
136+
1. **Session Storage**
137+
- Use secure storage mechanisms
138+
- Clear sessions on logout
139+
- Implement proper session expiration
140+
141+
2. **Profile Data**
142+
- Validate cached profile data
143+
- Handle missing or corrupt cache
144+
- Implement proper access controls
145+
146+
3. **Error Cases**
147+
- Handle extension disconnection
148+
- Manage multiple device sessions
149+
- Protect against session hijacking
150+
151+
## Example Implementation
152+
153+
```javascript
154+
class AuthManager {
155+
constructor() {
156+
this.auth = new NostrAuthMiddleware();
157+
}
158+
159+
async verifyUserSession(pubkey) {
160+
try {
161+
const isValid = await this.auth.verifySession(pubkey);
162+
if (!isValid) {
163+
await this.handleInvalidSession();
164+
return false;
165+
}
166+
return true;
167+
} catch (error) {
168+
console.error('Session verification failed:', error);
169+
return false;
170+
}
171+
}
172+
173+
async handleInvalidSession() {
174+
await this.auth.logout();
175+
this.auth.clearProfileCache(pubkey);
176+
// Additional cleanup...
177+
}
178+
}
179+
```
180+
181+
## Troubleshooting
182+
183+
Common issues and solutions:
184+
185+
1. **Invalid Session**
186+
- Check Nostr extension connection
187+
- Verify pubkey format
188+
- Check token expiration
189+
190+
2. **Cache Issues**
191+
- Clear corrupted cache
192+
- Check storage limits
193+
- Verify cache timing
194+
195+
3. **Development Mode**
196+
- Enable development mode for detailed logs
197+
- Check browser console
198+
- Monitor cache operations

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,38 @@ const auth = new NostrAuthMiddleware({
163163
- Configure appropriate token expiration times
164164
- Implement proper error handling for token validation
165165

166+
## Session Management
167+
168+
### Browser Session Verification
169+
```javascript
170+
// Verify if a user's session is still valid
171+
const isValid = await auth.verifySession(userPubkey);
172+
if (isValid) {
173+
console.log('Session is valid');
174+
} else {
175+
console.log('Session is invalid or expired');
176+
// Handle logout
177+
}
178+
```
179+
180+
The session verification:
181+
- Checks if the Nostr extension is still available
182+
- Verifies the public key matches
183+
- Handles disconnection gracefully
184+
- Works in both browser and server environments
185+
186+
### Development Mode
187+
When running in development mode, the middleware provides detailed logging:
188+
```javascript
189+
// Development mode logs
190+
Cached Profile: { /* profile data */ }
191+
Fresh Profile: { /* profile and event data */ }
192+
Profile Cache Hit: { pubkey, cacheAge }
193+
Profile Cache Expired: { pubkey, cacheAge }
194+
Profile Cached: { pubkey, profile }
195+
Profile Cache Cleared: { pubkey }
196+
```
197+
166198
## Documentation
167199

168200
- [Architecture Guide](docs/architecture-guide.md) - Understanding the service architecture

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nostr-auth-middleware",
3-
"version": "0.3.3",
3+
"version": "0.3.4",
44
"description": "A focused, security-first authentication middleware for Nostr applications",
55
"author": "vveerrgg",
66
"type": "module",

src/browser/nostr-browser-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ declare global {
1111
nostr?: {
1212
getPublicKey(): Promise<string>;
1313
signEvent(event: NostrEvent): Promise<NostrEvent>;
14-
getRelays(): Promise<{ [url: string]: { read: boolean; write: boolean } }>;
14+
getRelays?(): Promise<{ [url: string]: { read: boolean; write: boolean; } }>;
1515
nip04?: {
1616
encrypt(pubkey: string, plaintext: string): Promise<string>;
1717
decrypt(pubkey: string, ciphertext: string): Promise<string>;

src/middleware/nostr-auth.middleware.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,27 @@ export class NostrAuthMiddleware {
148148
}
149149
}
150150

151+
/**
152+
* Verify if a session is still valid by checking if the pubkey is still accessible
153+
* @param pubkey - The public key to verify
154+
* @returns Promise<boolean> - True if session is valid, false otherwise
155+
*/
156+
async verifySession(pubkey: string): Promise<boolean> {
157+
try {
158+
// In browser mode, verify through extension
159+
if (typeof window !== 'undefined' && window.nostr) {
160+
const extensionPubkey = await window.nostr.getPublicKey();
161+
return extensionPubkey === pubkey;
162+
}
163+
164+
// In server mode, just verify pubkey format
165+
return /^[0-9a-f]{64}$/.test(pubkey);
166+
} catch (error) {
167+
console.error('Error verifying session:', error);
168+
return false;
169+
}
170+
}
171+
151172
/**
152173
* Gets the Express router instance
153174
* @returns {Router} Express router

src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,18 @@ export interface VerificationResult {
108108
pubkey?: string;
109109
data?: Record<string, unknown>;
110110
}
111+
112+
// Extend Window interface to include Nostr
113+
declare global {
114+
interface Window {
115+
nostr?: {
116+
getPublicKey(): Promise<string>;
117+
signEvent(event: NostrEvent): Promise<NostrEvent>;
118+
getRelays?(): Promise<{ [url: string]: { read: boolean; write: boolean; } }>;
119+
nip04?: {
120+
encrypt(pubkey: string, plaintext: string): Promise<string>;
121+
decrypt(pubkey: string, ciphertext: string): Promise<string>;
122+
};
123+
};
124+
}
125+
}

0 commit comments

Comments
 (0)