84 lines
2.2 KiB
TypeScript

import { config } from './config'
export const AUTH_COOKIE_NAME = 'auth_token'
const SECRET_KEY = config.auth.secret
// Helper to generate a random nonce
function generateNonce() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
}
// Helper to create a signed token (simplified JWT-like structure)
export async function createToken(username: string) {
const header = { alg: 'HS256', typ: 'JWT' }
const payload = {
sub: username,
iat: Date.now(),
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
nonce: generateNonce() // Ensure token is different every time
}
const encodedHeader = btoa(JSON.stringify(header))
const encodedPayload = btoa(JSON.stringify(payload))
const data = `${encodedHeader}.${encodedPayload}`
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(SECRET_KEY),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signature = await crypto.subtle.sign(
'HMAC',
key,
new TextEncoder().encode(data)
)
// Convert signature to base64
const signatureArray = Array.from(new Uint8Array(signature))
const encodedSignature = btoa(String.fromCharCode.apply(null, signatureArray))
return `${data}.${encodedSignature}`
}
// Helper to verify token
export async function verifyToken(token: string) {
try {
const parts = token.split('.')
if (parts.length !== 3) return false
const [encodedHeader, encodedPayload, encodedSignature] = parts
const data = `${encodedHeader}.${encodedPayload}`
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(SECRET_KEY),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
)
const signature = new Uint8Array(
atob(encodedSignature).split('').map(c => c.charCodeAt(0))
)
const isValid = await crypto.subtle.verify(
'HMAC',
key,
signature,
new TextEncoder().encode(data)
)
if (!isValid) return false
const payload = JSON.parse(atob(encodedPayload))
if (payload.exp < Date.now()) return false
return true
} catch (e) {
return false
}
}