84 lines
2.2 KiB
TypeScript
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
|
|
}
|
|
}
|