Files
app/lib/auth/jwt.ts
Albert 93ebb0948c feat: Complete Step 3 & 4 - OAuth + SurrealDB schema
Step 3: ATproto OAuth + SurrealDB JWT
- Implement database-backed OAuth state storage (lib/auth/oauth-state.ts)
- Add session helpers for JWT decoding (lib/auth/session.ts)
- Fix OAuth callback to properly handle state retrieval
- Create /chat page displaying authenticated user handle
- Configure headless mode for Magnitude testing

Step 4: SurrealDB Schema & Permissions
- Define JWT-based access control (HS512 algorithm)
- Create user table with DID-based identity
- Create node table with row-level security (users can only access their own data)
- Create links_to relation table for graph edges
- Define vector search index (1536 dimensions for gemini-embedding-001)
- Add Docker Compose for local SurrealDB development

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 23:51:19 +00:00

70 lines
1.6 KiB
TypeScript

import jwt from 'jsonwebtoken';
export interface UserSession {
did: string;
handle: string;
iss: string;
aud: string;
exp: number;
iat: number;
}
/**
* Mints a new JWT for our application's session management.
* This token is what SurrealDB will validate.
*
* @param did - The user's canonical ATproto DID (e.g., "did:plc:...")
* @param handle - The user's Bluesky handle (e.g., "user.bsky.social")
* @returns A signed JWT string.
*/
export function mintSurrealJwt(did: string, handle: string): string {
const secret = process.env.SURREALDB_JWT_SECRET;
if (!secret) {
throw new Error('SURREALDB_JWT_SECRET is not set in environment.');
}
// This payload is critical. The `did` claim will be used
// in SurrealDB's PERMISSIONS clauses.
const payload = {
// Standard JWT claims
iss: 'Ponderants',
aud: 'SurrealDB',
// Custom claims
did: did,
handle: handle,
};
// Token expires in 7 days
const token = jwt.sign(payload, secret, {
algorithm: 'HS512',
expiresIn: '7d',
});
return token;
}
/**
* Verifies and decodes a JWT token.
*
* @param token - The JWT token to verify
* @returns The decoded user session, or null if invalid
*/
export function verifySurrealJwt(token: string): UserSession | null {
const secret = process.env.SURREALDB_JWT_SECRET;
if (!secret) {
throw new Error('SURREALDB_JWT_SECRET is not set in environment.');
}
try {
const decoded = jwt.verify(token, secret, {
algorithms: ['HS512'],
}) as UserSession;
return decoded;
} catch (error) {
console.error('JWT verification failed:', error);
return null;
}
}