move JWT validation to package

This commit is contained in:
Devin Zuczek
2026-07-09 20:14:23 -04:00
parent cb48cefa7d
commit bdebc50075
35 changed files with 102 additions and 530 deletions
+1
View File
@@ -17,6 +17,7 @@
"dependencies": {
"@repo/domain": "workspace:*",
"@repo/hono-helpers": "workspace:*",
"@repo/jwt": "workspace:*",
"hono": "4.12.27",
"workers-tagged-logger": "1.0.1"
},
-57
View File
@@ -1,57 +0,0 @@
/**
* Minimal HS256 JWT validation.
*
* The signing key is supplied by the caller from the shared `JWT_SECRET` Secrets
* Store binding (see context.ts) - the same key the `auth` worker signs with.
*/
function base64urlToBytes(input: string): Uint8Array {
const padded = input.replace(/-/g, '+').replace(/_/g, '/')
const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4))
const bytes = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i)
}
return bytes
}
/**
* Validate an HS256 token and return its `sub` (account id) claim, or `null`
* when the token is malformed, has a bad signature, or is expired.
*/
export async function validateAndGetAccountId(
token: string,
secret: string
): Promise<string | null> {
const parts = token.split('.')
if (parts.length !== 3) return null
const [header, payload, signature] = parts
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
)
const valid = await crypto.subtle.verify(
'HMAC',
key,
base64urlToBytes(signature),
new TextEncoder().encode(`${header}.${payload}`)
)
if (!valid) return null
let claims: { sub?: string; exp?: number }
try {
claims = JSON.parse(new TextDecoder().decode(base64urlToBytes(payload)))
} catch {
return null
}
if (typeof claims.exp === 'number' && claims.exp < Math.floor(Date.now() / 1000)) {
return null
}
return claims.sub ?? null
}
+1 -1
View File
@@ -3,8 +3,8 @@ import { useWorkersLogger } from 'workers-tagged-logger'
import { RoomInstanceType } from '@repo/domain'
import { withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
import { validateAndGetAccountId } from './jwt'
import {
createRoomInstance,
getJoinableInstance,