Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { Connection, PublicKey } from '@solana/web3.js';
import nacl from 'tweetnacl';
import bs58 from 'bs58';
import { v4 as uuidv4 } from 'uuid';
import { signToken, signRefreshToken, verifyToken, verifyRefreshToken, authenticateJWT } from '../utils/jwt.js';
import { signToken, signRefreshToken, verifyToken, verifyRefreshToken } from '../utils/jwt.js';
import { getIdentity } from '../db/queries.js';
import { logger } from '../utils/logger.js';
import { rateLimiter, challengeManager, sessionManager } from '../utils/redis.js';
import { rateLimiter, sessionManager } from '../utils/redis.js';

const router = Router();

Expand Down
30 changes: 0 additions & 30 deletions backend/src/routes/challenges.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,34 +289,4 @@ router.get('/:id', async (req, res) => {
}
});

/**
* GET /api/challenges
* List all challenges (for debugging in development)
*/
router.get('/', async (req, res) => {
try {
if (process.env.NODE_ENV !== 'development') {
return res.status(403).json({ error: 'Endpoint only available in development' });
}

const allChallenges = Array.from(challenges.values()).map(c => ({
challengeId: c.challengeId,
appName: c.appName,
status: c.status,
proofType: c.proofType,
createdAt: c.createdAt,
expiresAt: c.expiresAt
}));

res.json({
success: true,
count: allChallenges.length,
challenges: allChallenges
});
} catch (error) {
logger.error('Error listing challenges:', error);
res.status(500).json({ error: 'Failed to list challenges' });
}
});

export default router;
4 changes: 2 additions & 2 deletions backend/src/routes/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ router.get('/:walletAddress', async (req, res) => {
await cache.set(`identity:${walletAddress}`, {
walletAddress: identity.wallet_address,
isVerified: identity.is_verified,
attributesVerified: identity.attributes_verified,
createdAt: identity.created_at
verificationTimestamp: identity.verification_timestamp,
attributesVerified: identity.attributes_verified
Comment on lines +122 to +123

Copilot AI Jan 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change replaces 'createdAt' with 'verificationTimestamp' in the API response. This is a breaking change that will affect API consumers. The 'verificationTimestamp' field can be null for unverified identities, whereas 'createdAt' would always have a value. Ensure that API consumers and frontend code are updated to handle this change, or consider including both fields during a transition period to maintain backward compatibility.

Copilot uses AI. Check for mistakes.
}, 300);

res.json({
Expand Down
2 changes: 1 addition & 1 deletion backend/src/routes/proof.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { generateZKProof, verifyZKProof } from '../utils/zkproof.js';
import { updateIdentityVerification } from '../db/queries.js';
import { logger } from '../utils/logger.js';
import { v4 as uuidv4 } from 'uuid';
import { rateLimiter, cache } from '../utils/redis.js';
import { rateLimiter } from '../utils/redis.js';

const router = Router();

Expand Down
26 changes: 25 additions & 1 deletion backend/src/utils/jwt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import jwt from 'jsonwebtoken';
import { logger } from './logger.js';

const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
const NODE_ENV = process.env.NODE_ENV || 'development';
let JWT_SECRET = process.env.JWT_SECRET;

if (!JWT_SECRET || JWT_SECRET.length < 32) {
if (NODE_ENV !== 'development' && NODE_ENV !== 'test') {
throw new Error(
'JWT_SECRET environment variable must be set and at least 32 characters long in production-like environments'
);
}
logger.warn(
'JWT_SECRET is not set or is too weak; using an insecure fallback secret for development/test only. ' +
'Do NOT use this configuration in production.'
);
JWT_SECRET = 'fallback-secret-change-in-production-insecure-dev-only';
}

const JWT_EXPIRY = process.env.JWT_EXPIRY || '1h';
const JWT_REFRESH_EXPIRY = process.env.JWT_REFRESH_EXPIRY || '7d';

Expand Down Expand Up @@ -77,6 +92,15 @@ export function verifyToken(token) {
audience: 'solstice-api'
});

// Ensure this is an access token, not a refresh token
if (decoded.type !== 'access') {
logger.warn('Invalid token type for access verification');
return {
valid: false,
error: 'Invalid token type'
};
}

return {
valid: true,
payload: decoded,
Expand Down
26 changes: 22 additions & 4 deletions backend/src/utils/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,14 +321,32 @@ export const cache = {

/**
* Invalidate all cache entries matching a pattern
* Uses SCAN to avoid blocking Redis (safe for production)
* @param {string} pattern - Pattern to match (e.g., 'user:123:*')
*/
async invalidatePattern(pattern) {
const client = getRedisClient();
const keys = await client.keys(`cache:${pattern}`);
if (keys.length > 0) {
await client.del(keys);
logger.debug(`Invalidated ${keys.length} cache entries for pattern: ${pattern}`);
const matchPattern = `cache:${pattern}`;
let cursor = 0;
let totalDeleted = 0;
const batchSize = 1000;

do {
const reply = await client.scan(cursor, {
MATCH: matchPattern,
COUNT: batchSize
});
cursor = Number(reply.cursor);
const keys = reply.keys;

if (keys && keys.length > 0) {
const deleted = await client.del(keys);
totalDeleted += deleted;
}
} while (cursor !== 0);

if (totalDeleted > 0) {
logger.debug(`Invalidated ${totalDeleted} cache entries for pattern: ${pattern}`);
}
}
Comment on lines 327 to 351

Copilot AI Jan 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SCAN implementation should include error handling to prevent the application from crashing if Redis operations fail during cache invalidation. Consider wrapping the SCAN loop in a try-catch block and logging any errors that occur.

Copilot uses AI. Check for mistakes.
};
Expand Down
Loading