nextauthjs/next-auth · error · InvalidProvider

Provider must be WebAuthn

Error message

Provider must be WebAuthn

What it means

Auth.js throws InvalidProvider when the provider passed through assertInternalOptionsWebAuthn is missing or its `type` is not "webauthn". This guard exists so the rest of the WebAuthn code can rely on a narrowed InternalOptionsWebAuthn type with a correctly typed provider. It indicates the provider routing/wiring is wrong.

Source

Thrown at packages/core/src/lib/utils/webauthn-utils.ts:492

    excludeCredentials: authenticators?.map((a) => ({
      id: fromBase64(a.credentialID),
      type: "public-key",
      transports: stringToTransports(a.transports),
    })),
  })
}

export function assertInternalOptionsWebAuthn(
  options: InternalOptions
): InternalOptionsWebAuthn {
  const { provider, adapter } = options

  // Adapter is required for WebAuthn
  if (!adapter)
    throw new MissingAdapter("An adapter is required for the WebAuthn provider")
  // Provider must be WebAuthn
  if (!provider || provider.type !== "webauthn") {
    throw new InvalidProvider("Provider must be WebAuthn")
  }
  // Narrow the options type for typed usage later
  return { ...options, provider, adapter }
}

function fromAdapterAuthenticator(
  authenticator: AdapterAuthenticator
): InternalAuthenticator {
  return {
    ...authenticator,
    credentialDeviceType:
      authenticator.credentialDeviceType as InternalAuthenticator["credentialDeviceType"],
    transports: stringToTransports(authenticator.transports),
    credentialID: fromBase64(authenticator.credentialID),
    credentialPublicKey: fromBase64(authenticator.credentialPublicKey),
  }
}

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Ensure the provider used for WebAuthn routes is created with `type: "webauthn"` (e.g. via the official Passkey/WebAuthn provider helper).
  2. Check that the provider id in the route/URL matches the id of the registered WebAuthn provider.
  3. If using a custom provider object, add the required `type: "webauthn"` field and required WebAuthn options.
  4. Verify you are not accidentally passing the whole providers array element of a different kind into the WebAuthn path.

Example fix

// before
const provider = { id: "passkey", name: "Passkey" } // missing type
// after
const provider = {
  id: "passkey",
  name: "Passkey",
  type: "webauthn",
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!provider || provider.type !== "webauthn") {
  throw new Error(`Expected a WebAuthn provider, got: ${provider?.type ?? "undefined"}`)
}

Type guard

function isWebAuthnProvider(
  p: any
): p is { type: "webauthn"; id: string } & Record<string, unknown> {
  return !!p && p.type === "webauthn"
}

Try / catch

try {
  const opts = narrowOptions(options)
} catch (e) {
  if (e instanceof InvalidProvider) {
    throw new ConfigError("Route reached WebAuthn handler with a non-webauthn provider — check provider id/type wiring")
  }
  throw e
}

Prevention

When it happens

Trigger: localOptions or narrowOptions receiving options where `provider` is undefined, or where provider.type is something other than "webauthn" (e.g. "credentials", "oauth", "email") while the WebAuthn assertion path is being exercised.

Common situations: Registering the WebAuthn handlers under the wrong provider id in route config; passing a custom provider object that forgot `type: "webauthn"`; a refactor renaming/moving providers so the lookup by id returns the wrong or no provider; copy-pasting a provider config and not changing its type.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/ab9fedce571824f1. Report an issue: GitHub.