import type { H3Event } from "h3"; import type { H3AppShim } from "./framework-request-handler.js"; import { isHttpsRequest } from "./https-request.js"; type H3App = H3AppShim; import type { AuthPageProps, ResetPasswordPageProps } from "../shared/auth-page-types.js"; import { type WorkspaceAppAudience } from "../shared/workspace-app-audience.js"; import type { BetterAuthConfig } from "./better-auth-instance.js"; import type { GoogleAuthMode } from "./google-auth-mode.js"; import { type OnboardingHtmlOptions } from "./onboarding-html.js"; import { isSessionResolutionUnavailable } from "./session-resolution-unavailable.js"; export declare function getSessionMaxAge(): number; export interface AuthSession { email: string; userId?: string; authUserId?: string; token?: string; name?: string; image?: string; emailVerified?: boolean; orgId?: string; orgRole?: string; } /** * `AuthSession` as `/_agent-native/auth/session` returns it. The browser * cannot read `testIdentity.emails`, and that list must never be sent to it, * so the server resolves `isTestIdentity` here for browser telemetry to honor. */ export interface AuthSessionResponse extends AuthSession { testIdentity: boolean; identitySsoEnabled?: boolean; } export interface AuthOptions { maxAge?: number; getSession?: (event: H3Event) => Promise; /** * With custom authentication, resolve browser identity from cookies only. * Never accept bearer tokens, query tokens, or other request headers here. */ getBrowserSession?: (event: H3Event) => Promise; /** * An extra identity source, consulted before the built-in sessions. Unlike * `getSession`, it does not replace them: a request it does not recognize * (returns `null`) resolves exactly as it would without it, and the built-in * sign-in routes stay mounted. Use it to let a host product vouch for its * own users on a deployment that also serves everyone else. If it throws, the * request is refused as session-unavailable (503) rather than falling * through, so a browser holding two identities cannot silently become the * other one. Ignored when `getSession` is set, which already takes over every * session. */ getAdditionalSession?: (event: H3Event) => Promise; /** * The cookie-only counterpart of `getAdditionalSession`, for browser UI * capabilities and approvals. Never accept bearer tokens, query tokens, or * other request headers here. */ getAdditionalBrowserSession?: (event: H3Event) => Promise; /** * Called on sign-out to end the additional provider's own session (clear its * cookie, drop any server-side state), which the built-in sign-out knows * nothing about. Without it a user would still resolve as signed in on the * next request. */ clearAdditionalSession?: (event: H3Event) => void | Promise; /** * Set only when the custom provider (`getSession` or * `getAdditionalSession`) independently verifies email ownership. Without * this opt-in, an omitted `emailVerified` value remains unknown. */ trustCustomEmailVerification?: boolean; publicPaths?: string[]; /** * Routes whose handlers authenticate a query token. These bypass session * auth only when the named query parameter is present; without it, normal * session and workspace-app authorization runs. */ publicPathsWithQueryToken?: Array<{ path: string; queryParam: string; }>; /** * Public, unauthenticated ingest paths that may receive cross-origin * requests when CORS_ALLOWED_ORIGINS is unset. These routes must perform * their own request validation and must not rely on cookies for auth. */ publicCorsPaths?: string[]; workspaceAppAudience?: WorkspaceAppAudience; workspaceAppPublicPaths?: string[]; workspaceAppProtectedPaths?: string[]; loginHtml?: string; renderSignInPage?: (props: AuthPageProps) => string; renderResetPasswordPage?: (props: ResetPasswordPageProps) => string; rootAuth?: boolean; googleOnly?: boolean; mountGoogleOAuthRoutes?: boolean; googleScopes?: string[]; marketing?: false | { appName: string; tagline: string; description?: string; features?: string[]; learnMoreUrl?: string; }; signupLegalNotice?: OnboardingHtmlOptions["signupLegalNotice"]; googleAuthMode?: GoogleAuthMode; betterAuth?: BetterAuthConfig; } export declare function getCookieDomain(): string | undefined; export declare const COOKIE_NAME: string; export declare const SESSION_HINT_COOKIE: string; export declare const BETTER_AUTH_COOKIE_PREFIX: string; export declare function cookieDomainAttrs(): { domain?: string; }; export declare function sharedFirstPartyCookieDomainAttrs(): { domain?: string; }; export declare function getFrameworkSessionCookieValues(event: H3Event): string[]; export declare function clearFrameworkSessionHintCookies(event: H3Event): void; export declare function clearFrameworkSessionCookies(event: H3Event): void; export declare function isDevEnvironment(): boolean; /** * @deprecated Prefer `normalizeAppPath` from `@agent-native/core/shared`, * which returns `null` for a rejected path instead of a `"/"` a caller cannot * distinguish from a genuine request for the home page. * * Retained because eight template call sites use it for PROVIDER OAuth * returns (Google Calendar, Slack, Google Docs, …), which is a legitimately * separate concern from the sign-in journey. Deliberately passes NO base path: * provider return targets are not guaranteed to be base-path prefixed, and * tightening that here would silently collapse working provider returns to * "/" on base-path deploys. Base-path containment belongs to `signInJourney`. */ export declare function safeReturnPath(raw: string | null | undefined): string; export interface ConfiguredLoginPage { html: string; /** 503 while the deploy is missing a setting sign-in needs. */ status: 200 | 503; } export declare function getConfiguredLoginHtml(event: H3Event): ConfiguredLoginPage | null; /** * True only when the request originates from the local machine — the raw * socket peer is `127.0.0.0/8`, `::1`, or the IPv4-mapped `::ffff:127.0.0.1` * (an optional IPv6 zone id like `fe80::1%en0` is stripped first). * * `getRequestIP(event)` is called WITHOUT `{ xForwardedFor: true }`, so it * returns the real connection peer and never an attacker-controlled * `X-Forwarded-For` value — a remote client cannot spoof its way past this. * Used to scope local-only conveniences (the desktop SSO broker and the dev * auto-account) so a directly network-reachable dev server never exposes * them to a remote visitor. NOTE: a reverse proxy / tunnel that connects to * the dev server over localhost still appears as loopback, so this is a * necessary but not sufficient gate — callers pair it with NODE_ENV and, * for the dev account, a throwaway per-DB password. */ export declare function isLoopbackAddress(ip: string | undefined): boolean; /** * True when the request's actual socket peer is loopback. Uses * `getRequestIP(event)` WITHOUT `{ xForwardedFor: true }`, so it reflects the * real connecting IP and a remote client cannot spoof it via the `Host` / * `X-Forwarded-*` headers. Use this — not a parsed `Host`-header origin — for * any "is this local dev?" security gate (MCP/connect dev-open). */ export declare function isLoopbackRequest(event: H3Event): boolean; /** * Verify a connect-minted MCP OAuth access token presented as * `Authorization: Bearer ` and resolve it to a session. * * `agent-native connect` mints this token for the local Plans publish flow and * POSTs it to the HOSTED action route * `/_agent-native/actions/import-visual-plan-source`. That token is audience- * bound to the app's canonical MCP resource (`{appUrl}/mcp`; the legacy * `/_agent-native/mcp` resource is also accepted), not to the legacy `sessions` * table — so the legacy bearer lookup above never matches it. * Reuse the MCP surface's canonical `verifyAuth` here so the HTTP action surface * honors EXACTLY the tokens the MCP endpoint honors: same signature check, same * audience binding to THIS app's resource, same connect-token revocation gate. * It resolves to the same `{ userEmail, orgId }` identity the MCP path uses, so * downstream `accessFilter` / ownable-data scoping is identical. * * `allowDevOpen: false` and the `userEmail` guard ensure an invalid token (or a * bare ACCESS_TOKEN with no owner hint) never escalates to an unauthenticated * or unscoped identity on this path — it strictly adds acceptance of verified, * audience-bound caller tokens, nothing more. Custom routes can opt in by * calling this helper explicitly; generic `getSession` calls keep this token * limited to action routes by default. */ export declare function getMcpOAuthBearerSession(event: H3Event): Promise; export declare function isExpectedAuthFailure(error: unknown): boolean; export declare const authSessionsStore: import("../db/store-registry.js").Store; export declare function ensureSessionTable(): Promise; export declare function addSession(token: string, email?: string): Promise; export declare function hasLegacySessionForEmail(email: string): Promise; export declare function removeSession(token: string): Promise; export declare function logout(event: H3Event): Promise<{ ok: true; } | { error: string; }>; export declare function getSessionEmail(token: string): Promise; export { isSessionResolutionUnavailable }; /** * Allow framework routes with their own non-cookie authentication to reach * the handler. The handler remains responsible for verifying the credential. * * Registered paths are exact matches, where a `:name` segment stands for any * one segment: `/x/:id` admits `/x/abc`, never `/x` or `/x/abc/more`. * `/x/:id(uuidv4)` restricts that segment to a lowercase UUID v4. * These are registered separately from * template auth options because framework routes can be mounted after the auth * plugin and must also work in custom workspace deployments that do not own a * template auth file. */ export declare function registerAuthPublicPaths(paths: readonly string[], app?: object): void; export interface DesktopExchangeErrorPayload { message: string; code?: string; accountId?: string; existingOwner?: string; attemptedOwner?: string; } export declare const DESKTOP_OAUTH_BROWSER_BINDING_COOKIE = "an_desktop_oauth_binding"; export declare function prepareDesktopOAuthBrowserBinding(event: H3Event): string; export declare function matchesDesktopOAuthBrowserBinding(event: H3Event, expectedHash: string): boolean; export declare function setDesktopExchange(flowId: string, token: string, email: string, verifierHash: string): Promise; export declare function registerDesktopExchange(flowId: string, verifier: string, browserBindingHash?: string): Promise; export declare function setDesktopExchangeError(flowId: string, error: DesktopExchangeErrorPayload): void; export declare function runAuthGuard(event: H3Event): Promise; export declare function shouldBypassAuthForBuilderConnect(event: H3Event, p: string): boolean; export declare function getAuthGuardPath(event: H3Event): string | null; export declare function isLocalDevAuthAllowed(event: H3Event): boolean; export declare function getSession(event: H3Event): Promise; /** * The browser's signed-in user, read only from the session cookies sign-in * sets: Better Auth's and the framework's own. No other credential counts: * not a bearer or embed token, a query token, a generic custom resolver, or the * desktop fallback, nor any credential `getSession` accepts in future. For * pages whose decisions only the person may make. An app with a custom * resolver must supply the explicit cookie-only `getBrowserSession` hook, * since the framework can't tell which credentials `getSession` reads. * The framework cookie takes a raw login * token, so a desktop or mobile app can present its own login here; that app * is the user. With AUTH_DISABLED every request is the dev account, here too. */ export declare function getBrowserCookieSession(event: H3Event): Promise; export declare function getBrowserSessionConfigurationError(): string | undefined; /** * Cookie attributes that work in both same-site and third-party iframe * contexts. Over HTTPS we emit `SameSite=None; Secure; Partitioned` — * `None`+`Secure` is required by browsers to ship the cookie back inside a * cross-origin iframe at all; `Partitioned` keeps the cookie working under * Chrome's third-party-cookie deprecation by binding it to the embedding * site's storage partition. (Better Auth already sets the same trio on its * own session cookie; this matches so the framework's legacy cookie — * which the Builder OAuth popup exchange writes via * `setFrameworkSessionCookie` — survives iframe contexts too.) Plain-HTTP * dev keeps the default `SameSite=Lax`; `None` requires Secure, and * `Partitioned` only takes effect alongside `Secure`. */ export declare function crossSiteCookieAttrs(event: H3Event): { sameSite: "lax" | "none"; secure: boolean; partitioned?: boolean; }; export declare function setFirstRunOnboardingCookie(event: H3Event): void; export declare const authSessionHandler: import("h3").EventHandlerWithFetch>; export declare function setFrameworkSessionCookie(event: H3Event, token: string): void; export declare function redirectWithStagedCookies(event: H3Event, location: string, status?: number): Response; export { isHttpsRequest }; export declare function autoMountAuth(app: H3App, options?: AuthOptions): Promise; /** * @deprecated Use `autoMountAuth(app, options?)` instead. */ export declare function mountAuthMiddleware(app: H3App, accessToken: string): void;