Skip to content

Audience validation fails for RFC 8707 resource indicators with path #108

@bokhi

Description

@bokhi

Audience validation fails for RFC 8707 resource indicators with path

Problem

When using RFC 8707 Resource Indicators with a path component (e.g., resource=https://example.com/api), token validation fails with:

invalid_token: Token audience does not match resource server

This breaks OAuth flows for services like ChatGPT custom connectors, which send resource indicators as full URLs with paths.

Root Cause

In handleApiRequest, the resourceServer is computed using only the origin:

const resourceServer = `${requestUrl.protocol}//${requestUrl.host}`;

However, RFC 8707 recommends using full URLs with paths for resource indicators. The audienceMatches function performs strict equality (===), so:

  • Token audience: https://example.com/api (from authorization request's resource parameter)
  • Resource server: https://example.com (computed from request URL)
  • Result: "https://example.com/api" !== "https://example.com" → validation fails

Affected Code

Two locations in handleApiRequest:

Line ~1017 (internal token validation):

const resourceServer = `${requestUrl.protocol}//${requestUrl.host}`;
const audiences = Array.isArray(tokenData.audience) ? tokenData.audience : [tokenData.audience];
const matches = audiences.some((aud) => audienceMatches(resourceServer, aud));

Line ~1038 (external token validation):

const resourceServer = `${requestUrl.protocol}//${requestUrl.host}`;
const audiences = Array.isArray(ext.audience) ? ext.audience : [ext.audience];
const matches = audiences.some((aud) => audienceMatches(resourceServer, aud));

audienceMatches implementation (line ~1143):

function audienceMatches(resourceServerUrl, audienceValue) {
  return resourceServerUrl === audienceValue;
}

Minimal Reproduction

  1. Configure OAuth provider with apiRoute: '/api'
  2. Client sends authorization request with resource=https://server.example.com/api
  3. Token is issued with aud: "https://server.example.com/api"
  4. Client sends API request to https://server.example.com/api with access token
  5. Validation fails: resourceServer="https://server.example.com" doesn't match aud="https://server.example.com/api"

Expected Behavior

Token validation should succeed when the resource indicator includes the path component, as recommended by RFC 8707.

Proposed Fix

Include the pathname in resourceServer computation:

const resourceServer = `${requestUrl.protocol}//${requestUrl.host}${requestUrl.pathname}`;

This ensures:

  • resourceServer = "https://example.com/api"
  • Token audience = "https://example.com/api"
  • Validation succeeds: "https://example.com/api" === "https://example.com/api"

References

Workaround

Vendor the library and apply the fix locally:

// In oauth-provider.ts handleApiRequest method
const resourceServer = `${requestUrl.protocol}//${requestUrl.host}${requestUrl.pathname}`;

Environment

  • Package: @cloudflare/workers-oauth-provider v0.1.0
  • Runtime: Cloudflare Workers
  • OAuth client: ChatGPT custom connector (and other RFC 8707-compliant clients)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions