diff --git a/.changeset/config.json b/.changeset/config.json index d5dfa3d2..93cff2ef 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,7 +2,7 @@ "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json", "changelog": "@changesets/cli/changelog", "commit": "./commit.cjs", - "fixed": [["@openauthjs/openauth"]], + "fixed": [["@kagii/openauth"]], "linked": [], "access": "public", "baseBranch": "master", diff --git a/.changeset/popular-geese-reply.md b/.changeset/popular-geese-reply.md index d8d3f03c..17124071 100644 --- a/.changeset/popular-geese-reply.md +++ b/.changeset/popular-geese-reply.md @@ -1,5 +1,5 @@ --- -"@openauthjs/openauth": patch +"@kagii/openauth": patch --- update google icon to comply with branding guidelines diff --git a/.changeset/stupid-boats-play.md b/.changeset/stupid-boats-play.md index d31620d5..1c76f962 100644 --- a/.changeset/stupid-boats-play.md +++ b/.changeset/stupid-boats-play.md @@ -1,5 +1,5 @@ --- -"@openauthjs/openauth": patch +"@kagii/openauth": patch --- allow auth style autodetection diff --git a/.changeset/ten-pans-invent.md b/.changeset/ten-pans-invent.md index 3f3c0030..0d1a7db8 100644 --- a/.changeset/ten-pans-invent.md +++ b/.changeset/ten-pans-invent.md @@ -1,5 +1,5 @@ --- -"@openauthjs/openauth": patch +"@kagii/openauth": patch --- add linkedin adapter diff --git a/README.md b/README.md index 1014ec89..6d65e644 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,16 @@

Discord - npm - Build status + npm + Build status

--- +> Community-maintained fork of OpenAuth, published as `@kagii/openauth`. +> This fork keeps the upstream MIT license, carries forward unmerged fixes like the eventual-consistency key generation fix from `#323`, and is not an official SST/anomalyco release. +> Upstream source remains `sst/openauth` and `anomalyco/openauth`; this package exists so the project can keep shipping fixes in the open. + [OpenAuth](https://openauth.js.org) is a standards-based auth provider for web apps, mobile apps, single pages apps, APIs, or 3rd party clients. It is currently in beta. - **Universal**: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform. @@ -30,7 +34,13 @@ ## Quick Start -If you just want to get started as fast as possible you can jump straight into the [code examples](https://github.com/toolbeam/openauth/tree/master/examples) folder and copy paste away. There are also [SST components](https://sst.dev/docs/component/aws/auth) for deploying everything OpenAuth needs. +If you just want to get started as fast as possible you can jump straight into the [code examples](https://github.com/kagii-dev/openauth/tree/master/examples) folder and copy paste away. There are also [SST components](https://sst.dev/docs/component/aws/auth) for deploying everything OpenAuth needs. + +Install this fork with: + +```bash +npm install @kagii/openauth +``` ## Approach @@ -56,10 +66,10 @@ We'll show how to deploy the auth server and then a sample app that uses it. ### Auth server -Start by importing the `issuer` function from the `@openauthjs/openauth` package. +Start by importing the `issuer` function from the `@kagii/openauth` package. ```ts -import { issuer } from "@openauthjs/openauth" +import { issuer } from "@kagii/openauth" ``` OpenAuth is built on top of [Hono](https://github.com/honojs/hono) which is a minimal web framework that can run anywhere. The `issuer` function creates a Hono app with all of the auth server implemented that you can then deploy to AWS Lambda, Cloudflare Workers, or in a container running under Node.js or Bun. @@ -78,7 +88,7 @@ const app = issuer({ First we need to define some providers that are enabled - these are either third party identity providers like Google, GitHub, etc or built in flows like email/password or pin code. You can also implement your own. Let's try the GitHub provider. ```ts -import { GithubProvider } from "@openauthjs/openauth/provider/github" +import { GithubProvider } from "@kagii/openauth/provider/github" const app = issuer({ providers: { @@ -95,7 +105,7 @@ const app = issuer({ Providers take some configuration - since this is a third party identity provider there is no UI to worry about and all it needs is a client ID, secret and some scopes. Let's add the password provider which is a bit more complicated. ```ts -import { PasswordProvider } from "@openauthjs/openauth/provider/password" +import { PasswordProvider } from "@kagii/openauth/provider/password" const app = issuer({ providers: { @@ -109,8 +119,8 @@ const app = issuer({ The password provider is quite complicated as username/password involve a lot of flows so there are a lot of callbacks to implement. However you can opt into the default UI which has all of this already implemented for you. The only thing you have to specify is how to send a code for forgot password/email verification. In this case we'll log the code but you would send this over email. ```ts -import { PasswordProvider } from "@openauthjs/openauth/provider/password" -import { PasswordUI } from "@openauthjs/openauth/ui/password" +import { PasswordProvider } from "@kagii/openauth/provider/password" +import { PasswordUI } from "@kagii/openauth/ui/password" const app = issuer({ providers: { @@ -184,7 +194,7 @@ Note all of this is typesafe - based on the configured providers you will receiv Next we have the `storage` field which defines where things like refresh tokens and password hashes are stored. If on AWS we recommend DynamoDB, if on Cloudflare we recommend Cloudflare KV. We also have a MemoryStore used for testing. ```ts -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" +import { MemoryStorage } from "@kagii/openauth/storage/memory" const app = issuer({ providers: { ... }, @@ -219,7 +229,7 @@ You now have a centralized auth server. Test it out by visiting `/.well-known/oa Since this is a standard OAuth server you can use any libraries for OAuth and it will work. OpenAuth does provide some light tooling for this although even a manual flow is pretty simple. You can create a client like this: ```ts -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" const client = createClient({ clientID: "my-client", diff --git a/examples/client/astro/package.json b/examples/client/astro/package.json index 3ef2bdeb..eeb11cf9 100644 --- a/examples/client/astro/package.json +++ b/examples/client/astro/package.json @@ -9,7 +9,7 @@ "astro": "astro" }, "dependencies": { - "@openauthjs/openauth": "workspace:*", + "@kagii/openauth": "workspace:*", "astro": "5.0.2" } } diff --git a/examples/client/astro/src/auth.ts b/examples/client/astro/src/auth.ts index 5ff06822..b3db5f59 100644 --- a/examples/client/astro/src/auth.ts +++ b/examples/client/astro/src/auth.ts @@ -1,4 +1,4 @@ -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import type { APIContext } from "astro" export { subjects } from "../../../subjects" diff --git a/examples/client/astro/src/env.d.ts b/examples/client/astro/src/env.d.ts index 16e6269e..6221c8c2 100644 --- a/examples/client/astro/src/env.d.ts +++ b/examples/client/astro/src/env.d.ts @@ -1,4 +1,4 @@ -import type { SubjectPayload } from "@openauthjs/openauth/subject" +import type { SubjectPayload } from "@kagii/openauth/subject" import { subjects } from "./auth" declare global { diff --git a/examples/client/cloudflare-api/api.ts b/examples/client/cloudflare-api/api.ts index 347856fb..0c0ccc11 100644 --- a/examples/client/cloudflare-api/api.ts +++ b/examples/client/cloudflare-api/api.ts @@ -1,5 +1,5 @@ import type { Service } from "@cloudflare/workers-types" -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { subjects } from "../../subjects" interface Env { diff --git a/examples/client/jwt-api/CHANGELOG.md b/examples/client/jwt-api/CHANGELOG.md index 45535519..1de519d7 100644 --- a/examples/client/jwt-api/CHANGELOG.md +++ b/examples/client/jwt-api/CHANGELOG.md @@ -5,4 +5,4 @@ ### Patch Changes - Updated dependencies [8b5f490] - - @openauthjs/openauth@0.2.4 + - @kagii/openauth@0.2.4 diff --git a/examples/client/jwt-api/index.ts b/examples/client/jwt-api/index.ts index 2adcb4c5..3fd1421b 100644 --- a/examples/client/jwt-api/index.ts +++ b/examples/client/jwt-api/index.ts @@ -1,4 +1,4 @@ -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { subjects } from "../../subjects" const headers = { diff --git a/examples/client/jwt-api/package.json b/examples/client/jwt-api/package.json index 805251fa..cdbf146d 100644 --- a/examples/client/jwt-api/package.json +++ b/examples/client/jwt-api/package.json @@ -10,6 +10,6 @@ "author": "", "license": "ISC", "dependencies": { - "@openauthjs/openauth": "workspace:*" + "@kagii/openauth": "workspace:*" } } diff --git a/examples/client/lambda-api/api.ts b/examples/client/lambda-api/api.ts index 884d1138..1b7d85da 100644 --- a/examples/client/lambda-api/api.ts +++ b/examples/client/lambda-api/api.ts @@ -1,6 +1,6 @@ import { Context, Hono } from "hono" import { getCookie, setCookie } from "hono/cookie" -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { handle } from "hono/aws-lambda" import { subjects } from "../../subjects" diff --git a/examples/client/nextjs/CHANGELOG.md b/examples/client/nextjs/CHANGELOG.md index 352b1873..ebd24f4f 100644 --- a/examples/client/nextjs/CHANGELOG.md +++ b/examples/client/nextjs/CHANGELOG.md @@ -5,28 +5,28 @@ ### Patch Changes - Updated dependencies [8b5f490] - - @openauthjs/openauth@0.2.4 + - @kagii/openauth@0.2.4 ## 0.1.5 ### Patch Changes - Updated dependencies [80238de] - - @openauthjs/openauth@0.2.3 + - @kagii/openauth@0.2.3 ## 0.1.4 ### Patch Changes - Updated dependencies [6da8647] - - @openauthjs/openauth@0.2.2 + - @kagii/openauth@0.2.2 ## 0.1.3 ### Patch Changes - Updated dependencies [83125f1] - - @openauthjs/openauth@0.2.1 + - @kagii/openauth@0.2.1 ## 0.1.2 @@ -34,7 +34,7 @@ - Updated dependencies [8c3f050] - Updated dependencies [0f93def] - - @openauthjs/openauth@0.2.0 + - @kagii/openauth@0.2.0 ## 0.1.1 @@ -43,4 +43,4 @@ - Updated dependencies [584728f] - Updated dependencies [41acdc2] - Updated dependencies [2aa531b] - - @openauthjs/openauth@0.1.2 + - @kagii/openauth@0.1.2 diff --git a/examples/client/nextjs/app/auth.ts b/examples/client/nextjs/app/auth.ts index 92f6c4d7..7c695e57 100644 --- a/examples/client/nextjs/app/auth.ts +++ b/examples/client/nextjs/app/auth.ts @@ -1,4 +1,4 @@ -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { cookies as getCookies } from "next/headers" export { subjects } from "../../../subjects" diff --git a/examples/client/nextjs/package.json b/examples/client/nextjs/package.json index 1bdd20f4..b983f53c 100644 --- a/examples/client/nextjs/package.json +++ b/examples/client/nextjs/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@openauthjs/openauth": "workspace:*", + "@kagii/openauth": "workspace:*", "next": "15.1.0", "react": "19.0.0", "react-dom": "19.0.0" diff --git a/examples/client/react/package.json b/examples/client/react/package.json index 7564eb3c..77c00d25 100644 --- a/examples/client/react/package.json +++ b/examples/client/react/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@openauthjs/openauth": "workspace:*", + "@kagii/openauth": "workspace:*", "react": "19.0.0", "react-dom": "19.0.0" }, diff --git a/examples/client/react/src/AuthContext.tsx b/examples/client/react/src/AuthContext.tsx index 4d59925e..784c6921 100644 --- a/examples/client/react/src/AuthContext.tsx +++ b/examples/client/react/src/AuthContext.tsx @@ -6,7 +6,7 @@ import { useContext, createContext, } from "react" -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" const client = createClient({ clientID: "react", diff --git a/examples/client/sveltekit/package.json b/examples/client/sveltekit/package.json index 92b35a4a..5983098f 100644 --- a/examples/client/sveltekit/package.json +++ b/examples/client/sveltekit/package.json @@ -11,7 +11,7 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" }, "devDependencies": { - "@openauthjs/openauth": "^0.4.3", + "@kagii/openauth": "^0.4.4", "@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", diff --git a/examples/client/sveltekit/src/lib/auth.server.ts b/examples/client/sveltekit/src/lib/auth.server.ts index f9e3fa97..23671684 100644 --- a/examples/client/sveltekit/src/lib/auth.server.ts +++ b/examples/client/sveltekit/src/lib/auth.server.ts @@ -1,4 +1,4 @@ -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import type { RequestEvent } from "@sveltejs/kit" export function createAuthClient(event: RequestEvent) { diff --git a/examples/issuer/bun/issuer.ts b/examples/issuer/bun/issuer.ts index 55d4c86d..083c18f2 100644 --- a/examples/issuer/bun/issuer.ts +++ b/examples/issuer/bun/issuer.ts @@ -1,7 +1,7 @@ -import { issuer } from "@openauthjs/openauth" -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" -import { PasswordProvider } from "@openauthjs/openauth/provider/password" -import { PasswordUI } from "@openauthjs/openauth/ui/password" +import { issuer } from "@kagii/openauth" +import { MemoryStorage } from "@kagii/openauth/storage/memory" +import { PasswordProvider } from "@kagii/openauth/provider/password" +import { PasswordUI } from "@kagii/openauth/ui/password" import { subjects } from "../../subjects.js" async function getUser(email: string) { diff --git a/examples/issuer/bun/package.json b/examples/issuer/bun/package.json index bfa866a4..22444dc8 100644 --- a/examples/issuer/bun/package.json +++ b/examples/issuer/bun/package.json @@ -2,6 +2,6 @@ "name": "@openauthjs/example-issuer-bun", "version": "0.0.0", "dependencies": { - "@openauthjs/openauth": "workspace:*" + "@kagii/openauth": "workspace:*" } } diff --git a/examples/issuer/cloudflare/issuer.ts b/examples/issuer/cloudflare/issuer.ts index af4a2f76..fd94efbf 100644 --- a/examples/issuer/cloudflare/issuer.ts +++ b/examples/issuer/cloudflare/issuer.ts @@ -1,12 +1,12 @@ -import { issuer } from "@openauthjs/openauth" -import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" +import { issuer } from "@kagii/openauth" +import { CloudflareStorage } from "@kagii/openauth/storage/cloudflare" import { type ExecutionContext, type KVNamespace, } from "@cloudflare/workers-types" import { subjects } from "../../subjects.js" -import { PasswordProvider } from "@openauthjs/openauth/provider/password" -import { PasswordUI } from "@openauthjs/openauth/ui/password" +import { PasswordProvider } from "@kagii/openauth/provider/password" +import { PasswordUI } from "@kagii/openauth/ui/password" interface Env { CloudflareAuthKV: KVNamespace diff --git a/examples/issuer/cloudflare/package.json b/examples/issuer/cloudflare/package.json index a3e98ccf..ddfbd3b5 100644 --- a/examples/issuer/cloudflare/package.json +++ b/examples/issuer/cloudflare/package.json @@ -2,7 +2,7 @@ "name": "@openauthjs/example-issuer-cloudflare", "version": "0.0.0", "dependencies": { - "@openauthjs/openauth": "workspace:*", + "@kagii/openauth": "workspace:*", "sst": "3.5.1" } } diff --git a/examples/issuer/custom-frontend/auth/issuer.ts b/examples/issuer/custom-frontend/auth/issuer.ts index 0fc8c10c..6a03e2ac 100644 --- a/examples/issuer/custom-frontend/auth/issuer.ts +++ b/examples/issuer/custom-frontend/auth/issuer.ts @@ -1,6 +1,6 @@ -import { issuer } from "@openauthjs/openauth" -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" -import { CodeProvider } from "@openauthjs/openauth/provider/code" +import { issuer } from "@kagii/openauth" +import { MemoryStorage } from "@kagii/openauth/storage/memory" +import { CodeProvider } from "@kagii/openauth/provider/code" import { subjects } from "../../../subjects.js" async function getUser(email: string) { diff --git a/examples/issuer/custom-frontend/auth/package.json b/examples/issuer/custom-frontend/auth/package.json index 63b6ccc9..ba4af68d 100644 --- a/examples/issuer/custom-frontend/auth/package.json +++ b/examples/issuer/custom-frontend/auth/package.json @@ -2,6 +2,6 @@ "name": "@openauthjs/example-custom-frontend-issuer", "version": "0.0.0", "dependencies": { - "@openauthjs/openauth": "workspace:*" + "@kagii/openauth": "workspace:*" } } diff --git a/examples/issuer/lambda/issuer.ts b/examples/issuer/lambda/issuer.ts index 6d7c8519..d60af9b4 100644 --- a/examples/issuer/lambda/issuer.ts +++ b/examples/issuer/lambda/issuer.ts @@ -1,8 +1,8 @@ -import { issuer } from "@openauthjs/openauth" +import { issuer } from "@kagii/openauth" import { handle } from "hono/aws-lambda" import { subjects } from "../../subjects.js" -import { PasswordUI } from "@openauthjs/openauth/ui/password" -import { PasswordProvider } from "@openauthjs/openauth/provider/password" +import { PasswordUI } from "@kagii/openauth/ui/password" +import { PasswordProvider } from "@kagii/openauth/provider/password" async function getUser(email: string) { // Get user from database diff --git a/examples/issuer/lambda/package.json b/examples/issuer/lambda/package.json index 22ce6f1f..9c3c5088 100644 --- a/examples/issuer/lambda/package.json +++ b/examples/issuer/lambda/package.json @@ -2,7 +2,7 @@ "name": "@openauthjs/example-issuer-aws", "version": "0.0.0", "dependencies": { - "@openauthjs/openauth": "workspace:*", + "@kagii/openauth": "workspace:*", "sst": "3.5.1" } } diff --git a/examples/issuer/node/authorizer.ts b/examples/issuer/node/authorizer.ts index 578c21f5..1a46e459 100644 --- a/examples/issuer/node/authorizer.ts +++ b/examples/issuer/node/authorizer.ts @@ -1,9 +1,9 @@ -import { issuer } from "@openauthjs/openauth" -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" -import { PasswordUI } from "@openauthjs/openauth/ui/password" +import { issuer } from "@kagii/openauth" +import { MemoryStorage } from "@kagii/openauth/storage/memory" +import { PasswordUI } from "@kagii/openauth/ui/password" import { serve } from "@hono/node-server" import { subjects } from "../../subjects" -import { PasswordProvider } from "@openauthjs/openauth/provider/password" +import { PasswordProvider } from "@kagii/openauth/provider/password" async function getUser(email: string) { // Get user from database diff --git a/examples/quickstart/sst/app/auth.ts b/examples/quickstart/sst/app/auth.ts index 1095b8b6..63adc470 100644 --- a/examples/quickstart/sst/app/auth.ts +++ b/examples/quickstart/sst/app/auth.ts @@ -1,5 +1,5 @@ import { Resource } from "sst" -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { cookies as getCookies } from "next/headers" export const client = createClient({ diff --git a/examples/quickstart/sst/auth/index.ts b/examples/quickstart/sst/auth/index.ts index 9bbbc875..3e97732e 100644 --- a/examples/quickstart/sst/auth/index.ts +++ b/examples/quickstart/sst/auth/index.ts @@ -1,8 +1,8 @@ import { handle } from "hono/aws-lambda" -import { issuer } from "@openauthjs/openauth" -import { CodeUI } from "@openauthjs/openauth/ui/code" -import { CodeProvider } from "@openauthjs/openauth/provider/code" -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" +import { issuer } from "@kagii/openauth" +import { CodeUI } from "@kagii/openauth/ui/code" +import { CodeProvider } from "@kagii/openauth/provider/code" +import { MemoryStorage } from "@kagii/openauth/storage/memory" import { subjects } from "./subjects" async function getUser(email: string) { diff --git a/examples/quickstart/sst/auth/subjects.ts b/examples/quickstart/sst/auth/subjects.ts index 479fefad..43b086e4 100644 --- a/examples/quickstart/sst/auth/subjects.ts +++ b/examples/quickstart/sst/auth/subjects.ts @@ -1,5 +1,5 @@ import { object, string } from "valibot" -import { createSubjects } from "@openauthjs/openauth/subject" +import { createSubjects } from "@kagii/openauth/subject" export const subjects = createSubjects({ user: object({ diff --git a/examples/quickstart/sst/package-lock.json b/examples/quickstart/sst/package-lock.json index b61698f2..f1d55805 100644 --- a/examples/quickstart/sst/package-lock.json +++ b/examples/quickstart/sst/package-lock.json @@ -8,7 +8,7 @@ "name": "oa-nextjs", "version": "0.1.0", "dependencies": { - "@openauthjs/openauth": "^0.3.2", + "@kagii/openauth": "^0.4.4", "hono": "^4.6.16", "next": "15.1.4", "react": "^19.0.0", @@ -394,6 +394,30 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@kagii/openauth": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@kagii/openauth/-/openauth-0.4.4.tgz", + "integrity": "sha512-1tQegzRAsZgeeQNW+LsaxNoerizrYRJQQ6dLqXfN0fgvkamwMmB0yF2RSbPA/0aao9I/c5SzCWr0Sxd8pWbpcA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "1.0.0-beta.3", + "aws4fetch": "1.0.20", + "jose": "5.9.6" + }, + "peerDependencies": { + "arctic": "^2.2.2", + "hono": "^4.0.0" + } + }, + "node_modules/@kagii/openauth/node_modules/jose": { + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@next/env": { "version": "15.1.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.4.tgz", @@ -528,29 +552,6 @@ "node": ">= 10" } }, - "node_modules/@openauthjs/openauth": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@openauthjs/openauth/-/openauth-0.3.2.tgz", - "integrity": "sha512-kcQs/H9Hcd2ijm4bl6UNJwtYPigsZ1EiM39+oEbxrOL7Qabicizvc3W0zIm1iv+WArujT85eQkZGI8QU9NesaA==", - "dependencies": { - "@standard-schema/spec": "1.0.0-beta.3", - "aws4fetch": "1.0.20", - "jose": "5.9.6" - }, - "peerDependencies": { - "arctic": "^2.2.2", - "hono": "^4.0.0" - } - }, - "node_modules/@openauthjs/openauth/node_modules/jose": { - "version": "5.9.6", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", - "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/@standard-schema/spec": { "version": "1.0.0-beta.3", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz", diff --git a/examples/quickstart/sst/package.json b/examples/quickstart/sst/package.json index ff7a4767..13f27262 100644 --- a/examples/quickstart/sst/package.json +++ b/examples/quickstart/sst/package.json @@ -9,7 +9,7 @@ "start": "next start" }, "dependencies": { - "@openauthjs/openauth": "^0.3.2", + "@kagii/openauth": "^0.4.4", "hono": "^4.6.16", "next": "15.1.4", "react": "^19.0.0", diff --git a/examples/quickstart/standalone/app/auth.ts b/examples/quickstart/standalone/app/auth.ts index 00170148..ee403756 100644 --- a/examples/quickstart/standalone/app/auth.ts +++ b/examples/quickstart/standalone/app/auth.ts @@ -1,4 +1,4 @@ -import { createClient } from "@openauthjs/openauth/client" +import { createClient } from "@kagii/openauth/client" import { cookies as getCookies } from "next/headers" export const client = createClient({ diff --git a/examples/quickstart/standalone/auth/index.ts b/examples/quickstart/standalone/auth/index.ts index 91e90e0b..1fe477dc 100644 --- a/examples/quickstart/standalone/auth/index.ts +++ b/examples/quickstart/standalone/auth/index.ts @@ -1,7 +1,7 @@ -import { issuer } from "@openauthjs/openauth" -import { CodeUI } from "@openauthjs/openauth/ui/code" -import { CodeProvider } from "@openauthjs/openauth/provider/code" -import { MemoryStorage } from "@openauthjs/openauth/storage/memory" +import { issuer } from "@kagii/openauth" +import { CodeUI } from "@kagii/openauth/ui/code" +import { CodeProvider } from "@kagii/openauth/provider/code" +import { MemoryStorage } from "@kagii/openauth/storage/memory" import { subjects } from "./subjects" async function getUser(email: string) { diff --git a/examples/quickstart/standalone/auth/subjects.ts b/examples/quickstart/standalone/auth/subjects.ts index 479fefad..43b086e4 100644 --- a/examples/quickstart/standalone/auth/subjects.ts +++ b/examples/quickstart/standalone/auth/subjects.ts @@ -1,5 +1,5 @@ import { object, string } from "valibot" -import { createSubjects } from "@openauthjs/openauth/subject" +import { createSubjects } from "@kagii/openauth/subject" export const subjects = createSubjects({ user: object({ diff --git a/examples/quickstart/standalone/package.json b/examples/quickstart/standalone/package.json index e3c5bc65..6da32f1e 100644 --- a/examples/quickstart/standalone/package.json +++ b/examples/quickstart/standalone/package.json @@ -10,7 +10,7 @@ "lint": "next lint" }, "dependencies": { - "@openauthjs/openauth": "^0.3.2", + "@kagii/openauth": "^0.4.4", "next": "15.1.4", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/subjects.ts b/examples/subjects.ts index 479fefad..43b086e4 100644 --- a/examples/subjects.ts +++ b/examples/subjects.ts @@ -1,5 +1,5 @@ import { object, string } from "valibot" -import { createSubjects } from "@openauthjs/openauth/subject" +import { createSubjects } from "@kagii/openauth/subject" export const subjects = createSubjects({ user: object({ diff --git a/package.json b/package.json index 5c5606eb..529246c6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "examples/client/*" ], "scripts": { - "release": "bun run --filter=\"@openauthjs/openauth\" build && changeset publish" + "release": "bun run --filter=\"@kagii/openauth\" build && changeset publish", + "publish:fork": "bun run --filter=\"@kagii/openauth\" build && npm publish -w packages/openauth" }, "devDependencies": { "@tsconfig/node22": "22.0.0", diff --git a/packages/openauth/CHANGELOG.md b/packages/openauth/CHANGELOG.md index 4e7bfa69..4442993c 100644 --- a/packages/openauth/CHANGELOG.md +++ b/packages/openauth/CHANGELOG.md @@ -1,4 +1,4 @@ -# @openauthjs/openauth +# @kagii/openauth ## 0.4.3 diff --git a/packages/openauth/package.json b/packages/openauth/package.json index a5e3276b..a3efc71a 100644 --- a/packages/openauth/package.json +++ b/packages/openauth/package.json @@ -1,6 +1,8 @@ { - "name": "@openauthjs/openauth", - "version": "0.4.3", + "name": "@kagii/openauth", + "version": "0.4.4", + "description": "Community-maintained OpenAuth fork for standards-based auth servers", + "license": "MIT", "type": "module", "scripts": { "build": "bun run script/build.ts", @@ -42,5 +44,14 @@ "files": [ "src", "dist" - ] + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kagii-dev/openauth.git", + "directory": "packages/openauth" + }, + "homepage": "https://github.com/kagii-dev/openauth#readme" } diff --git a/packages/openauth/src/client.ts b/packages/openauth/src/client.ts index c4e282a3..6ebce821 100644 --- a/packages/openauth/src/client.ts +++ b/packages/openauth/src/client.ts @@ -5,7 +5,7 @@ * First, create a client. * * ```ts title="client.ts" - * import { createClient } from "@openauthjs/openauth/client" + * import { createClient } from "@kagii/openauth/client" * * const client = createClient({ * clientID: "my-client", @@ -226,7 +226,7 @@ export interface ExchangeError { * * @example * ```ts - * import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error" + * import { InvalidAuthorizationCodeError } from "@kagii/openauth/error" * * console.log(err instanceof InvalidAuthorizationCodeError) *``` @@ -266,7 +266,7 @@ export interface RefreshError { * * @example * ```ts - * import { InvalidRefreshTokenError } from "@openauthjs/openauth/error" + * import { InvalidRefreshTokenError } from "@kagii/openauth/error" * * console.log(err instanceof InvalidRefreshTokenError) *``` @@ -332,7 +332,7 @@ export interface VerifyError { * * @example * ```ts - * import { InvalidRefreshTokenError } from "@openauthjs/openauth/error" + * import { InvalidRefreshTokenError } from "@kagii/openauth/error" * * console.log(err instanceof InvalidRefreshTokenError) *``` @@ -418,7 +418,7 @@ export interface Client { * you can handle depending on the error. * * ```ts - * import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error" + * import { InvalidAuthorizationCodeError } from "@kagii/openauth/error" * * if (exchanged.err) { * if (exchanged.err instanceof InvalidAuthorizationCodeError) { @@ -466,7 +466,7 @@ export interface Client { * Or if it fails, it returns an error that you can handle depending on the error. * * ```ts - * import { InvalidRefreshTokenError } from "@openauthjs/openauth/error" + * import { InvalidRefreshTokenError } from "@kagii/openauth/error" * * if (next.err) { * if (next.err instanceof InvalidRefreshTokenError) { @@ -520,7 +520,7 @@ export interface Client { * Or if it fails, it returns an error that you can handle depending on the error. * * ```ts - * import { InvalidRefreshTokenError } from "@openauthjs/openauth/error" + * import { InvalidRefreshTokenError } from "@kagii/openauth/error" * * if (verified.err) { * if (verified.err instanceof InvalidRefreshTokenError) { diff --git a/packages/openauth/src/error.ts b/packages/openauth/src/error.ts index b35de0b5..13eebd35 100644 --- a/packages/openauth/src/error.ts +++ b/packages/openauth/src/error.ts @@ -4,7 +4,7 @@ * You can use these errors to check the type of error and handle it. For example. * * ```ts - * import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error" + * import { InvalidAuthorizationCodeError } from "@kagii/openauth/error" * * if (err instanceof InvalidAuthorizationCodeError) { * // handle invalid code error diff --git a/packages/openauth/src/index.ts b/packages/openauth/src/index.ts index e83e4868..2e35ed09 100644 --- a/packages/openauth/src/index.ts +++ b/packages/openauth/src/index.ts @@ -1,7 +1,7 @@ export { /** * @deprecated - * Use `import { createClient } from "@openauthjs/openauth/client"` instead - it will tree shake better + * Use `import { createClient } from "@kagii/openauth/client"` instead - it will tree shake better */ createClient, } from "./client.js" @@ -9,7 +9,7 @@ export { export { /** * @deprecated - * Use `import { createSubjects } from "@openauthjs/openauth/subject"` instead - it will tree shake better + * Use `import { createSubjects } from "@kagii/openauth/subject"` instead - it will tree shake better */ createSubjects, } from "./subject.js" @@ -19,7 +19,7 @@ import { issuer } from "./issuer.js" export { /** * @deprecated - * Use `import { issuer } from "@openauthjs/openauth"` instead, it was renamed + * Use `import { issuer } from "@kagii/openauth"` instead, it was renamed */ issuer as authorizer, issuer, diff --git a/packages/openauth/src/issuer.ts b/packages/openauth/src/issuer.ts index f4c1f277..efe9d734 100644 --- a/packages/openauth/src/issuer.ts +++ b/packages/openauth/src/issuer.ts @@ -5,7 +5,7 @@ * The `issuer` function requires a few things: * * ```ts title="issuer.ts" - * import { issuer } from "@openauthjs/openauth" + * import { issuer } from "@kagii/openauth" * * const app = issuer({ * providers: { ... }, @@ -21,8 +21,8 @@ * to be able to authenticate with GitHub and with their email and password. * * ```ts title="issuer.ts" - * import { GithubProvider } from "@openauthjs/openauth/provider/github" - * import { PasswordProvider } from "@openauthjs/openauth/provider/password" + * import { GithubProvider } from "@kagii/openauth/provider/github" + * import { PasswordProvider } from "@kagii/openauth/provider/password" * * const app = issuer({ * providers: { @@ -70,7 +70,7 @@ * * ```ts title="subjects.ts" * import { object, string } from "valibot" - * import { createSubjects } from "@openauthjs/openauth/subject" + * import { createSubjects } from "@kagii/openauth/subject" * * const subjects = createSubjects({ * user: object({ @@ -224,7 +224,7 @@ export interface IssuerInput< * * ```ts title="issuer.ts" * import { object, string } from "valibot" - * import { createSubjects } from "@openauthjs/openauth/subject" + * import { createSubjects } from "@kagii/openauth/subject" * * issuer({ * subjects: createSubjects({ @@ -242,7 +242,7 @@ export interface IssuerInput< * * @example * ```ts title="issuer.ts" - * import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo" + * import { DynamoStorage } from "@kagii/openauth/storage/dynamo" * * issuer({ * storage: DynamoStorage() @@ -257,7 +257,7 @@ export interface IssuerInput< * @example * * ```ts title="issuer.ts" - * import { GithubProvider } from "@openauthjs/openauth/provider/github" + * import { GithubProvider } from "@kagii/openauth/provider/github" * * issuer({ * providers: { @@ -289,7 +289,7 @@ export interface IssuerInput< * * @example * ```ts title="issuer.ts" - * import { THEME_SST } from "@openauthjs/openauth/ui/theme" + * import { THEME_SST } from "@kagii/openauth/ui/theme" * * issuer({ * theme: THEME_SST @@ -300,7 +300,7 @@ export interface IssuerInput< * Or define your own. * * ```ts title="issuer.ts" - * import type { Theme } from "@openauthjs/openauth/ui/theme" + * import type { Theme } from "@kagii/openauth/ui/theme" * * const MY_THEME: Theme = { * // ... @@ -354,7 +354,7 @@ export interface IssuerInput< * of the OpenAuth server. * * ```ts title="issuer.ts" - * import { Select } from "@openauthjs/openauth/ui/select" + * import { Select } from "@kagii/openauth/ui/select" * * issuer({ * select: Select({ diff --git a/packages/openauth/src/keys.ts b/packages/openauth/src/keys.ts index 81688d15..ec84659d 100644 --- a/packages/openauth/src/keys.ts +++ b/packages/openauth/src/keys.ts @@ -96,7 +96,25 @@ export async function signingKeys(storage: StorageAdapter): Promise { alg: signingAlg, } await Storage.set(storage, ["signing:key", serialized.id], serialized) - return signingKeys(storage) + + // Return the newly created key directly instead of recursively scanning + // to avoid race conditions with eventually consistent storage (e.g., Cloudflare KV) + // Issue #322: https://github.com/sst/openauth/issues/322 + const jwk = await exportJWK(key.publicKey) + jwk.kid = serialized.id + jwk.use = "sig" + return [ + ...results, + { + id: serialized.id, + alg: signingAlg, + created: new Date(serialized.created), + expired: undefined, + public: key.publicKey, + private: key.privateKey, + jwk, + }, + ] } export async function encryptionKeys( @@ -135,5 +153,22 @@ export async function encryptionKeys( alg: encryptionAlg, } await Storage.set(storage, ["encryption:key", serialized.id], serialized) - return encryptionKeys(storage) + + // Return the newly created key directly instead of recursively scanning + // to avoid race conditions with eventually consistent storage (e.g., Cloudflare KV) + // Issue #322: https://github.com/sst/openauth/issues/322 + const jwk = await exportJWK(key.publicKey) + jwk.kid = serialized.id + return [ + ...results, + { + id: serialized.id, + alg: encryptionAlg, + created: new Date(serialized.created), + expired: undefined, + public: key.publicKey, + private: key.privateKey, + jwk, + }, + ] } diff --git a/packages/openauth/src/provider/apple.ts b/packages/openauth/src/provider/apple.ts index f69e252b..366817a1 100644 --- a/packages/openauth/src/provider/apple.ts +++ b/packages/openauth/src/provider/apple.ts @@ -4,7 +4,7 @@ * #### Using OAuth * * ```ts {5-8} - * import { AppleProvider } from "@openauthjs/openauth/provider/apple" + * import { AppleProvider } from "@kagii/openauth/provider/apple" * * export default issuer({ * providers: { @@ -21,7 +21,7 @@ * When requesting name or email scopes from Apple, you must use form_post response mode: * * ```ts {5-9} - * import { AppleProvider } from "@openauthjs/openauth/provider/apple" + * import { AppleProvider } from "@kagii/openauth/provider/apple" * * export default issuer({ * providers: { @@ -37,7 +37,7 @@ * #### Using OIDC * * ```ts {5-7} - * import { AppleOidcProvider } from "@openauthjs/openauth/provider/apple" + * import { AppleOidcProvider } from "@kagii/openauth/provider/apple" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/code.ts b/packages/openauth/src/provider/code.ts index e8f7b709..83c780a7 100644 --- a/packages/openauth/src/provider/code.ts +++ b/packages/openauth/src/provider/code.ts @@ -3,8 +3,8 @@ * `CodeUI`. * * ```ts - * import { CodeUI } from "@openauthjs/openauth/ui/code" - * import { CodeProvider } from "@openauthjs/openauth/provider/code" + * import { CodeUI } from "@kagii/openauth/ui/code" + * import { CodeProvider } from "@kagii/openauth/provider/code" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/cognito.ts b/packages/openauth/src/provider/cognito.ts index 59a8e0dc..fdc10a16 100644 --- a/packages/openauth/src/provider/cognito.ts +++ b/packages/openauth/src/provider/cognito.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with a Cognito OAuth endpoint. * * ```ts {5-10} - * import { CognitoProvider } from "@openauthjs/openauth/provider/cognito" + * import { CognitoProvider } from "@kagii/openauth/provider/cognito" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/discord.ts b/packages/openauth/src/provider/discord.ts index f1c05af7..6b85b530 100644 --- a/packages/openauth/src/provider/discord.ts +++ b/packages/openauth/src/provider/discord.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Discord. * * ```ts {5-8} - * import { DiscordProvider } from "@openauthjs/openauth/provider/discord" + * import { DiscordProvider } from "@kagii/openauth/provider/discord" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/facebook.ts b/packages/openauth/src/provider/facebook.ts index c5fbc442..9519ebf6 100644 --- a/packages/openauth/src/provider/facebook.ts +++ b/packages/openauth/src/provider/facebook.ts @@ -4,7 +4,7 @@ * #### Using OAuth * * ```ts {5-8} - * import { FacebookProvider } from "@openauthjs/openauth/provider/facebook" + * import { FacebookProvider } from "@kagii/openauth/provider/facebook" * * export default issuer({ * providers: { @@ -19,7 +19,7 @@ * #### Using OIDC * * ```ts {5-7} - * import { FacebookOidcProvider } from "@openauthjs/openauth/provider/facebook" + * import { FacebookOidcProvider } from "@kagii/openauth/provider/facebook" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/github.ts b/packages/openauth/src/provider/github.ts index ca93ba3b..33d49635 100644 --- a/packages/openauth/src/provider/github.ts +++ b/packages/openauth/src/provider/github.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Github. * * ```ts {5-8} - * import { GithubProvider } from "@openauthjs/openauth/provider/github" + * import { GithubProvider } from "@kagii/openauth/provider/github" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/google.ts b/packages/openauth/src/provider/google.ts index 359a386a..5d8021f0 100644 --- a/packages/openauth/src/provider/google.ts +++ b/packages/openauth/src/provider/google.ts @@ -4,7 +4,7 @@ * #### Using OAuth * * ```ts {5-8} - * import { GoogleProvider } from "@openauthjs/openauth/provider/google" + * import { GoogleProvider } from "@kagii/openauth/provider/google" * * export default issuer({ * providers: { @@ -19,7 +19,7 @@ * #### Using OIDC * * ```ts {5-7} - * import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google" + * import { GoogleOidcProvider } from "@kagii/openauth/provider/google" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/jumpcloud.ts b/packages/openauth/src/provider/jumpcloud.ts index becb06b0..96ef9ec2 100644 --- a/packages/openauth/src/provider/jumpcloud.ts +++ b/packages/openauth/src/provider/jumpcloud.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with JumpCloud. * * ```ts {5-8} - * import { JumpCloudProvider } from "@openauthjs/openauth/provider/jumpcloud" + * import { JumpCloudProvider } from "@kagii/openauth/provider/jumpcloud" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/keycloak.ts b/packages/openauth/src/provider/keycloak.ts index 8992e30c..10a38984 100644 --- a/packages/openauth/src/provider/keycloak.ts +++ b/packages/openauth/src/provider/keycloak.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with a Keycloak server. * * ```ts {5-10} - * import { KeycloakProvider } from "@openauthjs/openauth/provider/keycloak" + * import { KeycloakProvider } from "@kagii/openauth/provider/keycloak" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/microsoft.ts b/packages/openauth/src/provider/microsoft.ts index f4aa8977..719529e6 100644 --- a/packages/openauth/src/provider/microsoft.ts +++ b/packages/openauth/src/provider/microsoft.ts @@ -4,7 +4,7 @@ * #### Using OAuth * * ```ts {5-9} - * import { MicrosoftProvider } from "@openauthjs/openauth/provider/microsoft" + * import { MicrosoftProvider } from "@kagii/openauth/provider/microsoft" * * export default issuer({ * providers: { @@ -20,7 +20,7 @@ * #### Using OIDC * * ```ts {5-7} - * import { MicrosoftOidcProvider } from "@openauthjs/openauth/provider/microsoft" + * import { MicrosoftOidcProvider } from "@kagii/openauth/provider/microsoft" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/oauth2.ts b/packages/openauth/src/provider/oauth2.ts index 5a0f6583..43157cd1 100644 --- a/packages/openauth/src/provider/oauth2.ts +++ b/packages/openauth/src/provider/oauth2.ts @@ -2,7 +2,7 @@ * Use this to connect authentication providers that support OAuth 2.0. * * ```ts {5-12} - * import { Oauth2Provider } from "@openauthjs/openauth/provider/oauth2" + * import { Oauth2Provider } from "@kagii/openauth/provider/oauth2" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/oidc.ts b/packages/openauth/src/provider/oidc.ts index 8e21fde7..dafd8736 100644 --- a/packages/openauth/src/provider/oidc.ts +++ b/packages/openauth/src/provider/oidc.ts @@ -2,7 +2,7 @@ * Use this to connect authentication providers that support OIDC. * * ```ts {5-8} - * import { OidcProvider } from "@openauthjs/openauth/provider/oidc" + * import { OidcProvider } from "@kagii/openauth/provider/oidc" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/password.ts b/packages/openauth/src/provider/password.ts index 850af8a3..b9e5bf55 100644 --- a/packages/openauth/src/provider/password.ts +++ b/packages/openauth/src/provider/password.ts @@ -3,8 +3,8 @@ * paired with the `PasswordUI`. * * ```ts - * import { PasswordUI } from "@openauthjs/openauth/ui/password" - * import { PasswordProvider } from "@openauthjs/openauth/provider/password" + * import { PasswordUI } from "@kagii/openauth/ui/password" + * import { PasswordProvider } from "@kagii/openauth/provider/password" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/slack.ts b/packages/openauth/src/provider/slack.ts index 2d3ddcaf..2ef68443 100644 --- a/packages/openauth/src/provider/slack.ts +++ b/packages/openauth/src/provider/slack.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Slack. * * ```ts {5-10} - * import { SlackProvider } from "@openauthjs/openauth/provider/slack" + * import { SlackProvider } from "@kagii/openauth/provider/slack" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/spotify.ts b/packages/openauth/src/provider/spotify.ts index 7c305583..e5c08cfd 100644 --- a/packages/openauth/src/provider/spotify.ts +++ b/packages/openauth/src/provider/spotify.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Spotify. * * ```ts {5-8} - * import { SpotifyProvider } from "@openauthjs/openauth/provider/spotify" + * import { SpotifyProvider } from "@kagii/openauth/provider/spotify" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/twitch.ts b/packages/openauth/src/provider/twitch.ts index e362d04d..3fa53508 100644 --- a/packages/openauth/src/provider/twitch.ts +++ b/packages/openauth/src/provider/twitch.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Twitch. * * ```ts {5-8} - * import { TwitchProvider } from "@openauthjs/openauth/provider/twitch" + * import { TwitchProvider } from "@kagii/openauth/provider/twitch" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/x.ts b/packages/openauth/src/provider/x.ts index 84b691b5..6cd50300 100644 --- a/packages/openauth/src/provider/x.ts +++ b/packages/openauth/src/provider/x.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with X.com. * * ```ts {5-8} - * import { XProvider } from "@openauthjs/openauth/provider/x" + * import { XProvider } from "@kagii/openauth/provider/x" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/provider/yahoo.ts b/packages/openauth/src/provider/yahoo.ts index 84242a5e..593eecc9 100644 --- a/packages/openauth/src/provider/yahoo.ts +++ b/packages/openauth/src/provider/yahoo.ts @@ -2,7 +2,7 @@ * Use this provider to authenticate with Yahoo. * * ```ts {5-8} - * import { YahooProvider } from "@openauthjs/openauth/provider/yahoo" + * import { YahooProvider } from "@kagii/openauth/provider/yahoo" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/storage/cloudflare.ts b/packages/openauth/src/storage/cloudflare.ts index 8fb7bb27..f799c4a9 100644 --- a/packages/openauth/src/storage/cloudflare.ts +++ b/packages/openauth/src/storage/cloudflare.ts @@ -3,7 +3,7 @@ * storage adapter. * * ```ts - * import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" + * import { CloudflareStorage } from "@kagii/openauth/storage/cloudflare" * * const storage = CloudflareStorage({ * namespace: "my-namespace" diff --git a/packages/openauth/src/storage/dynamo.ts b/packages/openauth/src/storage/dynamo.ts index 83d702e1..974e56d2 100644 --- a/packages/openauth/src/storage/dynamo.ts +++ b/packages/openauth/src/storage/dynamo.ts @@ -2,7 +2,7 @@ * Configure OpenAuth to use [DynamoDB](https://aws.amazon.com/dynamodb/) as a storage adapter. * * ```ts - * import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo" + * import { DynamoStorage } from "@kagii/openauth/storage/dynamo" * * const storage = DynamoStorage({ * table: "my-table", diff --git a/packages/openauth/src/storage/memory.ts b/packages/openauth/src/storage/memory.ts index e8fe594d..10622a33 100644 --- a/packages/openauth/src/storage/memory.ts +++ b/packages/openauth/src/storage/memory.ts @@ -8,7 +8,7 @@ * This is useful for testing and development. It's not meant to be used in production. * * ```ts - * import { MemoryStorage } from "@openauthjs/openauth/storage/memory" + * import { MemoryStorage } from "@kagii/openauth/storage/memory" * * const storage = MemoryStorage() * diff --git a/packages/openauth/src/ui/code.tsx b/packages/openauth/src/ui/code.tsx index 5ed77392..e6569b3f 100644 --- a/packages/openauth/src/ui/code.tsx +++ b/packages/openauth/src/ui/code.tsx @@ -2,8 +2,8 @@ * Configure the UI that's used by the Code provider. * * ```ts {1,7-12} - * import { CodeUI } from "@openauthjs/openauth/ui/code" - * import { CodeProvider } from "@openauthjs/openauth/provider/code" + * import { CodeUI } from "@kagii/openauth/ui/code" + * import { CodeProvider } from "@kagii/openauth/provider/code" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/ui/password.tsx b/packages/openauth/src/ui/password.tsx index 360f28da..6d92f2ec 100644 --- a/packages/openauth/src/ui/password.tsx +++ b/packages/openauth/src/ui/password.tsx @@ -2,8 +2,8 @@ * Configure the UI that's used by the Password provider. * * ```ts {1,7-12} - * import { PasswordUI } from "@openauthjs/openauth/ui/password" - * import { PasswordProvider } from "@openauthjs/openauth/provider/password" + * import { PasswordUI } from "@kagii/openauth/ui/password" + * import { PasswordProvider } from "@kagii/openauth/provider/password" * * export default issuer({ * providers: { diff --git a/packages/openauth/src/ui/select.tsx b/packages/openauth/src/ui/select.tsx index 159b6132..cb4b6a81 100644 --- a/packages/openauth/src/ui/select.tsx +++ b/packages/openauth/src/ui/select.tsx @@ -3,7 +3,7 @@ * which providers should be displayed in the select UI. * * ```ts - * import { Select } from "@openauthjs/openauth/ui/select" + * import { Select } from "@kagii/openauth/ui/select" * * export default issuer({ * select: Select({ diff --git a/packages/openauth/src/ui/theme.ts b/packages/openauth/src/ui/theme.ts index 9207314c..3a6ef199 100644 --- a/packages/openauth/src/ui/theme.ts +++ b/packages/openauth/src/ui/theme.ts @@ -4,7 +4,7 @@ * @example * * ```ts - * import { THEME_SST } from "@openauthjs/openauth/ui/theme" + * import { THEME_SST } from "@kagii/openauth/ui/theme" * * export default issuer({ * theme: THEME_SST, @@ -15,7 +15,7 @@ * Or define your own. * * ```ts - * import type { Theme } from "@openauthjs/openauth/ui/theme" + * import type { Theme } from "@kagii/openauth/ui/theme" * * const MY_THEME: Theme = { * title: "Acne", diff --git a/packages/openauth/test/keys.test.ts b/packages/openauth/test/keys.test.ts new file mode 100644 index 00000000..27633be6 --- /dev/null +++ b/packages/openauth/test/keys.test.ts @@ -0,0 +1,212 @@ +import { describe, expect, test } from "bun:test" +import { signingKeys, encryptionKeys } from "../src/keys.js" +import type { StorageAdapter } from "../src/storage/storage.js" + +/** + * Mock storage adapter that simulates eventual consistency. + * This mimics Cloudflare KV behavior where writes complete successfully + * but subsequent scans may not immediately see the new values. + */ +class EventuallyConsistentStorage implements StorageAdapter { + private data = new Map() + private scanDelayedWrites = new Set() + private maxRecursionDepth: number + + constructor(maxRecursionDepth = Infinity) { + this.maxRecursionDepth = maxRecursionDepth + } + + async get(key: string[]) { + return this.data.get(JSON.stringify(key)) + } + + async set(key: string[], value: any, expiry?: Date | number) { + const keyStr = JSON.stringify(key) + this.data.set(keyStr, value) + + // Simulate eventual consistency: mark this write as "not yet visible to scans" + // but limit it to prevent infinite recursion in tests + if (this.data.size <= this.maxRecursionDepth) { + this.scanDelayedWrites.add(keyStr) + } + + return undefined + } + + async remove(key: string[]) { + this.data.delete(JSON.stringify(key)) + } + + async *scan(prefix: string[]): AsyncGenerator<[string[], any], void, unknown> { + const prefixStr = JSON.stringify(prefix).slice(0, -1) // Remove trailing ] + + for (const [key, value] of this.data.entries()) { + // Skip entries that are in the "delayed write" set (simulating eventual consistency) + if (this.scanDelayedWrites.has(key)) { + continue + } + + if (key.startsWith(prefixStr)) { + yield [JSON.parse(key), value] + } + } + } + + // Helper method to "complete" the eventual consistency and make all writes visible + makeConsistent() { + this.scanDelayedWrites.clear() + } + + // Get the total number of keys created + getKeyCount() { + return this.data.size + } +} + +/** + * Mock storage that counts how many times set() is called. + * This helps us verify we're not creating hundreds of keys. + */ +class CountingStorage implements StorageAdapter { + private data = new Map() + public setCallCount = 0 + + async get(key: string[]) { + return this.data.get(JSON.stringify(key)) + } + + async set(key: string[], value: any, expiry?: Date | number) { + this.setCallCount++ + this.data.set(JSON.stringify(key), value) + return undefined + } + + async remove(key: string[]) { + this.data.delete(JSON.stringify(key)) + } + + async *scan(prefix: string[]): AsyncGenerator<[string[], any], void, unknown> { + const prefixStr = JSON.stringify(prefix).slice(0, -1) + for (const [key, value] of this.data.entries()) { + if (key.startsWith(prefixStr)) { + yield [JSON.parse(key), value] + } + } + } +} + +describe("signingKeys", () => { + test("generates exactly one key on empty storage", async () => { + const storage = new CountingStorage() + + const keys = await signingKeys(storage) + + // Should generate exactly one key, not hundreds + expect(storage.setCallCount).toBe(1) + expect(keys).toHaveLength(1) + expect(keys[0].alg).toBe("ES256") + }) + + test("ISSUE #322: fix prevents multiple keys with eventual consistency", async () => { + // This test verifies the fix for issue #322: + // The OLD code would recursively call signingKeys() after writing a key. + // With eventual consistency, the scan wouldn't see the newly written key, + // so it would create another, triggering infinite recursion (hundreds of keys). + // + // The FIX: Return the newly created key directly without recursive scanning. + // This ensures only ONE key is created per function call. + const storage = new EventuallyConsistentStorage(10) + + const keys = await signingKeys(storage) + + // With the fix, we create exactly 1 key (no recursive loop) + expect(storage.getKeyCount()).toBe(1) + expect(keys).toHaveLength(1) + + // Verify the key is actually persisted + storage.makeConsistent() + const persistedKeys = await Array.fromAsync(storage.scan(["signing:key"])) + expect(persistedKeys).toHaveLength(1) + }) + + test("key is actually persisted to storage", async () => { + const storage = new CountingStorage() + + const keys = await signingKeys(storage) + const keyId = keys[0].id + + // Verify the key is in storage by directly scanning + const storedKeys = await Array.fromAsync(storage.scan(["signing:key"])) + expect(storedKeys).toHaveLength(1) + expect(storedKeys[0][1].id).toBe(keyId) + expect(storedKeys[0][1].alg).toBe("ES256") + }) + + test("reuses existing keys instead of creating new ones", async () => { + const storage = new CountingStorage() + + // First call creates a key + const keys1 = await signingKeys(storage) + expect(storage.setCallCount).toBe(1) + + // Second call should reuse the existing key + const keys2 = await signingKeys(storage) + expect(storage.setCallCount).toBe(1) // No new keys created + expect(keys2[0].id).toBe(keys1[0].id) + }) +}) + +describe("encryptionKeys", () => { + test("generates exactly one key on empty storage", async () => { + const storage = new CountingStorage() + + const keys = await encryptionKeys(storage) + + // Should generate exactly one key, not hundreds + expect(storage.setCallCount).toBe(1) + expect(keys).toHaveLength(1) + expect(keys[0].alg).toBe("RSA-OAEP-512") + }) + + test("ISSUE #322: fix prevents multiple keys with eventual consistency", async () => { + // Same fix as signingKeys - see test above for detailed explanation + const storage = new EventuallyConsistentStorage(10) + + const keys = await encryptionKeys(storage) + + // With the fix, we create exactly 1 key (no recursive loop) + expect(storage.getKeyCount()).toBe(1) + expect(keys).toHaveLength(1) + + // Verify the key is actually persisted + storage.makeConsistent() + const persistedKeys = await Array.fromAsync(storage.scan(["encryption:key"])) + expect(persistedKeys).toHaveLength(1) + }) + + test("key is actually persisted to storage", async () => { + const storage = new CountingStorage() + + const keys = await encryptionKeys(storage) + const keyId = keys[0].id + + // Verify the key is in storage by directly scanning + const storedKeys = await Array.fromAsync(storage.scan(["encryption:key"])) + expect(storedKeys).toHaveLength(1) + expect(storedKeys[0][1].id).toBe(keyId) + expect(storedKeys[0][1].alg).toBe("RSA-OAEP-512") + }) + + test("reuses existing keys instead of creating new ones", async () => { + const storage = new CountingStorage() + + // First call creates a key + const keys1 = await encryptionKeys(storage) + expect(storage.setCallCount).toBe(1) + + // Second call should reuse the existing key + const keys2 = await encryptionKeys(storage) + expect(storage.setCallCount).toBe(1) // No new keys created + expect(keys2[0].id).toBe(keys1[0].id) + }) +}) diff --git a/www/generate.ts b/www/generate.ts index 52332d38..8732852d 100644 --- a/www/generate.ts +++ b/www/generate.ts @@ -582,7 +582,7 @@ function renderType(type: TypeDoc.SomeType): Text { // Special handle hard-to-document types if ( type.type === "reference" && - type.package === "@openauthjs/openauth" && + type.package === "@kagii/openauth" && type.qualifiedName === "IssuerInput.Result" ) return `${type.name}` @@ -604,7 +604,7 @@ function renderType(type: TypeDoc.SomeType): Text { if (type.type === "array") return renderArrayType(type) if (type.type === "reference") { if (type.package === "typescript") return renderTypescriptType(type) - if (type.package === "@openauthjs/openauth") return renderOpenAuthType(type) + if (type.package === "@kagii/openauth") return renderOpenAuthType(type) if (type.package === "@standard-schema/spec") return renderStandardSchemaType(type) return `${type.name}`