@codefox-inc/oauth-provider 0.4.1 → 0.4.2

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.
Files changed (40) hide show
  1. package/README.md +28 -0
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +5 -1
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/component/clientManagement.d.ts.map +1 -1
  6. package/dist/component/clientManagement.js +9 -0
  7. package/dist/component/clientManagement.js.map +1 -1
  8. package/dist/component/handlers.d.ts +19 -1
  9. package/dist/component/handlers.d.ts.map +1 -1
  10. package/dist/component/handlers.js +76 -15
  11. package/dist/component/handlers.js.map +1 -1
  12. package/dist/component/mutations.d.ts +3 -1
  13. package/dist/component/mutations.d.ts.map +1 -1
  14. package/dist/component/mutations.js +113 -19
  15. package/dist/component/mutations.js.map +1 -1
  16. package/dist/component/queries.d.ts +7 -1
  17. package/dist/component/queries.d.ts.map +1 -1
  18. package/dist/component/queries.js +7 -1
  19. package/dist/component/queries.js.map +1 -1
  20. package/dist/component/schema.d.ts +7 -1
  21. package/dist/component/schema.d.ts.map +1 -1
  22. package/dist/component/schema.js +3 -0
  23. package/dist/component/schema.js.map +1 -1
  24. package/dist/lib/oauth.d.ts.map +1 -1
  25. package/dist/lib/oauth.js +26 -8
  26. package/dist/lib/oauth.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/client/__tests__/oauth-provider.test.ts +15 -0
  29. package/src/client/index.ts +6 -1
  30. package/src/component/__tests__/bugs.test.ts +1001 -0
  31. package/src/component/__tests__/handlers-protocol.test.ts +148 -0
  32. package/src/component/__tests__/oauth.test.ts +18 -15
  33. package/src/component/__tests__/rfc-compliance.test.ts +233 -0
  34. package/src/component/clientManagement.ts +11 -0
  35. package/src/component/handlers.ts +116 -18
  36. package/src/component/mutations.ts +159 -17
  37. package/src/component/queries.ts +6 -1
  38. package/src/component/schema.ts +3 -0
  39. package/src/lib/__tests__/oauth-jwt.test.ts +1 -1
  40. package/src/lib/oauth.ts +28 -8
package/src/lib/oauth.ts CHANGED
@@ -135,6 +135,8 @@ export async function getJWKS(config: OAuthConfig): Promise<{ keys: JoseJWK[] }>
135
135
  return {
136
136
  ...publicKey,
137
137
  kid: publicKey.kid ?? keyId,
138
+ use: publicKey.use ?? "sig",
139
+ alg: publicKey.alg ?? "RS256",
138
140
  };
139
141
  });
140
142
 
@@ -145,13 +147,29 @@ function ensureKidOnJwksKeys(keys: JoseJWK[], keyId: string): JoseJWK[] {
145
147
  return keys.map((key) => ({
146
148
  ...key,
147
149
  kid: key.kid ?? keyId,
150
+ use: key.use ?? "sig",
151
+ alg: key.alg ?? "RS256",
148
152
  }));
149
153
  }
150
154
 
151
155
  export function getSigningKeyId(config: OAuthConfig): string {
152
- if (config.keyId) return config.keyId;
156
+ let jwks: { keys?: JoseJWK[] };
157
+ try {
158
+ jwks = JSON.parse(config.jwks) as { keys?: JoseJWK[] };
159
+ } catch {
160
+ return config.keyId ?? DEFAULT_KEY_ID;
161
+ }
162
+
163
+ if (config.keyId) {
164
+ const hasExplicitKids = jwks.keys?.some((key) => typeof key.kid === "string" && key.kid.length > 0) ?? false;
165
+ const publishesKeyId = jwks.keys?.some((key) => key.kid === config.keyId) ?? false;
166
+ if (hasExplicitKids && !publishesKeyId) {
167
+ throw new Error(`Configured keyId "${config.keyId}" is not present in JWKS`);
168
+ }
169
+ return config.keyId;
170
+ }
171
+
153
172
  try {
154
- const jwks = JSON.parse(config.jwks) as { keys?: JoseJWK[] };
155
173
  const kid = jwks.keys?.[0]?.kid;
156
174
  if (typeof kid === "string" && kid.length > 0) {
157
175
  return kid;
@@ -223,16 +241,18 @@ export async function verifyAccessToken(
223
241
  async function getVerificationKey(
224
242
  config: OAuthConfig
225
243
  ): Promise<ReturnType<typeof createLocalJWKSet>> {
226
- const cached = jwksKeyCache.get(config.jwks);
244
+ const keyId = getSigningKeyId(config);
245
+ const cacheKey = `${config.jwks}:${keyId}`;
246
+ const cached = jwksKeyCache.get(cacheKey);
227
247
  if (cached) return cached;
228
248
 
229
249
  const jwks = JSON.parse(config.jwks) as { keys: JoseJWK[] };
230
250
  if (!jwks.keys?.length) {
231
251
  throw new Error("jwks must include at least one key");
232
252
  }
233
- const normalized = { keys: ensureKidOnJwksKeys(jwks.keys, getSigningKeyId(config)) };
253
+ const normalized = { keys: ensureKidOnJwksKeys(jwks.keys, keyId) };
234
254
  const localJwks = createLocalJWKSet(normalized);
235
- jwksKeyCache.set(config.jwks, localJwks);
255
+ jwksKeyCache.set(cacheKey, localJwks);
236
256
  return localJwks;
237
257
  }
238
258
 
@@ -254,16 +274,16 @@ export function generateCode(length = 32): string {
254
274
  * Generate a cryptographically strong Client Secret (hex string)
255
275
  */
256
276
  export function generateClientSecret(length = 64): string {
257
- const array = new Uint8Array(length);
277
+ const array = new Uint8Array(Math.ceil(length / 2));
258
278
  crypto.getRandomValues(array);
259
- return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
279
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('').slice(0, length);
260
280
  }
261
281
 
262
282
  /**
263
283
  * Get Issuer URL helper
264
284
  */
265
285
  export function getIssuerUrl(config: OAuthConfig): string {
266
- const issuerBaseUrl = config.convexSiteUrl ?? config.siteUrl;
286
+ const issuerBaseUrl = (config.convexSiteUrl ?? config.siteUrl).replace(/\/+$/, "");
267
287
  const prefix = normalizePrefix(config.prefix);
268
288
  return issuerBaseUrl + prefix;
269
289
  }