Skip to content

Commit 66710fe

Browse files
committed
security: move JWT secret to environment variable
- Add optional secret parameter to generateToken/verifyToken - Falls back to hardcoded constant for local dev without wrangler secret - Add JWT_SECRET to Bindings interface - Update all generateToken callsites to pass c.env.JWT_SECRET - Update requireAuth and optionalAuth middleware to pass env secret - Update magic-link-auth and otp-login plugins Production: set via `wrangler secret put JWT_SECRET` Fixes VULN-001
1 parent 2250de9 commit 66710fe

5 files changed

Lines changed: 21 additions & 17 deletions

File tree

packages/core/src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface Bindings {
5353
IMAGES_ACCOUNT_ID?: string
5454
IMAGES_API_TOKEN?: string
5555
ENVIRONMENT?: string
56+
JWT_SECRET?: string
5657
BUCKET_NAME?: string
5758
GOOGLE_MAPS_API_KEY?: string
5859
}

packages/core/src/middleware/auth.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ type JWTPayload = {
1010
iat: number
1111
}
1212

13-
// JWT secret - in production this should come from environment variables
14-
const JWT_SECRET = 'your-super-secret-jwt-key-change-in-production'
13+
// Fallback JWT secret for local development only (no wrangler secret set)
14+
const JWT_SECRET_FALLBACK = 'your-super-secret-jwt-key-change-in-production'
1515

1616
export class AuthManager {
17-
static async generateToken(userId: string, email: string, role: string): Promise<string> {
17+
static async generateToken(userId: string, email: string, role: string, secret?: string): Promise<string> {
1818
const payload: JWTPayload = {
1919
userId,
2020
email,
2121
role,
2222
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24), // 24 hours
2323
iat: Math.floor(Date.now() / 1000)
2424
}
25-
26-
return await sign(payload, JWT_SECRET, 'HS256')
25+
26+
return await sign(payload, secret || JWT_SECRET_FALLBACK, 'HS256')
2727
}
2828

29-
static async verifyToken(token: string): Promise<JWTPayload | null> {
29+
static async verifyToken(token: string, secret?: string): Promise<JWTPayload | null> {
3030
try {
31-
const payload = await verify(token, JWT_SECRET, 'HS256') as JWTPayload
31+
const payload = await verify(token, secret || JWT_SECRET_FALLBACK, 'HS256') as JWTPayload
3232

3333
// Check if token is expired
3434
if (payload.exp < Math.floor(Date.now() / 1000)) {
@@ -112,7 +112,8 @@ export const requireAuth = () => {
112112

113113
// If not cached, verify token
114114
if (!payload) {
115-
payload = await AuthManager.verifyToken(token)
115+
const jwtSecret = (c.env as any)?.JWT_SECRET
116+
payload = await AuthManager.verifyToken(token, jwtSecret)
116117

117118
// Cache the verified payload for 5 minutes
118119
if (payload && kv) {
@@ -186,7 +187,8 @@ export const optionalAuth = () => {
186187
}
187188

188189
if (token) {
189-
const payload = await AuthManager.verifyToken(token)
190+
const jwtSecret = (c.env as any)?.JWT_SECRET
191+
const payload = await AuthManager.verifyToken(token, jwtSecret)
190192
if (payload) {
191193
c.set('user', payload)
192194
}

packages/core/src/plugins/available/magic-link-auth/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ export function createMagicLinkAuthPlugin(): Plugin {
204204
const jwtToken = await AuthManager.generateToken(
205205
user.id,
206206
user.email,
207-
user.role
207+
user.role,
208+
(c.env as any).JWT_SECRET
208209
)
209210

210211
// Set auth cookie

packages/core/src/plugins/core-plugins/otp-login-plugin/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export function createOTPLoginPlugin(): Plugin {
282282
}
283283

284284
// Generate JWT token
285-
const token = await AuthManager.generateToken(user.id, user.email, user.role)
285+
const token = await AuthManager.generateToken(user.id, user.email, user.role, (c.env as any).JWT_SECRET)
286286

287287
// Set HTTP-only cookie
288288
setCookie(c, 'auth_token', token, {

packages/core/src/routes/auth.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ authRoutes.post('/register',
150150
).run()
151151

152152
// Generate JWT token
153-
const token = await AuthManager.generateToken(userId, normalizedEmail, 'viewer')
153+
const token = await AuthManager.generateToken(userId, normalizedEmail, 'viewer', c.env.JWT_SECRET)
154154

155155
// Set HTTP-only cookie
156156
setCookie(c, 'auth_token', token, {
@@ -226,7 +226,7 @@ authRoutes.post('/login', async (c) => {
226226
}
227227

228228
// Generate JWT token
229-
const token = await AuthManager.generateToken(user.id, user.email, user.role)
229+
const token = await AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET)
230230

231231
// Set HTTP-only cookie
232232
setCookie(c, 'auth_token', token, {
@@ -323,7 +323,7 @@ authRoutes.post('/refresh', requireAuth(), async (c) => {
323323
}
324324

325325
// Generate new token
326-
const token = await AuthManager.generateToken(user.userId, user.email, user.role)
326+
const token = await AuthManager.generateToken(user.userId, user.email, user.role, c.env.JWT_SECRET)
327327

328328
// Set new cookie
329329
setCookie(c, 'auth_token', token, {
@@ -436,7 +436,7 @@ authRoutes.post('/register/form', async (c) => {
436436
).run()
437437

438438
// Generate JWT token
439-
const token = await AuthManager.generateToken(userId, normalizedEmail, role)
439+
const token = await AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET)
440440

441441
// Set HTTP-only cookie
442442
setCookie(c, 'auth_token', token, {
@@ -516,7 +516,7 @@ authRoutes.post('/login/form', async (c) => {
516516
}
517517

518518
// Generate JWT token
519-
const token = await AuthManager.generateToken(user.id, user.email, user.role)
519+
const token = await AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET)
520520

521521
// Set HTTP-only cookie
522522
setCookie(c, 'auth_token', token, {
@@ -884,7 +884,7 @@ authRoutes.post('/accept-invitation', async (c) => {
884884
).run()
885885

886886
// Generate JWT token for auto-login
887-
const authToken = await AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role)
887+
const authToken = await AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET)
888888

889889
// Set HTTP-only cookie
890890
setCookie(c, 'auth_token', authToken, {

0 commit comments

Comments
 (0)