← Public packages
@danj/audible-auth
Locked Audible credential gateway: Connect, token refresh, and narrow Audible calls for other Kody packages.
src/device.ts
77 lines · 2.2 KB · TypeScript/** Non-secret facts about the registered virtual device, in package storage. */
import { packageStorage } from 'kody:runtime'
import { ARCHIVE_SIGNING_KEY, DEVICE_KEY } from './config.ts'
export type DeviceRecord = {
locale: string
withUsername: boolean
serial: string
deviceType: string
customerId?: string
deviceName?: string
connectedAt: string
/** Unix seconds when the current access token expires. */
accessTokenExpiresAt: number
/** Saved package id that owns the token secrets; builds host-approval links. */
packageId?: string
/** First time an Audible call succeeded, which proves the hosts are approved. */
verifiedAt?: string
}
/** ADP signing material. Stored only while archive is enabled. */
export type ArchiveSigning = {
adpToken: string
devicePrivateKey: string
}
export class NotConnectedError extends Error {
constructor() {
super('Audible is not connected. Open the Connect tab in the Audible app.')
this.name = 'NotConnectedError'
}
}
function parse<T>(raw: unknown): T | null {
if (raw == null) return null
if (typeof raw === 'string') {
try {
return JSON.parse(raw) as T
} catch {
return null
}
}
return raw as T
}
export async function loadDevice(): Promise<DeviceRecord | null> {
const device = parse<DeviceRecord>(await packageStorage().get(DEVICE_KEY))
return device?.serial && device.locale ? device : null
}
export async function requireDevice(): Promise<DeviceRecord> {
const device = await loadDevice()
if (!device) throw new NotConnectedError()
return device
}
export async function saveDevice(device: DeviceRecord): Promise<void> {
await packageStorage().set(DEVICE_KEY, device)
}
export async function clearDevice(): Promise<void> {
await packageStorage().delete(DEVICE_KEY)
}
export async function loadArchiveSigning(): Promise<ArchiveSigning | null> {
const signing = parse<ArchiveSigning>(await packageStorage().get(ARCHIVE_SIGNING_KEY))
return signing?.adpToken && signing.devicePrivateKey ? signing : null
}
export async function saveArchiveSigning(signing: ArchiveSigning): Promise<void> {
await packageStorage().set(ARCHIVE_SIGNING_KEY, signing)
}
export async function clearArchiveSigning(): Promise<void> {
await packageStorage().delete(ARCHIVE_SIGNING_KEY)
}