different-ai/openwork · error

OIDC discovery document is missing required endpoints.

Error message

OIDC discovery document is missing required endpoints.

What it means

After fetching the discovery document, the response body is parsed with oidcDiscoverySchema. If the JSON lacks the required endpoint fields (or has wrong types), safeParse fails and this Error is thrown, meaning the document exists but is not a usable OIDC discovery payload.

Source

Thrown at ee/apps/den-api/src/sso.ts:127

      authorizationEndpoint: input.authorizationEndpoint,
      tokenEndpoint: input.tokenEndpoint,
      jwksEndpoint: input.jwksEndpoint,
      userInfoEndpoint: input.userInfoEndpoint ?? undefined,
      tokenEndpointAuthentication: input.tokenEndpointAuthentication ?? undefined,
    }
  }

  const response = await fetch(getOidcDiscoveryUrl(input.issuer), {
    headers: { accept: "application/json" },
    signal: AbortSignal.timeout(10_000),
  })
  if (!response.ok) {
    throw new Error(`OIDC discovery failed with ${response.status}. Enter manual OIDC endpoints or enable skip discovery.`)
  }

  const parsed = oidcDiscoverySchema.safeParse(await response.json())
  if (!parsed.success) {
    throw new Error("OIDC discovery document is missing required endpoints.")
  }
  if (normalizeIssuer(parsed.data.issuer) !== normalizeIssuer(input.issuer)) {
    throw new Error("OIDC discovery issuer does not match the configured issuer.")
  }

  return {
    skipDiscovery: true,
    authorizationEndpoint: parsed.data.authorization_endpoint,
    tokenEndpoint: parsed.data.token_endpoint,
    jwksEndpoint: parsed.data.jwks_uri,
    userInfoEndpoint: parsed.data.userinfo_endpoint,
    tokenEndpointAuthentication: input.tokenEndpointAuthentication ?? undefined,
  }
}

async function getSsoProviderByProviderId(providerId: string) {
  const rows = await db
    .select()

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Confirm the IdP supports OpenID Connect discovery and its document includes authorization_endpoint, token_endpoint, and jwks_uri
  2. If it is plain OAuth2, enter the endpoints manually with skipDiscovery: true
  3. curl the discovery URL and inspect the JSON body for a proxy or error page

Example fix

// before
// discovery returns { issuer: '...', authorization_endpoint: '...' } // missing token/jwks
// after
{ issuer: 'https://idp.example.com', skipDiscovery: true, authorizationEndpoint: 'https://idp.example.com/authorize', tokenEndpoint: 'https://idp.example.com/token', jwksEndpoint: 'https://idp.example.com/.well-known/jwks.json' }
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(getOidcDiscoveryUrl(issuer), { headers: { accept: 'application/json' } })
const body = await res.json()
const required = ['authorization_endpoint', 'token_endpoint', 'jwks_uri'] as const
const missing = required.filter((k) => typeof body?.[k] !== 'string' || !body[k])
if (missing.length) throw new Error(`Discovery document missing: ${missing.join(', ')}`)

Type guard

function isOidcDiscoveryDocument(v: unknown): v is { authorization_endpoint: string; token_endpoint: string; jwks_uri: string } {
  const d = v as Record<string, unknown>
  return typeof d.authorization_endpoint === 'string' && typeof d.token_endpoint === 'string' && typeof d.jwks_uri === 'string'
}

Try / catch

try {
  await resolveOidcEndpoints(input)
} catch (e) {
  if (e instanceof Error && e.message === 'OIDC discovery document is missing required endpoints.') {
    // provider lacks OIDC metadata; switch to manual endpoints
  } else throw e
}

Prevention

When it happens

Trigger: Discovery endpoint returns 200 with JSON missing authorization_endpoint, token_endpoint, or jwks_uri fields, or returning a non-JSON body (HTML error page, OAuth 2.0-only metadata).

Common situations: Plain OAuth2 server without OpenID Connect discovery fields; IdP serving an HTML login/error page with 200; truncated or proxied response; wrong URL returning some other JSON API's output.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/d86163c7ebc864a0. Report an issue: GitHub.