The job board keeps listings public. applications returns candidates only after requireAuth.
Clone starters/redwood. SDK reference: RedwoodJS.
initAuthdog() stores the browser token after a shape check. requireAuth on the API side is the identity gate. It does not grant a permission. Apply authorization inside the guarded handler.
What you need
| Requirement | Detail |
|---|---|
| Public key | pk_... from the console |
| App | This folder is the Authdog slice, not a generated Redwood app. Create the app with yarn create redwood-app job-board, copy web/src/App.tsx and api/src/functions/applications.ts into it, and install @authdog/redwood in both workspaces |
| Environment | Set PK_AUTHDOG and REDWOOD_ENV_AUTHDOG_PUBLIC_KEY to the same public key. Add REDWOOD_ENV_AUTHDOG_PUBLIC_KEY to includeEnvironmentVariables in redwood.toml |
| Secret key | Never put sk_... in the web side |
| Return URL | Account portal back to the web app |
Run
From the Redwood app, not from the starter folder alone:
yarn rw devAfter hosted sign-in, initAuthdog() stores the token. /.redwood/functions/applications without a bearer returns 401. With Authorization: Bearer it returns app_12 (Support, Northwind) and app_13 (Billing, Contoso), plus the user from userinfo. Do not wrap initAuthdog() in AuthdogProvider. That provider strips the token and does not persist it.
Web callback
// web/src/App.tsx
import { useEffect } from "react"
import { initAuthdog } from "@authdog/redwood/web"
import { RedwoodProvider } from "@redwoodjs/web"
import Routes from "src/Routes"
const App = () => {
useEffect(() => {
initAuthdog()
}, [])
return (
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<Routes />
</RedwoodProvider>
)
}
export default AppApplications
// api/src/functions/applications.ts
import { createAuthdog } from "@authdog/redwood/api"
import type { LambdaEvent } from "@authdog/redwood/api"
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! })
const APPLICATIONS = [
{ id: "app_12", role: "Support", candidate: "Northwind", state: "review" },
{ id: "app_13", role: "Billing", candidate: "Contoso", state: "screen" },
]
export const handler = authdog.requireAuth(async (event: LambdaEvent) => ({
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user: event.authdog?.user ?? null,
applications: APPLICATIONS,
}),
}))In GraphQL services, resolve the user with await authdog.getUser(context.event).