import { managementPermissionStatement, managementRolePermissions, } from "@automate.ax/api-contract" import { createAuthClient } from "better-auth/client" import { deviceAuthorizationClient, emailOTPClient, organizationClient, } from "better-auth/client/plugins" import { createAccessControl } from "better-auth/plugins/access" import { joinURL } from "ufo" import { env } from "./env" import { loadSavedTokenSync, saveTokenSync } from "./session" /** Shared access-control definition for the organization client roles. */ const ac = createAccessControl(managementPermissionStatement) export interface CliAuthUser { createdAt: Date | string email: string emailVerified: boolean id: string image?: string | null name: string updatedAt: Date | string } export interface CliAuthSession { user: CliAuthUser } export interface CliAuthClient { device: { code(input: { client_id: string }): Promise<{ device_code: string user_code: string }> token(input: { client_id: string device_code: string fetchOptions?: { headers: Record } grant_type: string }): Promise<{ access_token: string }> } getSession(): Promise organization: { acceptInvitation(input: { invitationId: string }): Promise<{ member: { organizationId: string role: string } }> } signOut(): Promise } export const authClient: CliAuthClient = createAuthClient({ baseURL: joinURL(env.APP_ORIGIN, "/api/auth"), fetchOptions: { throw: true, auth: { type: "Bearer", token: () => loadSavedTokenSync() || "", }, onSuccess: (ctx) => { const token = ctx.response.headers.get("set-auth-token") if (token) { saveTokenSync(token) } }, }, // API-key and Stripe operations intentionally use the first-party management // API. Their Better Auth client plugins add a peer-heavy dependency graph to // the published SDK that can make npm installation loop or hang. plugins: [ organizationClient({ ac, roles: { admin: ac.newRole(managementRolePermissions.admin), member: ac.newRole(managementRolePermissions.member), }, }), emailOTPClient(), deviceAuthorizationClient(), ], })