Headless image-to-video UI queues jobs on the desktop ComfyUI instance with live SSE progress and Authentik SSO.
86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'
|
|
import type { H3Event } from 'h3'
|
|
|
|
export interface SessionUser {
|
|
sub: string
|
|
email?: string
|
|
name?: string
|
|
}
|
|
|
|
const COOKIE = 'aigen_session'
|
|
const STATE_COOKIE = 'aigen_oauth_state'
|
|
|
|
function secret() {
|
|
const config = useRuntimeConfig()
|
|
return config.sessionSecret || 'dev-only-change-me'
|
|
}
|
|
|
|
function sign(value: string) {
|
|
return createHmac('sha256', secret()).update(value).digest('base64url')
|
|
}
|
|
|
|
function seal(payload: unknown) {
|
|
const data = Buffer.from(JSON.stringify(payload)).toString('base64url')
|
|
return `${data}.${sign(data)}`
|
|
}
|
|
|
|
function unseal<T>(token: string | undefined): T | null {
|
|
if (!token || !token.includes('.')) return null
|
|
const [data, sig] = token.split('.')
|
|
const expected = sign(data)
|
|
const a = Buffer.from(sig)
|
|
const b = Buffer.from(expected)
|
|
if (a.length !== b.length || !timingSafeEqual(a, b)) return null
|
|
try {
|
|
return JSON.parse(Buffer.from(data, 'base64url').toString('utf8')) as T
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function cookieOpts(event: H3Event) {
|
|
const proto = getRequestHeader(event, 'x-forwarded-proto') || getRequestProtocol(event)
|
|
return {
|
|
httpOnly: true,
|
|
sameSite: 'lax' as const,
|
|
path: '/',
|
|
secure: proto === 'https',
|
|
maxAge: 60 * 60 * 24 * 14
|
|
}
|
|
}
|
|
|
|
export function getSessionUser(event: H3Event): SessionUser | null {
|
|
return unseal<SessionUser>(getCookie(event, COOKIE))
|
|
}
|
|
|
|
export function setSessionUser(event: H3Event, user: SessionUser) {
|
|
setCookie(event, COOKIE, seal(user), cookieOpts(event))
|
|
}
|
|
|
|
export function clearAuthSession(event: H3Event) {
|
|
deleteCookie(event, COOKIE, { path: '/' })
|
|
}
|
|
|
|
export function createOauthState(event: H3Event) {
|
|
const state = randomBytes(24).toString('hex')
|
|
setCookie(event, STATE_COOKIE, state, { ...cookieOpts(event), maxAge: 600 })
|
|
return state
|
|
}
|
|
|
|
export function consumeOauthState(event: H3Event, incoming: string | undefined) {
|
|
const stored = getCookie(event, STATE_COOKIE)
|
|
deleteCookie(event, STATE_COOKIE, { path: '/' })
|
|
return Boolean(stored && incoming && stored === incoming)
|
|
}
|
|
|
|
export function authEnabled() {
|
|
const config = useRuntimeConfig()
|
|
return Boolean(config.public.authEnabled && config.oidcClientId && config.oidcClientSecret)
|
|
}
|
|
|
|
export function publicBaseUrl(event: H3Event) {
|
|
const proto = getRequestHeader(event, 'x-forwarded-proto') || getRequestProtocol(event)
|
|
const host = getRequestHeader(event, 'x-forwarded-host') || getRequestHost(event)
|
|
return `${proto}://${host}`
|
|
}
|