Discovery Shim

A discovery service for OAuth 2.0 Protected Resource Metadata (RFC 9728) until providers implement native support.

What is this?

RFC 9728 allows OAuth protected resources to publish metadata about their capabilities. Most APIs haven't implemented this yet. We provide that metadata for them.

Our goal is to make ourselves obsolete. Once providers implement native PRM support, we automatically proxy their responses and notify clients to switch.

We provide fallback metadata for well-known APIs including Google, GitHub, Microsoft, and others.

Usage

Always try the native endpoint first. Only use our fallback if the native endpoint returns 404:

# First, try the native endpoint
curl https://api.github.com/.well-known/oauth-protected-resource

# If that returns 404, use our discovery endpoint
curl https://fallback.auth101.dev/oauth-protected-resource/api.github.com

If you see X-PRM-Native-URL in the response headers, the resource now supports native PRM. Update your client to use the native endpoint directly.

Client Library

npm install prm-shim

import { fetchProtectedResourceMetadata } from 'prm-shim';

const metadata = await fetchProtectedResourceMetadata('https://api.github.com');

OAuth Library Integration

Use Discovery Shim as a fallback with popular OAuth libraries that support discovery:

openid-client

import { Issuer } from 'openid-client';

// Custom fetch that tries native endpoint first, falls back to Discovery Shim
const customFetch = async (url, options) => {
  // Only intercept PRM discovery requests
  if (url.includes('/.well-known/oauth-protected-resource')) {
    const nativeResponse = await fetch(url, options);
    
    if (nativeResponse.status === 404) {
      // Extract the resource URL and use our shim
      const resourceUrl = new URL(url).origin;
      const shimUrl = `fallback.auth101.dev/oauth-protected-resource/${resourceUrl.replace(/^https?:\/\//, '')}`;
      return fetch(shimUrl, options);
    }
    
    return nativeResponse;
  }
  
  // For all other requests, use default fetch
  return fetch(url, options);
};

// Configure the issuer with custom fetch
const issuer = await Issuer.discover('https://accounts.google.com', {
  customFetch
});

// Now use the client as normal
const client = new issuer.Client({
  client_id: 'your_client_id',
  client_secret: 'your_client_secret',
  redirect_uris: ['http://localhost:3000/callback'],
  response_types: ['code'],
});

oauth4webapi

import * as oauth from 'oauth4webapi';

// Create a custom request function for PRM discovery
async function discoveryRequest(url) {
  // Try native endpoint first
  const nativeResponse = await fetch(url.href);
  
  if (nativeResponse.status === 404 && url.pathname.includes('oauth-protected-resource')) {
    // Use Discovery Shim as fallback
    const resourceUrl = url.origin;
    const shimUrl = new URL(`fallback.auth101.dev/oauth-protected-resource/${resourceUrl.replace(/^https?:\/\//, '')}`);
    return fetch(shimUrl.href);
  }
  
  return nativeResponse;
}

// Discover authorization server metadata
const issuer = new URL('https://accounts.google.com');
const as = await oauth.processDiscoveryResponse(
  issuer,
  await discoveryRequest(new URL('/.well-known/openid-configuration', issuer))
);

// Discover protected resource metadata (if needed)
const resourceUrl = new URL('https://www.googleapis.com/gmail/v1');
const prmResponse = await discoveryRequest(
  new URL('/.well-known/oauth-protected-resource', resourceUrl)
);

// Process the PRM response
if (prmResponse.ok) {
  const prm = await prmResponse.json();
  console.log('Resource supports scopes:', prm.scopes_supported);
}

Important: Always check for the X-PRM-Native-URLheader in responses. If present, update your configuration to use the native endpoint directly.