Skip to content
← Public packages

@kentcdodds/audible

Personal Audible library app — browser re-auth, library, wishlist, NAS archive stub.

src/auth-session.ts

87 lines · 2.1 KB · TypeScript
/**
 * In-flight OpenID+PKCE session state in packageStorage (verifier + locale + serial).
 */

import { packageStorage } from 'kody:runtime'

export type AuthSession = {
	authSessionId: string
	codeVerifier: string
	locale: string
	serial: string
	withUsername: boolean
	createdAt: string
}

const PREFIX = 'auth-session:'
const MAX_AGE_MS = 30 * 60 * 1000

function keyFor(id: string) {
	return `${PREFIX}${id}`
}

export async function saveAuthSession(session: AuthSession): Promise<void> {
	await packageStorage().set(keyFor(session.authSessionId), session)
}

export async function loadAuthSession(
	authSessionId: string,
): Promise<AuthSession | null> {
	const id = authSessionId?.trim()
	if (!id) return null
	try {
		const raw = await packageStorage().get(keyFor(id))
		if (raw == null) return null
		const session =
			typeof raw === 'string'
				? (JSON.parse(raw) as AuthSession)
				: (raw as AuthSession)
		if (!session?.codeVerifier || !session?.serial) return null
		const created = Date.parse(session.createdAt || '')
		if (created && Date.now() - created > MAX_AGE_MS) {
			await deleteAuthSession(id)
			return null
		}
		return session
	} catch {
		return null
	}
}

export async function deleteAuthSession(authSessionId: string): Promise<void> {
	try {
		const storage = packageStorage() as {
			delete?: (key: string) => Promise<unknown>
			set: (key: string, value: unknown) => Promise<unknown>
		}
		if (typeof storage.delete === 'function') {
			await storage.delete(keyFor(authSessionId))
		} else {
			await storage.set(keyFor(authSessionId), null)
		}
	} catch {
		// best-effort cleanup
	}
}

const SCOPE_KEY = 'audibleAuthSecretScope'

export async function rememberAuthSecretScope(
	scope: 'package' | 'user',
): Promise<void> {
	await packageStorage().set(SCOPE_KEY, scope)
}

export async function loadAuthSecretScope(): Promise<'package' | 'user' | null> {
	try {
		const raw = await packageStorage().get(SCOPE_KEY)
		if (raw === 'package' || raw === 'user') return raw
		if (typeof raw === 'string') {
			const parsed = JSON.parse(raw)
			if (parsed === 'package' || parsed === 'user') return parsed
		}
	} catch {
		// ignore
	}
	return null
}