Generic Web-Fetch adapter
WIPMount duck-auth on any runtime that speaks the Web-Fetch Request / Response standard. Bun, Deno, Cloudflare Workers, Service Workers - all work.
When to use
The generic adapter is the lowest-level mount point. It exposes:
authExecuteIntents(intents, baseStatus?) -> Response- turnIntent[]into aResponse.parseSignInBody(raw)- validate a sign-in JSON payload.parseProviderBeginBody(raw)- validate a provider-begin payload.
Use it when:
- Your framework isn't on the shipped-adapters list and you don't want to PR a new adapter.
- You need to customise the response shape (e.g. wrap everything in
{ ok: true, data: ... }). - You're writing a Service Worker that intercepts auth calls client-side.
Bun (native)
import { authExecuteIntents, parseSignInBody } from '@gentleduck/auth/server/generic'
import { auth } from './lib/auth'
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url)
if (url.pathname === '/auth/signin' && req.method === 'POST') {
const body = parseSignInBody(await req.json())
if (!body) return new Response('Bad Request', { status: 400 })
const intents = await auth.flows.signIn(body)
return authExecuteIntents(intents)
}
if (url.pathname === '/auth/session' && req.method === 'GET') {
const ctx = await auth.resolveSession(req)
if (!ctx) return new Response('Unauthorized', { status: 401 })
return Response.json(ctx)
}
return new Response('Not Found', { status: 404 })
},
})
Deno
import { authExecuteIntents } from '@gentleduck/auth/server/generic'
import { auth } from './lib/auth.ts'
Deno.serve({ port: 3000 }, async (req) => {
const url = new URL(req.url)
if (url.pathname === '/auth/signin' && req.method === 'POST') {
const body = await req.json()
const intents = await auth.flows.signIn(body)
return authExecuteIntents(intents)
}
// ...
return new Response('Not Found', { status: 404 })
})
Cloudflare Workers
import { authExecuteIntents } from '@gentleduck/auth/server/generic'
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const auth = createAuthForRequest(env)
const url = new URL(req.url)
if (url.pathname === '/auth/signin' && req.method === 'POST') {
const intents = await auth.flows.signIn(await req.json())
return authExecuteIntents(intents)
}
return new Response('Not Found', { status: 404 })
},
}
Custom intent processing
If you need to wrap the response or add headers:
import { authExecuteIntents } from '@gentleduck/auth/server/generic'
const baseResponse = authExecuteIntents(intents)
// Read the body, wrap it, return a new Response with the same headers.
const wrapped = new Response(
JSON.stringify({ ok: true, data: await baseResponse.json() }),
{
status: baseResponse.status,
headers: baseResponse.headers,
},
)
return wrapped
For more sophisticated needs (different status codes, redirects to your SPA after sign-in), construct the response by walking the intents:
for (const intent of intents) {
switch (intent.kind) {
case 'startSession':
// mint cookie/token, attach to response
break
case 'redirect':
return Response.redirect(intent.url, intent.status ?? 302)
case 'json':
return Response.json(intent.body, { status: intent.status })
}
}
This is rarely necessary - the shipped adapters cover 99% of cases - but the door is open.