@codefox-inc/oauth-provider 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,16 @@ OAuth 2.1 / OpenID Connect Provider implemented as a Convex component.
|
|
|
6
6
|
> Built for [Convex Auth](https://labs.convex.dev/auth) which is currently in **Beta**.
|
|
7
7
|
> Expect breaking changes. Production use at your own risk.
|
|
8
8
|
|
|
9
|
+
## Why?
|
|
10
|
+
|
|
11
|
+
Most MCP clients (like Claude Code or ChatGPT) require your app to be an OAuth provider. If you want to connect your Convex app to MCP clients, you need to implement OAuth 2.1.
|
|
12
|
+
|
|
13
|
+
This component turns your Convex Auth app into a fully compliant OAuth 2.1 provider, so you can:
|
|
14
|
+
- Connect to MCP clients out of the box
|
|
15
|
+
- Let clients register automatically via Dynamic Client Registration
|
|
16
|
+
- Let users control what permissions each app gets
|
|
17
|
+
- Focus on your app, not OAuth complexity
|
|
18
|
+
|
|
9
19
|
## Installation
|
|
10
20
|
|
|
11
21
|
```bash
|
|
@@ -113,13 +123,7 @@ if (isOAuthToken(identity)) {
|
|
|
113
123
|
|
|
114
124
|
#### With Convex Auth (Recommended)
|
|
115
125
|
|
|
116
|
-
If you're using [Convex Auth](https://labs.convex.dev/auth),
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
npx @convex-dev/auth
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
This automatically sets `JWT_PRIVATE_KEY`, `JWKS`, and `SITE_URL`. The OAuth Provider will use these by default.
|
|
126
|
+
If you're using [Convex Auth](https://labs.convex.dev/auth), you already have the required environment variables configured (`JWT_PRIVATE_KEY`, `JWKS`, `SITE_URL`).
|
|
123
127
|
|
|
124
128
|
#### Without Convex Auth
|
|
125
129
|
|
|
@@ -145,8 +149,8 @@ const privateKey = fs.readFileSync('private.pem', 'utf8');
|
|
|
145
149
|
Set environment variables:
|
|
146
150
|
|
|
147
151
|
```bash
|
|
148
|
-
npx convex env set
|
|
149
|
-
npx convex env set
|
|
152
|
+
npx convex env set JWT_PRIVATE_KEY "-----BEGIN RSA PRIVATE KEY-----\n..."
|
|
153
|
+
npx convex env set JWKS '{"keys":[...]}'
|
|
150
154
|
npx convex env set SITE_URL "https://your-app.example.com"
|
|
151
155
|
```
|
|
152
156
|
|
|
@@ -178,23 +182,15 @@ import { api } from "./_generated/api";
|
|
|
178
182
|
const http = httpRouter();
|
|
179
183
|
|
|
180
184
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
181
|
-
privateKey: process.env.
|
|
182
|
-
jwks: process.env.
|
|
185
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
186
|
+
jwks: process.env.JWKS!,
|
|
183
187
|
siteUrl: process.env.SITE_URL!,
|
|
184
|
-
convexSiteUrl: process.env.CONVEX_SITE_URL,
|
|
185
|
-
// OPTIONAL: OAuth endpoint prefix (default: "/oauth")
|
|
186
|
-
// Note: This must match the route prefix used below.
|
|
187
|
-
// prefix: "/oauth",
|
|
188
|
-
allowedScopes: ["openid", "profile", "email", "offline_access"],
|
|
189
188
|
|
|
190
189
|
// REQUIRED: Authenticate user for authorization endpoint
|
|
191
190
|
getUserId: async (ctx, request) => {
|
|
192
191
|
const identity = await ctx.auth.getUserIdentity();
|
|
193
192
|
return identity?.subject ?? null;
|
|
194
193
|
},
|
|
195
|
-
|
|
196
|
-
// OPTIONAL: Enable dynamic client registration (default: false)
|
|
197
|
-
allowDynamicClientRegistration: false,
|
|
198
194
|
});
|
|
199
195
|
|
|
200
196
|
// Register all OAuth routes automatically
|
|
@@ -230,14 +226,9 @@ import { components } from "./_generated/api";
|
|
|
230
226
|
const http = httpRouter();
|
|
231
227
|
|
|
232
228
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
233
|
-
privateKey: process.env.
|
|
234
|
-
jwks: process.env.
|
|
229
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
230
|
+
jwks: process.env.JWKS!,
|
|
235
231
|
siteUrl: process.env.SITE_URL!,
|
|
236
|
-
convexSiteUrl: process.env.CONVEX_SITE_URL,
|
|
237
|
-
// OPTIONAL: OAuth endpoint prefix (default: "/oauth")
|
|
238
|
-
// Note: This must match the route prefix used below.
|
|
239
|
-
// prefix: "/oauth",
|
|
240
|
-
allowedScopes: ["openid", "profile", "email", "offline_access"],
|
|
241
232
|
|
|
242
233
|
// REQUIRED: Authenticate user for authorization endpoint
|
|
243
234
|
getUserId: async (ctx, request) => {
|
|
@@ -335,8 +326,8 @@ export const registerOAuthClient = mutation({
|
|
|
335
326
|
if (!identity) throw new Error("Unauthorized");
|
|
336
327
|
|
|
337
328
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
338
|
-
privateKey: process.env.
|
|
339
|
-
jwks: process.env.
|
|
329
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
330
|
+
jwks: process.env.JWKS!,
|
|
340
331
|
siteUrl: process.env.SITE_URL!,
|
|
341
332
|
});
|
|
342
333
|
|
|
@@ -404,8 +395,8 @@ export const approveAuthorization = mutation({
|
|
|
404
395
|
if (!identity) throw new Error("Not authenticated");
|
|
405
396
|
|
|
406
397
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
407
|
-
privateKey: process.env.
|
|
408
|
-
jwks: process.env.
|
|
398
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
399
|
+
jwks: process.env.JWKS!,
|
|
409
400
|
siteUrl: process.env.SITE_URL!,
|
|
410
401
|
});
|
|
411
402
|
|
|
@@ -440,8 +431,8 @@ export const listAuthorizedApps = query({
|
|
|
440
431
|
if (!identity) return [];
|
|
441
432
|
|
|
442
433
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
443
|
-
privateKey: process.env.
|
|
444
|
-
jwks: process.env.
|
|
434
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
435
|
+
jwks: process.env.JWKS!,
|
|
445
436
|
siteUrl: process.env.SITE_URL!,
|
|
446
437
|
});
|
|
447
438
|
|
|
@@ -463,8 +454,8 @@ export const revokeApp = mutation({
|
|
|
463
454
|
if (!identity) throw new Error("Not authenticated");
|
|
464
455
|
|
|
465
456
|
const oauthProvider = new OAuthProvider(components.oauthProvider, {
|
|
466
|
-
privateKey: process.env.
|
|
467
|
-
jwks: process.env.
|
|
457
|
+
privateKey: process.env.JWT_PRIVATE_KEY!,
|
|
458
|
+
jwks: process.env.JWKS!,
|
|
468
459
|
siteUrl: process.env.SITE_URL!,
|
|
469
460
|
});
|
|
470
461
|
|
|
@@ -540,7 +531,7 @@ import { verifyAccessToken } from "@codefox-inc/oauth-provider";
|
|
|
540
531
|
const payload = await verifyAccessToken(
|
|
541
532
|
token,
|
|
542
533
|
{
|
|
543
|
-
jwks: process.env.
|
|
534
|
+
jwks: process.env.JWKS!,
|
|
544
535
|
siteUrl: process.env.SITE_URL!,
|
|
545
536
|
},
|
|
546
537
|
issuerUrl
|
|
@@ -551,16 +542,6 @@ console.log("Scopes:", payload.scp);
|
|
|
551
542
|
console.log("Client ID:", payload.cid);
|
|
552
543
|
```
|
|
553
544
|
|
|
554
|
-
## Environment Variables Reference
|
|
555
|
-
|
|
556
|
-
| Variable | Required | Description |
|
|
557
|
-
|----------|----------|-------------|
|
|
558
|
-
| `OAUTH_PRIVATE_KEY` | Yes | RSA private key (PEM format) |
|
|
559
|
-
| `OAUTH_JWKS` | Yes | JSON Web Key Set for token verification |
|
|
560
|
-
| `SITE_URL` | Yes | Your application's public URL |
|
|
561
|
-
| `CONVEX_SITE_URL` | No | Convex deployment URL (used as issuer if set) |
|
|
562
|
-
| `ALLOWED_ORIGINS` | No | Comma-separated CORS origins |
|
|
563
|
-
|
|
564
545
|
## Testing
|
|
565
546
|
|
|
566
547
|
```bash
|