Authdog

Support inbox

Last updated Oct 7, 2026
View as Markdown

The support inbox lists customer tickets for the signed-in agent. The home page stays reachable without a session.

Clone starters/remix. SDK reference: Remix.

The ticket list answers who the agent is. It does not grant a permission. Apply authorization after the identity check.

What you need

Requirement Detail
Public key pk_... from the console
Environment Copy .env.example to .env and set PK_AUTHDOG
Secret key Never put sk_... in client code
Return URL Account portal back to this app. Local: http://localhost:5173. The root loader must receive ?token=

Run

npm install
npm run dev

Open http://localhost:5173. Sign in, then open /inbox. You should see tkt_88 (Northwind, invoice PDF) and tkt_89 (Contoso, locked test user). Without a session, /inbox redirects to signinUri.

Root loader

identityLoader() validates ?token= through userinfo and sets HttpOnly cookies. Keep that exchange on the root loader. Calling .json() on the callback response and rebuilding it drops Set-Cookie.

// app/root.tsx
import { Outlet, Scripts } from "@remix-run/react"
import { AuthdogProvider, ReloadPage } from "@authdog/remix-node/client"
import { identityLoader } from "@authdog/remix-node"

export const loader = identityLoader()

export default function App() {
  return (
    <AuthdogProvider>
      <Outlet />
      <Scripts />
      <ReloadPage />
    </AuthdogProvider>
  )
}

AuthdogProvider strips ?token= in the browser. The HttpOnly cookie is set by the server loader. The loader itself returns an unauthenticated result instead of denying the request. The inbox enforces it.

Inbox

// app/routes/inbox.tsx
import { identityLoader } from "@authdog/remix-node"
import { redirect, type LoaderFunctionArgs } from "@remix-run/node"

const TICKETS = [
  { id: "tkt_88", from: "Northwind", subject: "Invoice PDF missing", state: "open" },
  { id: "tkt_89", from: "Contoso", subject: "SSO test user locked", state: "waiting" },
]

const loadIdentity = identityLoader()

export const loader = async (args: LoaderFunctionArgs) => {
  const response = await loadIdentity(args)
  if (!(response instanceof Response)) {
    throw new Response("Identity loader failed", { status: 500 })
  }
  const identity = (await response.json()) as {
    isAuthenticated: boolean
    signinUri: string
  }
  if (!identity.isAuthenticated) throw redirect(identity.signinUri)
  return { ...identity, tickets: TICKETS }
}