x-oauth-provider
v0.0.25
Published
[](https://uithub.com/janwilmake/x-oauth-provider/tree/main/README.md) [](https://letmeprompt.com?q=https://uithub.
Readme
X OAuth Provider
This X OAuth Provider uses the client's domain name as the client_id and automatically derives it from the redirect_uri (e.g., https://example.com/callback means client_id must equal example.com), eliminating the need for client registration while maintaining security through domain validation.
Why
- X Free or Basic just allows creating a single client
- A single client can have up to just 10 callback URLs, which limits you in how many apps you can create
- To bypass this problem, you can use the
simplerauth-clientto create unlimited X Authed Apps (even without a client)
Key Features:
- Makes your worker a oauth provider
- No client registration required - use any domain as client_id
- MCP compatible Authorization including dynamic client registraiton fully supported
- Uses DORM to expose admin panel with all users in aggregate (readonly) without compromising on performance (each user gets their own DO as source of truth)
- Users that are already logged in won't be redirected to X Again, even from other clients.
Run locally
wrangler dev --env localhost (needed in order to have localhost with correct port as hostname)
3 ways to use it
- Hosted - Use directly from https://login.wilmake.com
- Internal - Use directly in your cloudflare worker
- Central - Host as a separate worker and use as a central "OAuth Hub" for all your x-oauthed apps
Simplest Setup: Hosted
The Simple Hosted Setup is explained in the root readme
Setup: Internal and Central
- Installation:
npm i x-oauth-providerSet environment variables:
X_CLIENT_ID: Your X OAuth app client IDX_CLIENT_SECRET: Your X OAuth app client secretADMIN_X_USERNAME: Your X Username that needs access to/admin
Add Durable Object binding to your
wrangler.toml:
[[durable_objects.bindings]]
name = "UserDO"
class_name = "UserDO"
[[migrations]]
new_sqlite_classes = ["UserDO"]
tag = "v1"Usage Examples
Simple Enforced Authentication
import { UserDO, withSimplerAuth } from "x-oauth-provider";
export { UserDO };
export default {
fetch: withSimplerAuth(
async (request, env, ctx) => {
return new Response(
`<html><body>
<h1>X OAuth Demo</h1>
<p>Welcome, ${ctx.user.name || ctx.user.username}!</p>
<img src="${
ctx.user.profile_image_url || "/default-avatar.png"
}" alt="Avatar" width="400" height="400" style="border-radius:200px;">
<p>Username: @${ctx.user.username}</p>
<p>Verified: ${ctx.user.verified ? "✓" : "✗"}</p>
<a href="/logout">Logout</a>
</body></html>`,
{ headers: { "Content-Type": "text/html;charset=utf8" } }
);
},
{ isLoginRequired: true }
),
};Step-by-Step Integration
- Authorization Request: Redirect users to your provider's authorize endpoint:
https://your-provider.com/authorize?client_id=CLIENT_DOMAIN&redirect_uri=REDIRECT_URI&response_type=code&state=RANDOM_STATEParameters:
client_id: Your client's domain (e.g.,example.com)redirect_uri: Where to redirect after auth (must be HTTPS and on same domain as client_id)response_type: Must becodestate: Random string for CSRF protection
- Handle Authorization Callback: After user authorizes, they'll be redirected to your
redirect_uriwith:
https://your-app.com/callback?code=AUTH_CODE&state=YOUR_STATE- Exchange Code for Token: Make a POST request to exchange the authorization code:
const response = await fetch("https://your-provider.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: "AUTH_CODE_FROM_CALLBACK",
client_id: "your-domain.com",
redirect_uri: "https://your-domain.com/callback",
}),
});
const { access_token } = await response.json();- Get User Information: Use the
/meendpoint to get X user data:
const userResponse = await fetch("https://your-provider.com/me", {
headers: { Authorization: `Bearer ${access_token}` },
});
const userData = await userResponse.json();
console.log(userData.data); // X user object with id, name, username, etc.Available User Data
The /me endpoint returns X user information in this format:
{
"data": {
"id": "123456789",
"name": "John Doe",
"username": "johndoe",
"profile_image_url": "https://pbs.twimg.com/profile_images/.../photo.jpg",
"verified": false
}
}API Routes
Your OAuth provider exposes these endpoints:
GET /admin- Readonly DB Access to admin aggregate DBGET /authorize- OAuth authorization endpointPOST /token- OAuth token endpointGET /callback- X OAuth callback handlerGET /me- Get authenticated user informationGET /logout- Logout and clear sessionGET /.well-known/oauth-authorization-server- OAuth server metadata (MCP required)GET /.well-known/oauth-protected-resource- Protected resource metadata (MCP required)
Security Features
- Domain Validation: Client domains are validated -
client_idmust be a valid domain - HTTPS Enforcement: Redirect URIs must be HTTPS and on the same domain as
client_id - CSRF Protection: State parameter validation prevents cross-site request forgery
- Token Expiration: Authorization codes expire after 10 minutes
- Secure Storage: User data encrypted in Durable Objects
- PKCE Support: Proof Key for Code Exchange for enhanced security
MCP Compliance
This implementation is fully compliant with the Model Context Protocol (MCP) OAuth 2.0 requirements, including:
- RFC 8414 OAuth 2.0 Authorization Server Metadata
- RFC 9728 OAuth 2.0 Protected Resource Metadata
- Proper WWW-Authenticate headers with login URLs
- Bearer token support in Authorization headers
- Resource parameter support for audience validation
Notes
This provider is designed to be agent-friendly while maintaining security. When authentication is required, it provides multiple indicators for where to login:
{
status: isBrowser ? 302 : 401,
headers: {
Location: loginUrl,
"X-Login-URL": loginUrl,
"WWW-Authenticate": `Bearer realm="main", login_url="${loginUrl}", resource_metadata="${resourceMetadataUrl}"`,
},
}Agents can either attempt automated login or direct users to the login URL for credential retrieval.
