Using generateId: "serial" (integer PKs) and allowDynamicHostRegistration: true, any external agent that self-registers crashes the request instead of succeeding or getting a clean error.
findHostByIdOrKid looks up the host by the JWT's iss claim directly against id:
async function findHostByIdOrKid(adapter, iss) {
return await adapter.findOne({
model: TABLE.host,
where: [{ field: "id", value: iss }]
}) ?? await adapter.findOne({
model: TABLE.host,
where: [{ field: "kid", value: iss }]
});
}
Under generateId: "serial", better-auth's core adapter coerces any id-referencing filter value with Number(value) before it reaches Prisma, with no check on whether that produces a real number:
const useNumberId = options.advanced?.database?.generateId === "serial";
...
if (defaultFieldName === "id" || fieldAttr.references?.field === "id") {
newValue = Number(newValue);
}
@auth/agent's client defaults a host JWT's iss to a JWK thumbprint, a non-numeric string by design. Number(thumbprint) is NaN, Prisma rejects it as an invalid Int filter, and the request throws before it ever reaches the kid fallback in findHostByIdOrKid. That fallback only runs if the first lookup resolves to null, not if it throws.
Environment
@better-auth/agent-auth: 0.4.5 (same code in 0.6.2, unchanged)
better-auth / @better-auth/core / @better-auth/prisma-adapter: 1.5.6
@auth/agent (client): 0.5.1
- PostgreSQL via Prisma,
advanced.database.generateId: "serial"
Reproduction
agentAuth({ modes: ["autonomous"], allowDynamicHostRegistration: true, ... }) on a betterAuth() instance with generateId: "serial".
- External agent discovers the provider (works fine) and calls
client.connectAgent({ provider, mode: "autonomous", ... }).
connectAgent() signs a host JWT with the default iss and POSTs to /agent/register.
Current vs. expected
Current, 500:
PrismaClientValidationError:
Invalid `prisma.agentHost.findFirst()` invocation:
{ where: { id: { equals: Int | IntFieldRefInput, ... } }, select: undefined }
Argument `id` is missing.
Expected: registration succeeds, or fails with a normal auth error, not an unhandled adapter exception. At minimum the kid fallback in findHostByIdOrKid should actually be reachable for a non-numeric iss.
Using
generateId: "serial"(integer PKs) andallowDynamicHostRegistration: true, any external agent that self-registers crashes the request instead of succeeding or getting a clean error.findHostByIdOrKidlooks up the host by the JWT'sissclaim directly againstid:Under
generateId: "serial", better-auth's core adapter coerces anyid-referencing filter value withNumber(value)before it reaches Prisma, with no check on whether that produces a real number:@auth/agent's client defaults a host JWT'sissto a JWK thumbprint, a non-numeric string by design.Number(thumbprint)isNaN, Prisma rejects it as an invalidIntfilter, and the request throws before it ever reaches thekidfallback infindHostByIdOrKid. That fallback only runs if the first lookup resolves tonull, not if it throws.Environment
@better-auth/agent-auth:0.4.5(same code in0.6.2, unchanged)better-auth/@better-auth/core/@better-auth/prisma-adapter:1.5.6@auth/agent(client):0.5.1advanced.database.generateId: "serial"Reproduction
agentAuth({ modes: ["autonomous"], allowDynamicHostRegistration: true, ... })on abetterAuth()instance withgenerateId: "serial".client.connectAgent({ provider, mode: "autonomous", ... }).connectAgent()signs a host JWT with the defaultissand POSTs to/agent/register.Current vs. expected
Current, 500:
Expected: registration succeeds, or fails with a normal auth error, not an unhandled adapter exception. At minimum the
kidfallback infindHostByIdOrKidshould actually be reachable for a non-numericiss.