[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-73514":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":25,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":27,"readmeContent":28,"aiSummary":29,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":30,"discoverSource":31},73514,"openauth","anomalyco\u002Fopenauth","anomalyco","▦ Universal, standards-based auth provider.","https:\u002F\u002Fopenauth.js.org",null,"TypeScript",6963,262,22,99,0,2,14,44,6,78.66,"MIT License",false,"master",true,[],"2026-06-12 04:01:09","\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fopenauth.js.org\">\n    \u003Cpicture>\n      \u003Csource srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Flogo-dark.svg\" media=\"(prefers-color-scheme: dark)\">\n      \u003Csource srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Flogo-light.svg\" media=\"(prefers-color-scheme: light)\">\n      \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Flogo-light.svg\" alt=\"OpenAuth logo\">\n    \u003C\u002Fpicture>\n  \u003C\u002Fa>\n\u003C\u002Fp>\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fsst.dev\u002Fdiscord\">\u003Cimg alt=\"Discord\" src=\"https:\u002F\u002Fimg.shields.io\u002Fdiscord\u002F983865673656705025?style=flat-square&label=Discord\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@openauthjs\u002Fopenauth\">\u003Cimg alt=\"npm\" src=\"https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002F%40openauthjs%2Fcore?style=flat-square\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ftoolbeam\u002Fopenauth\u002Factions\u002Fworkflows\u002Frelease.yml\">\u003Cimg alt=\"Build status\" src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Factions\u002Fworkflow\u002Fstatus\u002Ftoolbeam\u002Fopenauth\u002Frelease.yml?style=flat-square&branch=master\" \u002F>\u003C\u002Fa>\n\u003C\u002Fp>\n\n---\n\n[OpenAuth](https:\u002F\u002Fopenauth.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.\n\n- **Universal**: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform.\n- **Self-hosted**: It runs entirely on your infrastructure and can be deployed on Node.js, Bun, AWS Lambda, or Cloudflare Workers.\n- **Standards-based**: It implements the OAuth 2.0 spec and is based on web standards. So any OAuth client can use it.\n- **Customizable**: It comes with prebuilt themeable UI that you can customize or opt out of.\n\n\u003Cpicture>\n  \u003Csource srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Fassets\u002Fthemes-dark.png\" media=\"(prefers-color-scheme: dark)\">\n  \u003Csource srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Fassets\u002Fthemes-light.png\" media=\"(prefers-color-scheme: dark)\">\n  \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Ftoolbeam\u002Fidentity\u002Fmain\u002Fopenauth\u002Fassets\u002Fthemes-light.png\" alt=\"OpenAuth themes\">\n\u003C\u002Fpicture>\n\n## Quick Start\n\nIf you just want to get started as fast as possible you can jump straight into the [code examples](https:\u002F\u002Fgithub.com\u002Ftoolbeam\u002Fopenauth\u002Ftree\u002Fmaster\u002Fexamples) folder and copy paste away. There are also [SST components](https:\u002F\u002Fsst.dev\u002Fdocs\u002Fcomponent\u002Faws\u002Fauth) for deploying everything OpenAuth needs.\n\n## Approach\n\nWhile there are many open source solutions for auth, almost all of them are libraries that are meant to be embedded into a single application. Centralized auth servers typically are delivered as SaaS services - eg Auth0 or Clerk.\n\nOpenAuth instead is a centralized auth server that runs on your own infrastructure and has been designed for ease of self hosting. It can be used to authenticate all of your applications - web apps, mobile apps, internal admin tools, etc.\n\nIt adheres mostly to OAuth 2.0 specifications - which means anything that can speak OAuth can use it to receive access and refresh tokens. When a client initiates an authorization flow, OpenAuth will hand off to one of the configured providers - this can be third party identity providers like Google, GitHub, etc or built in flows like email\u002Fpassword or pin code.\n\nBecause it follows these specifications it can even be used to issue credentials for third party applications - allowing you to implement \"login with myapp\" flows.\n\nOpenAuth very intentionally does not attempt to solve user management. We've found that this is a very difficult problem given the wide range of databases and drivers that are used in the JS ecosystem. Additionally it's quite hard to build data abstractions that work for every use case. Instead, once a user has identified themselves OpenAuth will invoke a callback where you can implement your own user lookup\u002Fcreation logic.\n\nWhile OpenAuth tries to be mostly stateless, it does need to store a minimal amount of data (refresh tokens, password hashes, etc). However this has been reduced to a simple KV store with various implementations for zero overhead systems like Cloudflare KV and DynamoDB. You should never need to directly access any data that is stored in there.\n\nThere is also a themeable UI that you can use to get going without implementing any designs yourself. This is built on top of a lower level system so you can copy paste the default UI and tweak it or opt out entirely and implement your own.\n\nFinally, OpenAuth is created by the maintainers of [SST](https:\u002F\u002Fsst.dev) which is a tool to manage all the infrastructure for your app. It contains components for OpenAuth that make deploying it to AWS or Cloudflare as simple as it can get.\n\n## Tutorial\n\nWe'll show how to deploy the auth server and then a sample app that uses it.\n\n### Auth server\n\nStart by importing the `issuer` function from the `@openauthjs\u002Fopenauth` package.\n\n```ts\nimport { issuer } from \"@openauthjs\u002Fopenauth\"\n```\n\nOpenAuth is built on top of [Hono](https:\u002F\u002Fgithub.com\u002Fhonojs\u002Fhono) 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.\n\nThe `issuer` function requires a few things:\n\n```ts\nconst app = issuer({\n  providers: { ... },\n  storage,\n  subjects,\n  success: async (ctx, value) => { ... }\n})\n```\n\nFirst 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\u002Fpassword or pin code. You can also implement your own. Let's try the GitHub provider.\n\n```ts\nimport { GithubProvider } from \"@openauthjs\u002Fopenauth\u002Fprovider\u002Fgithub\"\n\nconst app = issuer({\n  providers: {\n    github: GithubProvider({\n      clientID: process.env.GITHUB_CLIENT_ID!,\n      clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n      scopes: [\"user:email\"],\n    }),\n  },\n  ...\n})\n```\n\nProviders 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.\n\n```ts\nimport { PasswordProvider } from \"@openauthjs\u002Fopenauth\u002Fprovider\u002Fpassword\"\n\nconst app = issuer({\n  providers: {\n    github: ...,\n    password: PasswordProvider(...),\n  },\n  ...\n})\n```\n\nThe password provider is quite complicated as username\u002Fpassword 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\u002Femail verification. In this case we'll log the code but you would send this over email.\n\n```ts\nimport { PasswordProvider } from \"@openauthjs\u002Fopenauth\u002Fprovider\u002Fpassword\"\nimport { PasswordUI } from \"@openauthjs\u002Fopenauth\u002Fui\u002Fpassword\"\n\nconst app = issuer({\n  providers: {\n    github: ...,\n    password: PasswordProvider(\n      PasswordUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  ...\n})\n```\n\nNext up is the `subjects` field. Subjects are what the access token generated at the end of the auth flow will map to. Under the hood, the access token is a JWT that contains this data. You will likely just have a single subject to start but you can define additional ones for different types of users.\n\n```ts\nimport { object, string } from \"valibot\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string(),\n    \u002F\u002F may want to add workspaceID here if doing a multi-tenant app\n    workspaceID: string(),\n  }),\n})\n```\n\nNote we are using [valibot](https:\u002F\u002Fgithub.com\u002Ffabian-hiller\u002Fvalibot) to define the shape of the subject so it can be validated properly. You can use any validation library that is following the [standard-schema specification](https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema) - the next version of Zod will support this.\n\nYou typically will want to place subjects in its own file as it can be imported by all of your apps. You can pass it to the issuer in the `subjects` field.\n\n```ts\nimport { subjects } from \".\u002Fsubjects.js\"\n\nconst app = issuer({\n  providers: { ... },\n  subjects,\n  ...\n})\n```\n\nNext we'll implement the `success` callback which receives the payload when a user successfully completes a provider flow.\n\n```ts\nconst app = issuer({\n  providers: { ... },\n  subjects,\n  async success(ctx, value) {\n    let userID\n    if (value.provider === \"password\") {\n      console.log(value.email)\n      userID = ... \u002F\u002F lookup user or create them\n    }\n    if (value.provider === \"github\") {\n      console.log(value.tokenset.access)\n      userID = ... \u002F\u002F lookup user or create them\n    }\n    return ctx.subject(\"user\", {\n      userID,\n      'a workspace id'\n    })\n  }\n})\n```\n\nNote all of this is typesafe - based on the configured providers you will receive different properties in the `value` object. Also the `subject` method will only accept properties. Note - most callbacks in OpenAuth can return a `Response` object. In this case if something goes wrong, you can return a `Response.redirect(\"...\")` sending them to a different place or rendering an error.\n\nNext 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.\n\n```ts\nimport { MemoryStorage } from \"@openauthjs\u002Fopenauth\u002Fstorage\u002Fmemory\"\n\nconst app = issuer({\n  providers: { ... },\n  subjects,\n  async success(ctx, value) { ... },\n  storage: MemoryStorage(),\n})\n```\n\nAnd now we are ready to deploy! Here's how you do that depending on your infrastructure.\n\n```ts\n\u002F\u002F Bun\nexport default app\n\n\u002F\u002F Cloudflare\nexport default app\n\n\u002F\u002F Lambda\nimport { handle } from \"hono\u002Faws-lambda\"\nexport const handler = handle(app)\n\n\u002F\u002F Node.js\nimport { serve } from \"@hono\u002Fnode-server\"\nserve(app)\n```\n\nYou now have a centralized auth server. Test it out by visiting `\u002F.well-known\u002Foauth-authorization-server` - you can see a live example [here](https:\u002F\u002Fauth.terminal.shop\u002F.well-known\u002Foauth-authorization-server).\n\n### Auth client\n\nSince 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:\n\n```ts\nimport { createClient } from \"@openauthjs\u002Fopenauth\u002Fclient\"\n\nconst client = createClient({\n  clientID: \"my-client\",\n  issuer: \"https:\u002F\u002Fauth.myserver.com\", \u002F\u002F url to the OpenAuth server\n})\n```\n\n#### SSR Sites\n\nIf your frontend has a server component you can use the code flow. Redirect the user here\n\n```ts\nconst { url } = await client.authorize(\n  \u003Credirect-uri>,\n  \"code\"\n)\n```\n\nYou can make up a `client_id` that represents your app. This will initiate the auth flow and user will be redirected to the `redirect_uri` you provided with a query parameter `code` which you can exchange for an access token.\n\n```ts\n\u002F\u002F the redirect_uri is the original redirect_uri you passed in and is used for verification\nconst tokens = await client.exchange(query.get(\"code\"), redirect_uri)\nconsole.log(tokens.access, tokens.refresh)\n```\n\nYou likely want to store both the access token and refresh token in an HTTP only cookie so they are sent up with future requests. Then you can use the `client` to verify the tokens.\n\n```ts\nconst verified = await client.verify(subjects, cookies.get(\"access_token\")!, {\n  refresh: cookies.get(\"refresh_token\") || undefined,\n})\nconsole.log(\n  verified.subject.type,\n  verified.subject.properties,\n  verified.refresh,\n  verified.access,\n)\n```\n\nPassing in the refresh token is optional but if you do, this function will automatically refresh the access token if it has expired. It will return a new access token and refresh token which you should set back into the cookies.\n\n#### SPA Sites, Mobile apps, etc\n\nIn cases where you do not have a server, you can use the `token` flow with `pkce` on the frontend.\n\n```ts\nconst { challenge, url } = await client.authorize(\u003Credirect_uri>, \"code\", { pkce: true })\nlocalStorage.setItem(\"challenge\", JSON.stringify(challenge))\nlocation.href = url\n```\n\nWhen the auth flow is complete the user's browser will be redirected to the `redirect_uri` with a `code` query parameter. You can then exchange the code for access\u002Frefresh tokens.\n\n```ts\nconst challenge = JSON.parse(localStorage.getItem(\"challenge\"))\nconst exchanged = await client.exchange(\n  query.get(\"code\"),\n  redirect_uri,\n  challenge.verifier,\n)\nif (exchanged.err) throw new Error(\"Invalid code\")\nlocalStorage.setItem(\"access_token\", exchanged.tokens.access)\nlocalStorage.setItem(\"refresh_token\", exchanged.tokens.refresh)\n```\n\nThen when you make requests to your API you can include the access token in the `Authorization` header.\n\n```ts\nconst accessToken = localStorage.getItem(\"access_token\")\nfetch(\"https:\u002F\u002Fauth.example.com\u002Fapi\u002Fuser\", {\n  headers: {\n    Authorization: `Bearer ${accessToken}`,\n  },\n})\n```\n\nAnd then you can verify the access token on the server.\n\n```ts\nconst verified = await client.verify(subjects, accessToken)\nconsole.log(verified.subject)\n```\n\n---\n\nOpenAuth is created by the maintainers of [SST](https:\u002F\u002Fsst.dev).\n\n**Join our community** [Discord](https:\u002F\u002Fsst.dev\u002Fdiscord) | [YouTube](https:\u002F\u002Fwww.youtube.com\u002Fc\u002Fsst-dev) | [X.com](https:\u002F\u002Fx.com\u002FSST_dev)\n","OpenAuth 是一个基于标准的身份验证提供者，适用于Web应用、移动应用、单页应用、API或第三方客户端。其核心功能包括通用性、自托管能力、遵循OAuth 2.0标准以及高度可定制的用户界面。OpenAuth可以作为独立服务部署，也可以嵌入到现有应用程序中，并且支持多种框架和平台。它完全运行在用户的基础设施上，能够部署于Node.js、Bun、AWS Lambda或Cloudflare Workers等环境。此外，OpenAuth提供了预构建的主题化UI，允许开发者根据需要进行个性化调整或选择不使用。该解决方案特别适合需要在一个中心位置管理多个不同类型应用认证的企业或团队使用。","2026-06-11 03:45:56","high_star"]