Skip to content

verifyIdArgs

verifyIdArgs<TSource, TContext, TArgs>(codecs, resolver): GraphQLFieldResolver<TSource, TContext, TArgs>

Defined in: src/adapters/graphql.ts:106

Wraps a GraphQL field resolver so the named ID arguments are authenticated before the resolver body runs.

GraphQL scalar coercers (parseValue/parseLiteral) are synchronous and cannot await the HMAC check, so idScalar cannot verify a Signed Timestamp tag itself. verifyIdArgs performs that async verification one layer out, at the resolver. For each argName → codec entry it calls codec.safeVerify(args[argName]); a forged or tampered tag throws GraphQLError before the wrapped resolver runs. A null/undefined arg is skipped (nullable/absent args pass through). Present args’ safeVerify calls all fire concurrently; the reported failure — if any — is the first one in map order, not the first to settle.

Both the Signed Timestamp codec and the Wrapped key codec satisfy IdVerifiableCodec; passing any other codec is a compile-time type error. Verification covers top-level args only — IDs nested inside input objects are not reached.

Pair with idScalar. The wrapper checks the tag but returns args unchanged — it does not substitute the canonical id from safeVerify. Front each verified arg with an idScalar built from the same codec so parseValue/parseLiteral canonicalises the value (case, Crockford aliases) before the resolver runs; on a plain GraphQLString arg a non-canonical variant would verify yet reach the resolver un-normalised.

Only listed args are verified, and keys must match the field’s argument names exactly. On the first invocation for each schema coordinate (ParentType.fieldName) the wrapper resolves the field’s declared argument names from info and throws a GraphQLError if any codec-map key does not match a declared argument — hardening so a typo cannot silently disable verification. Subsequent invocations on the same coordinate use the cached result. If the field cannot be found in parentType.getFields(), the arg-name guard is skipped for that coordinate — this path is not fail-closed for unknown fields — but per-ID safeVerify still runs on every invocation regardless.

TSource

TContext

TArgs extends Record<string, unknown> = Record<string, unknown>

{ [K in string | number | symbol]?: IdVerifiableCodec<string> }

GraphQLFieldResolver<TSource, TContext, TArgs>

GraphQLFieldResolver<TSource, TContext, TArgs>

import { verifyIdArgs } from "@smonn/ids/graphql";
const resolve = verifyIdArgs({ userId: usr }, (_root, args, ctx) => {
// args.userId is an authenticated Id<"usr">
return ctx.loadUser(args.userId);
});