Skip to content
← Public packages

@kentcdodds/raycast

Raycast launcher: stored execute commands via webhooks (canonical; do not use raycast-shortcuts).

src/launcher-auth.ts

38 lines · 1.4 KB · TypeScript
import { packageStorage } from 'kody:runtime'

export const LAUNCHER_TOKEN_HASH_KEY = 'launcherTokenSha256'
export const LAUNCHER_TOKEN_HINT_KEY = 'launcherTokenHint'

export async function sha256Hex(value: string): Promise<string> {
	const bytes = new TextEncoder().encode(value)
	const digest = await crypto.subtle.digest('SHA-256', bytes)
	return [...new Uint8Array(digest)]
		.map((b) => b.toString(16).padStart(2, '0'))
		.join('')
}

export async function storeLauncherToken(token: string) {
	const trimmed = token.trim()
	if (trimmed.length < 24) {
		throw new Error('Launcher token must be at least 24 characters.')
	}
	const hash = await sha256Hex(trimmed)
	const storage = packageStorage()
	await storage.set(LAUNCHER_TOKEN_HASH_KEY, hash)
	await storage.set(LAUNCHER_TOKEN_HINT_KEY, trimmed.slice(-4))
	return { hash, hint: trimmed.slice(-4) }
}

export async function verifyLauncherToken(token: string | null): Promise<boolean> {
	if (!token) return false
	const expected = await packageStorage().get(LAUNCHER_TOKEN_HASH_KEY)
	if (typeof expected !== 'string' || !expected) return false
	const actual = await sha256Hex(token.trim())
	return actual === expected
}

export function bearerFromRequest(request: Request): string | null {
	const header = request.headers.get('authorization') || ''
	const match = header.match(/^Bearer\s+(.+)$/i)
	return match?.[1]?.trim() || null
}