[
  {
    "path": ".changeset/README.md",
    "content": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works\nwith multi-package repos, or single-package repos to help you version and publish your code. You can\nfind the full documentation for it [in our repository](https://github.com/changesets/changesets)\n\nWe have a quick list of common questions to get you started engaging with this project in\n[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)\n"
  },
  {
    "path": ".changeset/commit.cjs",
    "content": "/** @type {import('@changesets/types').CommitFunctions[\"getAddMessage\"]} */\nmodule.exports.getAddMessage = async (changeset) => {\n  return changeset.summary;\n};\n"
  },
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@3.0.4/schema.json\",\n  \"changelog\": \"@changesets/cli/changelog\",\n  \"commit\": \"./commit.cjs\",\n  \"fixed\": [[\"@openauthjs/openauth\"]],\n  \"linked\": [],\n  \"access\": \"public\",\n  \"baseBranch\": \"master\",\n  \"updateInternalDependencies\": \"patch\",\n  \"ignore\": [\"@openauthjs/example-*\"]\n}\n"
  },
  {
    "path": ".changeset/popular-geese-reply.md",
    "content": "---\n\"@openauthjs/openauth\": patch\n---\n\nupdate google icon to comply with branding guidelines\n"
  },
  {
    "path": ".changeset/stupid-boats-play.md",
    "content": "---\n\"@openauthjs/openauth\": patch\n---\n\nallow auth style autodetection\n"
  },
  {
    "path": ".changeset/ten-pans-invent.md",
    "content": "---\n\"@openauthjs/openauth\": patch\n---\n\nadd linkedin adapter\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT",
    "content": "# Code of Conduct\n\nI don't typically set up a code of conduct for our projects but given this one is security related it will draw a very specific set of problems I want to avoid. There's only two rules\n\n1. Reporting security issues\n\nIf you find a security issue please report them to me directly on [X](https://twitter.com/thdxr) or [Bluesky](https://bsky.app/). Do not open a public issue or post publicly in case the issue can be exploited. Feel free to give us a window of time to respond before disclosing it publicly - that seems fair.\n\n2. Reporting \"security\" issues\n\nA lot of things that seem to fall in that first category are not really security problems, just tradeoffs that were made in the design of OpenAuth. Security products attract a lot of binary opinions like \"never use X\". We reject this type of thinking entirely - security is a spectrum of usability and infinitely optimizing for \"security\" does not yield a good product.\n\nAll discussions around the tradeoffs that were made must consider this - if you disagree with a decision you MUST articulate why the decision was probably made before you argue against it. Eg. \"X seem to be used because of benefit [a] and their downside [b] is mitigated by [c] BUT I do not think this is enough because of [d]\"\n\nWe do not tolerate wasting the maintainers time and forcing them to articulate this nuance. If something is not clear of course you can ask for clarification.\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: docs\n\non:\n  # Trigger the workflow every time you push to the `main` branch\n  # Using a different branch name? Replace `main` with your branch’s name\n  push:\n    branches: [master]\n  # Allows you to run this workflow manually from the Actions tab on GitHub.\n  workflow_dispatch:\n\n# Allow this job to clone the repo and create a page deployment\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout your repository using git\n        uses: actions/checkout@v4\n      - name: Install, build, and upload your site\n        uses: withastro/action@v3\n        with:\n          path: www\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n        with:\n          path: www\n"
  },
  {
    "path": ".github/workflows/format.yml",
    "content": "name: format\n\non:\n  push:\n    branches: [master]\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  format:\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: write\n      pull-requests: write\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: ${{ github.head_ref }}\n          fetch-depth: 0\n      - uses: oven-sh/setup-bun@v2\n      - run: |\n          git config --local user.email \"github-actions[bot]@users.noreply.github.com\"\n          git config --local user.name \"github-actions[bot]\"\n          ./scripts/format\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: release\n\non:\n  push:\n    branches:\n      - master\n\npermissions:\n  contents: write\n  pull-requests: write\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n  release:\n    name: release\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: oven-sh/setup-bun@v2\n      - run: bun install\n      - id: changesets\n        uses: changesets/action@v1\n        with:\n          publish: bun run release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: test\n\non:\n  push:\n    branches: [master]\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - uses: oven-sh/setup-bun@v2\n        with:\n          bun-version: latest\n      - run: bun install\n      - run: cd packages/openauth && bun run build\n      - run: cd packages/openauth && bun test\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules\n.sst\n.env\ndist\npersist.json\n.DS_Store\nnotes\n.nvim.lua\n.svelte-kit"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"semi\": false,\n}\n"
  },
  {
    "path": "CNAME",
    "content": "openauth.js.org\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 SST\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://openauth.js.org\">\n    <picture>\n      <source srcset=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/logo-dark.svg\" media=\"(prefers-color-scheme: dark)\">\n      <source srcset=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/logo-light.svg\" media=\"(prefers-color-scheme: light)\">\n      <img src=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/logo-light.svg\" alt=\"OpenAuth logo\">\n    </picture>\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"https://sst.dev/discord\"><img alt=\"Discord\" src=\"https://img.shields.io/discord/983865673656705025?style=flat-square&label=Discord\" /></a>\n  <a href=\"https://www.npmjs.com/package/@openauthjs/openauth\"><img alt=\"npm\" src=\"https://img.shields.io/npm/v/%40openauthjs%2Fcore?style=flat-square\" /></a>\n  <a href=\"https://github.com/toolbeam/openauth/actions/workflows/release.yml\"><img alt=\"Build status\" src=\"https://img.shields.io/github/actions/workflow/status/toolbeam/openauth/release.yml?style=flat-square&branch=master\" /></a>\n</p>\n\n---\n\n[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.\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<picture>\n  <source srcset=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/assets/themes-dark.png\" media=\"(prefers-color-scheme: dark)\">\n  <source srcset=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/assets/themes-light.png\" media=\"(prefers-color-scheme: dark)\">\n  <img src=\"https://raw.githubusercontent.com/toolbeam/identity/main/openauth/assets/themes-light.png\" alt=\"OpenAuth themes\">\n</picture>\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://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.\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/password 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/creation 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://sst.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/openauth` package.\n\n```ts\nimport { issuer } from \"@openauthjs/openauth\"\n```\n\nOpenAuth 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.\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/password or pin code. You can also implement your own. Let's try the GitHub provider.\n\n```ts\nimport { GithubProvider } from \"@openauthjs/openauth/provider/github\"\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/openauth/provider/password\"\n\nconst app = issuer({\n  providers: {\n    github: ...,\n    password: PasswordProvider(...),\n  },\n  ...\n})\n```\n\nThe 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.\n\n```ts\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\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    // 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://github.com/fabian-hiller/valibot) 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://github.com/standard-schema/standard-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 \"./subjects.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 = ... // lookup user or create them\n    }\n    if (value.provider === \"github\") {\n      console.log(value.tokenset.access)\n      userID = ... // 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/openauth/storage/memory\"\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// Bun\nexport default app\n\n// Cloudflare\nexport default app\n\n// Lambda\nimport { handle } from \"hono/aws-lambda\"\nexport const handler = handle(app)\n\n// Node.js\nimport { serve } from \"@hono/node-server\"\nserve(app)\n```\n\nYou now have a centralized auth server. Test it out by visiting `/.well-known/oauth-authorization-server` - you can see a live example [here](https://auth.terminal.shop/.well-known/oauth-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/openauth/client\"\n\nconst client = createClient({\n  clientID: \"my-client\",\n  issuer: \"https://auth.myserver.com\", // 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  <redirect-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// 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(<redirect_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/refresh 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://auth.example.com/api/user\", {\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://sst.dev).\n\n**Join our community** [Discord](https://sst.dev/discord) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/SST_dev)\n"
  },
  {
    "path": "bunfig.toml",
    "content": "[install]\nexact = true\n"
  },
  {
    "path": "examples/.gitignore",
    "content": "# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore\n\n# Logs\n\nlogs\n_.log\nnpm-debug.log_\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Caches\n\n.cache\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\n\nreport.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json\n\n# Runtime data\n\npids\n_.pid\n_.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\n\nlib-cov\n\n# Coverage directory used by tools like istanbul\n\ncoverage\n*.lcov\n\n# nyc test coverage\n\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n\n.grunt\n\n# Bower dependency directory (https://bower.io/)\n\nbower_components\n\n# node-waf configuration\n\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\n\nbuild/Release\n\n# Dependency directories\n\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\n\nweb_modules/\n\n# TypeScript cache\n\n*.tsbuildinfo\n\n# Optional npm cache directory\n\n.npm\n\n# Optional eslint cache\n\n.eslintcache\n\n# Optional stylelint cache\n\n.stylelintcache\n\n# Microbundle cache\n\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n\n.node_repl_history\n\n# Output of 'npm pack'\n\n*.tgz\n\n# Yarn Integrity file\n\n.yarn-integrity\n\n# dotenv environment variable files\n\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n\n.parcel-cache\n\n# Next.js build output\n\n.next\nout\n\n# Nuxt.js build / generate output\n\n.nuxt\ndist\n\n# Gatsby files\n\n# Comment in the public line in if your project uses Gatsby and not Next.js\n\n# https://nextjs.org/blog/next-9-1#public-directory-support\n\n# public\n\n# vuepress build output\n\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n\n.temp\n\n# Docusaurus cache and generated files\n\n.docusaurus\n\n# Serverless directories\n\n.serverless/\n\n# FuseBox cache\n\n.fusebox/\n\n# DynamoDB Local files\n\n.dynamodb/\n\n# TernJS port file\n\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n\n.vscode-test\n\n# yarn v2\n\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n# IntelliJ based IDEs\n.idea\n\n# Finder (MacOS) folder config\n.DS_Store\n\n# sst\n.sst\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\nThere are two sets of examples here, issuers and clients. Issuers are examples of setting up an OpenAuth server. The clients are examples of using OpenAuth in a client application and work with any of the issuer servers.\n\nThe fastest way to play around is to use the bun issuer. You can bring it up with:\n\n```shell\n$ bun run --hot ./issuer/bun/issuer.ts\n```\n\nYou might have to install some workspace packages first, run this in the root:\n\n```shell\n$ bun install\n$ cd packages/openauth\n$ bun run build\n```\n\nThis will bring it up on port 3000. Then try one of the clients - for example the astro one.\n\n```\n$ cd client/astro\n$ bun dev\n```\n\nNow visit `http://localhost:4321` (the astro app) and experience the auth flow.\n\nOr head over to `http://localhost:3000/password/authorize` to try the password flow directly.\n"
  },
  {
    "path": "examples/client/astro/.gitignore",
    "content": "# build output\ndist/\n\n# generated types\n.astro/\n\n# dependencies\nnode_modules/\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# environment variables\n.env\n.env.production\n\n# macOS-specific files\n.DS_Store\n\n# jetbrains setting folder\n.idea/\n"
  },
  {
    "path": "examples/client/astro/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"astro-build.astro-vscode\"],\n  \"unwantedRecommendations\": []\n}\n"
  },
  {
    "path": "examples/client/astro/.vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"command\": \"./node_modules/.bin/astro dev\",\n      \"name\": \"Development server\",\n      \"request\": \"launch\",\n      \"type\": \"node-terminal\"\n    }\n  ]\n}\n"
  },
  {
    "path": "examples/client/astro/README.md",
    "content": "# OpenAuth Astro Client\n\nThe files to note are\n\n- `src/auth.ts` - creates the client that is used to interact with the auth server\n- `src/middleware.ts` - middleware that runs to verify access tokens, refresh them if out of date, and redirect the user to the auth server if they are not logged in\n- `src/pages/callback.ts` - the callback endpoint that receives the auth code and exchanges it for an access/refresh token\n"
  },
  {
    "path": "examples/client/astro/astro.config.mjs",
    "content": "// @ts-check\nimport { defineConfig } from \"astro/config\";\n\n// https://astro.build/config\nexport default defineConfig({\n  output: \"server\",\n  server: {\n    host: \"0.0.0.0\",\n  },\n});\n"
  },
  {
    "path": "examples/client/astro/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-client-astro\",\n  \"type\": \"module\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"build\": \"astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\"\n  },\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\",\n    \"astro\": \"5.0.2\"\n  }\n}\n"
  },
  {
    "path": "examples/client/astro/src/auth.ts",
    "content": "import { createClient } from \"@openauthjs/openauth/client\"\nimport type { APIContext } from \"astro\"\nexport { subjects } from \"../../../subjects\"\n\nexport const client = createClient({\n  clientID: \"astro\",\n  issuer: \"http://localhost:3000\",\n})\n\nexport function setTokens(ctx: APIContext, access: string, refresh: string) {\n  ctx.cookies.set(\"refresh_token\", refresh, {\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  ctx.cookies.set(\"access_token\", access, {\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n"
  },
  {
    "path": "examples/client/astro/src/components/Welcome.astro",
    "content": "---\nimport astroLogo from '../assets/astro.svg';\nimport background from '../assets/background.svg';\n---\n\n<div id=\"container\">\n\t<img id=\"background\" src={background.src} alt=\"\" fetchpriority=\"high\" />\n\t<main>\n\t\t<section id=\"hero\">\n\t\t\t<a href=\"https://astro.build\"\n\t\t\t\t><img src={astroLogo.src} width=\"115\" height=\"48\" alt=\"Astro Homepage\" /></a\n\t\t\t>\n\t\t\t<h1>\n\t\t\t\tTo get started, open the <code><pre>src/pages</pre></code> directory in your project.\n\t\t\t</h1>\n\t\t\t<section id=\"links\">\n\t\t\t\t<a class=\"button\" href=\"https://docs.astro.build\">Read our docs</a>\n\t\t\t\t<a href=\"https://astro.build/chat\"\n\t\t\t\t\t>Join our Discord <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 127.14 96.36\"\n\t\t\t\t\t\t><path\n\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\td=\"M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z\"\n\t\t\t\t\t\t></path></svg\n\t\t\t\t\t>\n\t\t\t\t</a>\n\t\t\t</section>\n\t\t</section>\n\t</main>\n\n\t<a href=\"https://astro.build/blog/astro-5/\" id=\"news\" class=\"box\">\n\t\t<svg width=\"32\" height=\"32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t><path\n\t\t\t\td=\"M24.667 12c1.333 1.414 2 3.192 2 5.334 0 4.62-4.934 5.7-7.334 12C18.444 28.567 18 27.456 18 26c0-4.642 6.667-7.053 6.667-14Zm-5.334-5.333c1.6 1.65 2.4 3.43 2.4 5.333 0 6.602-8.06 7.59-6.4 17.334C13.111 27.787 12 25.564 12 22.666c0-4.434 7.333-8 7.333-16Zm-6-5.333C15.111 3.555 16 5.556 16 7.333c0 8.333-11.333 10.962-5.333 22-3.488-.774-6-4-6-8 0-8.667 8.666-10 8.666-20Z\"\n\t\t\t\tfill=\"#111827\"></path></svg\n\t\t>\n\t\t<h2>What's New in Astro 5.0?</h2>\n\t\t<p>\n\t\t\tFrom content layers to server islands, click to learn more about the new features and\n\t\t\timprovements in Astro 5.0\n\t\t</p>\n\t</a>\n</div>\n\n<style>\n\t#background {\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tz-index: -1;\n\t\tfilter: blur(100px);\n\t}\n\n\t#container {\n\t\tfont-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;\n\t\theight: 100%;\n\t}\n\n\tmain {\n\t\theight: 100%;\n\t\tdisplay: flex;\n\t\tjustify-content: center;\n\t}\n\n\t#hero {\n\t\tdisplay: flex;\n\t\talign-items: start;\n\t\tflex-direction: column;\n\t\tjustify-content: center;\n\t\tpadding: 16px;\n\t}\n\n\th1 {\n\t\tfont-size: 22px;\n\t\tmargin-top: 0.25em;\n\t}\n\n\t#links {\n\t\tdisplay: flex;\n\t\tgap: 16px;\n\t}\n\n\t#links a {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tpadding: 10px 12px;\n\t\tcolor: #111827;\n\t\ttext-decoration: none;\n\t\ttransition: color 0.2s;\n\t}\n\n\t#links a:hover {\n\t\tcolor: rgb(78, 80, 86);\n\t}\n\n\t#links a svg {\n\t\theight: 1em;\n\t\tmargin-left: 8px;\n\t}\n\n\t#links a.button {\n\t\tcolor: white;\n\t\tbackground: linear-gradient(83.21deg, #3245ff 0%, #bc52ee 100%);\n\t\tbox-shadow:\n\t\t\tinset 0 0 0 1px rgba(255, 255, 255, 0.12),\n\t\t\tinset 0 -2px 0 rgba(0, 0, 0, 0.24);\n\t\tborder-radius: 10px;\n\t}\n\n\t#links a.button:hover {\n\t\tcolor: rgb(230, 230, 230);\n\t\tbox-shadow: none;\n\t}\n\n\tpre {\n\t\tfont-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas,\n\t\t\t'DejaVu Sans Mono', monospace;\n\t\tfont-weight: normal;\n\t\tbackground: linear-gradient(14deg, #d83333 0%, #f041ff 100%);\n\t\t-webkit-background-clip: text;\n\t\t-webkit-text-fill-color: transparent;\n\t\tbackground-clip: text;\n\t\tmargin: 0;\n\t}\n\n\th2 {\n\t\tmargin: 0 0 1em;\n\t\tfont-weight: normal;\n\t\tcolor: #111827;\n\t\tfont-size: 20px;\n\t}\n\n\tp {\n\t\tcolor: #4b5563;\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tletter-spacing: -0.006em;\n\t\tmargin: 0;\n\t}\n\n\tcode {\n\t\tdisplay: inline-block;\n\t\tbackground:\n\t\t\tlinear-gradient(66.77deg, #f3cddd 0%, #f5cee7 100%) padding-box,\n\t\t\tlinear-gradient(155deg, #d83333 0%, #f041ff 18%, #f5cee7 45%) border-box;\n\t\tborder-radius: 8px;\n\t\tborder: 1px solid transparent;\n\t\tpadding: 6px 8px;\n\t}\n\n\t.box {\n\t\tpadding: 16px;\n\t\tbackground: rgba(255, 255, 255, 1);\n\t\tborder-radius: 16px;\n\t\tborder: 1px solid white;\n\t}\n\n\t#news {\n\t\tposition: absolute;\n\t\tbottom: 16px;\n\t\tright: 16px;\n\t\tmax-width: 300px;\n\t\ttext-decoration: none;\n\t\ttransition: background 0.2s;\n\t\tbackdrop-filter: blur(50px);\n\t}\n\n\t#news:hover {\n\t\tbackground: rgba(255, 255, 255, 0.55);\n\t}\n\n\t@media screen and (max-height: 368px) {\n\t\t#news {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t@media screen and (max-width: 768px) {\n\t\t#container {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t}\n\n\t\t#hero {\n\t\t\tdisplay: block;\n\t\t\tpadding-top: 10%;\n\t\t}\n\n\t\t#links {\n\t\t\tflex-wrap: wrap;\n\t\t}\n\n\t\t#links a.button {\n\t\t\tpadding: 14px 18px;\n\t\t}\n\n\t\t#news {\n\t\t\tright: 16px;\n\t\t\tleft: 16px;\n\t\t\tbottom: 2.5rem;\n\t\t\tmax-width: 100%;\n\t\t}\n\n\t\th1 {\n\t\t\tline-height: 1.5;\n\t\t}\n\t}\n</style>\n"
  },
  {
    "path": "examples/client/astro/src/env.d.ts",
    "content": "import type { SubjectPayload } from \"@openauthjs/openauth/subject\"\nimport { subjects } from \"./auth\"\n\ndeclare global {\n  declare namespace App {\n    interface Locals {\n      subject?: SubjectPayload<typeof subjects>\n    }\n  }\n}\n"
  },
  {
    "path": "examples/client/astro/src/layouts/Layout.astro",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width\" />\n\t\t<link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n\t\t<meta name=\"generator\" content={Astro.generator} />\n\t\t<title>Astro Basics</title>\n\t</head>\n\t<body>\n\t\t<slot />\n\t</body>\n</html>\n\n<style>\n\thtml,\n\tbody {\n\t\tmargin: 0;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t}\n</style>\n"
  },
  {
    "path": "examples/client/astro/src/middleware.ts",
    "content": "import { defineMiddleware } from \"astro:middleware\"\nimport { subjects } from \"../../../subjects\"\nimport { client, setTokens } from \"./auth\"\n\nexport const onRequest = defineMiddleware(async (ctx, next) => {\n  if (ctx.routePattern === \"/callback\") {\n    return next()\n  }\n\n  try {\n    const accessToken = ctx.cookies.get(\"access_token\")\n    if (accessToken) {\n      const refreshToken = ctx.cookies.get(\"refresh_token\")\n      const verified = await client.verify(subjects, accessToken.value, {\n        refresh: refreshToken?.value,\n      })\n      if (!verified.err) {\n        if (verified.tokens)\n          setTokens(ctx, verified.tokens.access, verified.tokens.refresh)\n        ctx.locals.subject = verified.subject\n        return next()\n      }\n    }\n  } catch (e) {}\n\n  const { url } = await client.authorize(\n    new URL(ctx.request.url).origin + \"/callback\",\n    \"code\",\n  )\n  return Response.redirect(url, 302)\n})\n"
  },
  {
    "path": "examples/client/astro/src/pages/callback.ts",
    "content": "import type { APIRoute } from \"astro\"\nimport { client, setTokens } from \"../auth\"\n\nexport const GET: APIRoute = async (ctx) => {\n  const code = ctx.url.searchParams.get(\"code\")\n  try {\n    const tokens = await client.exchange(code!, ctx.url.origin + \"/callback\")\n    if (!tokens.err) {\n      setTokens(ctx, tokens.tokens.access, tokens.tokens.refresh)\n    } else {\n      throw tokens.err\n    }\n    return ctx.redirect(\"/\", 302)\n  } catch (e) {\n    return Response.json(e, {\n      status: 400,\n    })\n  }\n}\n"
  },
  {
    "path": "examples/client/astro/src/pages/index.astro",
    "content": "---\nimport Welcome from '../components/Welcome.astro';\nimport Layout from '../layouts/Layout.astro';\n\n// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build\n// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.\n---\n\n<Layout>\n  Hello {Astro.locals.subject?.properties.id}\n</Layout>\n"
  },
  {
    "path": "examples/client/astro/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"include\": [\".astro/types.d.ts\", \"**/*\"],\n  \"exclude\": [\"dist\"]\n}\n"
  },
  {
    "path": "examples/client/cloudflare-api/api.ts",
    "content": "import type { Service } from \"@cloudflare/workers-types\"\nimport { createClient } from \"@openauthjs/openauth/client\"\nimport { subjects } from \"../../subjects\"\n\ninterface Env {\n  OPENAUTH_ISSUER: string\n  Auth: Service\n  CloudflareAuth: Service\n}\n\nexport default {\n  async fetch(request: Request, env: Env) {\n    const client = createClient({\n      clientID: \"cloudflare-api\",\n      // enables worker to worker communication if issuer is also a worker\n      fetch: (input, init) => env.CloudflareAuth.fetch(input, init),\n      issuer: env.OPENAUTH_ISSUER,\n    })\n    const url = new URL(request.url)\n    const redirectURI = url.origin + \"/callback\"\n\n    switch (url.pathname) {\n      case \"/callback\":\n        try {\n          const code = url.searchParams.get(\"code\")!\n          const exchanged = await client.exchange(code, redirectURI)\n          if (exchanged.err) throw new Error(\"Invalid code\")\n          const response = new Response(null, { status: 302, headers: {} })\n          response.headers.set(\"Location\", url.origin)\n          setSession(\n            response,\n            exchanged.tokens.access,\n            exchanged.tokens.refresh,\n          )\n          return response\n        } catch (e: any) {\n          return new Response(e.toString())\n        }\n      case \"/authorize\":\n        return Response.redirect(\n          await client.authorize(redirectURI, \"code\").then((v) => v.url),\n          302,\n        )\n      case \"/\":\n        const cookies = new URLSearchParams(\n          request.headers.get(\"cookie\")?.replaceAll(\"; \", \"&\"),\n        )\n        const verified = await client.verify(\n          subjects,\n          cookies.get(\"access_token\")!,\n          {\n            refresh: cookies.get(\"refresh_token\") || undefined,\n          },\n        )\n        if (verified.err)\n          return Response.redirect(url.origin + \"/authorize\", 302)\n        const resp = Response.json(verified.subject)\n        if (verified.tokens)\n          setSession(resp, verified.tokens.access, verified.tokens.refresh)\n        return resp\n      default:\n        return new Response(\"Not found\", { status: 404 })\n    }\n  },\n}\n\nfunction setSession(response: Response, access: string, refresh: string) {\n  if (access) {\n    response.headers.append(\n      \"Set-Cookie\",\n      `access_token=${access}; HttpOnly; SameSite=Strict; Path=/; Max-Age=2147483647`,\n    )\n  }\n  if (refresh) {\n    response.headers.append(\n      \"Set-Cookie\",\n      `refresh_token=${refresh}; HttpOnly; SameSite=Strict; Path=/; Max-Age=2147483647`,\n    )\n  }\n}\n"
  },
  {
    "path": "examples/client/cloudflare-api/package.json",
    "content": "{\n  \"name\": \"cloudflare-api\",\n  \"version\": \"0.0.0\",\n  \"private\": true\n}\n"
  },
  {
    "path": "examples/client/jwt-api/CHANGELOG.md",
    "content": "# jwt-api\n\n## 1.0.1\n\n### Patch Changes\n\n- Updated dependencies [8b5f490]\n  - @openauthjs/openauth@0.2.4\n"
  },
  {
    "path": "examples/client/jwt-api/README.md",
    "content": "# JWT API\n\nThis simple API verifies the `Authorization` header using the OpenAuth client and returns the subject.\n\nRun it using.\n\n```bash\nbun run --hot index.ts\n```\n\nThen visit `http://localhost:3001/` in your browser.\n\nThis works with the [React Client](../react) example that makes a call to this API after the auth flow.\n"
  },
  {
    "path": "examples/client/jwt-api/index.ts",
    "content": "import { createClient } from \"@openauthjs/openauth/client\"\nimport { subjects } from \"../../subjects\"\n\nconst headers = {\n  \"Access-Control-Allow-Origin\": \"*\",\n  \"Access-Control-Allow-Headers\": \"*\",\n  \"Access-Control-Allow-Methods\": \"*\",\n}\n\nconst client = createClient({\n  clientID: \"jwt-api\",\n  issuer: \"http://localhost:3000\",\n})\n\nconst server = Bun.serve({\n  port: 3001,\n  async fetch(req) {\n    const url = new URL(req.url)\n\n    if (req.method === \"OPTIONS\") {\n      return new Response(null, { headers })\n    }\n\n    if (url.pathname === \"/\" && req.method === \"GET\") {\n      const authHeader = req.headers.get(\"Authorization\")\n\n      if (!authHeader) {\n        return new Response(\"401\", { headers, status: 401 })\n      }\n\n      const token = authHeader.split(\" \")[1]\n      const verified = await client.verify(subjects, token)\n\n      if (verified.err) {\n        return new Response(\"401\", { headers, status: 401 })\n      }\n\n      return new Response(verified.subject.properties.id, { headers })\n    }\n\n    return new Response(\"404\", { status: 404 })\n  },\n})\n\nconsole.log(`Listening on ${server.url}`)\n"
  },
  {
    "path": "examples/client/jwt-api/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-jwt-api\",\n  \"version\": \"1.0.1\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "examples/client/lambda-api/api.ts",
    "content": "import { Context, Hono } from \"hono\"\nimport { getCookie, setCookie } from \"hono/cookie\"\nimport { createClient } from \"@openauthjs/openauth/client\"\nimport { handle } from \"hono/aws-lambda\"\nimport { subjects } from \"../../subjects\"\n\nconst client = createClient({\n  clientID: \"lambda-api\",\n})\n\nconst app = new Hono()\n  .get(\"/authorize\", async (c) => {\n    const origin = new URL(c.req.url).origin\n    const { url } = await client.authorize(origin + \"/callback\", \"code\")\n    return c.redirect(url, 302)\n  })\n  .get(\"/callback\", async (c) => {\n    const origin = new URL(c.req.url).origin\n    try {\n      const code = c.req.query(\"code\")\n      if (!code) throw new Error(\"Missing code\")\n      const exchanged = await client.exchange(code, origin + \"/callback\")\n      if (exchanged.err)\n        return new Response(exchanged.err.toString(), {\n          status: 400,\n        })\n      setSession(c, exchanged.tokens.access, exchanged.tokens.refresh)\n      return c.redirect(\"/\", 302)\n    } catch (e: any) {\n      return new Response(e.toString())\n    }\n  })\n  .get(\"/\", async (c) => {\n    const access = getCookie(c, \"access_token\")\n    const refresh = getCookie(c, \"refresh_token\")\n    try {\n      const verified = await client.verify(subjects, access!, {\n        refresh,\n      })\n      if (verified.err) throw new Error(\"Invalid access token\")\n      if (verified.tokens)\n        setSession(c, verified.tokens.access, verified.tokens.refresh)\n      return c.json(verified.subject)\n    } catch (e) {\n      console.error(e)\n      return c.redirect(\"/authorize\", 302)\n    }\n  })\n\nexport const handler = handle(app)\n\nfunction setSession(c: Context, accessToken?: string, refreshToken?: string) {\n  if (accessToken) {\n    setCookie(c, \"access_token\", accessToken, {\n      httpOnly: true,\n      sameSite: \"Strict\",\n      path: \"/\",\n      maxAge: 34560000,\n    })\n  }\n  if (refreshToken) {\n    setCookie(c, \"refresh_token\", refreshToken, {\n      httpOnly: true,\n      sameSite: \"Strict\",\n      path: \"/\",\n      maxAge: 34560000,\n    })\n  }\n}\n"
  },
  {
    "path": "examples/client/lambda-api/package.json",
    "content": "{\n  \"name\": \"lambda-api\",\n  \"version\": \"0.0.0\",\n  \"private\": true\n}\n"
  },
  {
    "path": "examples/client/nextjs/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "examples/client/nextjs/CHANGELOG.md",
    "content": "# nextjs\n\n## 0.1.6\n\n### Patch Changes\n\n- Updated dependencies [8b5f490]\n  - @openauthjs/openauth@0.2.4\n\n## 0.1.5\n\n### Patch Changes\n\n- Updated dependencies [80238de]\n  - @openauthjs/openauth@0.2.3\n\n## 0.1.4\n\n### Patch Changes\n\n- Updated dependencies [6da8647]\n  - @openauthjs/openauth@0.2.2\n\n## 0.1.3\n\n### Patch Changes\n\n- Updated dependencies [83125f1]\n  - @openauthjs/openauth@0.2.1\n\n## 0.1.2\n\n### Patch Changes\n\n- Updated dependencies [8c3f050]\n- Updated dependencies [0f93def]\n  - @openauthjs/openauth@0.2.0\n\n## 0.1.1\n\n### Patch Changes\n\n- Updated dependencies [584728f]\n- Updated dependencies [41acdc2]\n- Updated dependencies [2aa531b]\n  - @openauthjs/openauth@0.1.2\n"
  },
  {
    "path": "examples/client/nextjs/README.md",
    "content": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).\n\n## Getting Started\n\nMake sure your OpenAuth server is running at `http://localhost:3000`.\n\nThen start the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3001](http://localhost:3001) with your browser and click **Login with OpenAuth** to start the auth flow.\n\n## Files\n\n- [`app/auth.ts`](app/auth.ts): OpenAuth client and helper to set tokens in cookies.\n- [`app/actions.ts`](app/actions.ts): Actions to get current logged in user, and to login and logout.\n- [`app/api/callback/route.ts`](app/api/callback/route.ts): Callback route for OpenAuth.\n- [`app/page.tsx`](app/page.tsx): Shows login and logout buttons and the current user.\n"
  },
  {
    "path": "examples/client/nextjs/app/actions.ts",
    "content": "\"use server\"\n\nimport { redirect } from \"next/navigation\"\nimport { headers as getHeaders, cookies as getCookies } from \"next/headers\"\nimport { client, subjects, setTokens } from \"./auth\"\n\nexport async function auth() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (!accessToken) {\n    return false\n  }\n\n  const verified = await client.verify(subjects, accessToken.value, {\n    refresh: refreshToken?.value,\n  })\n\n  if (verified.err) {\n    return false\n  }\n  if (verified.tokens) {\n    await setTokens(verified.tokens.access, verified.tokens.refresh)\n  }\n\n  return verified.subject\n}\n\nexport async function login() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (accessToken) {\n    const verified = await client.verify(subjects, accessToken.value, {\n      refresh: refreshToken?.value,\n    })\n    if (!verified.err && verified.tokens) {\n      await setTokens(verified.tokens.access, verified.tokens.refresh)\n      redirect(\"/\")\n    }\n  }\n\n  const headers = await getHeaders()\n  const host = headers.get(\"host\")\n  const protocol = host?.includes(\"localhost\") ? \"http\" : \"https\"\n  const { url } = await client.authorize(\n    `${protocol}://${host}/api/callback`,\n    \"code\",\n  )\n  redirect(url)\n}\n\nexport async function logout() {\n  const cookies = await getCookies()\n  cookies.delete(\"access_token\")\n  cookies.delete(\"refresh_token\")\n\n  redirect(\"/\")\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/api/callback/route.ts",
    "content": "import { client, setTokens } from \"../../auth\"\nimport { type NextRequest, NextResponse } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  const url = new URL(req.url)\n  const code = url.searchParams.get(\"code\")\n  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)\n  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })\n  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)\n  return NextResponse.redirect(`${url.origin}/`)\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/auth.ts",
    "content": "import { createClient } from \"@openauthjs/openauth/client\"\nimport { cookies as getCookies } from \"next/headers\"\nexport { subjects } from \"../../../subjects\"\n\nexport const client = createClient({\n  clientID: \"nextjs\",\n  issuer: \"http://localhost:3000\",\n})\n\nexport async function setTokens(access: string, refresh: string) {\n  const cookies = await getCookies()\n\n  cookies.set({\n    name: \"access_token\",\n    value: access,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  cookies.set({\n    name: \"refresh_token\",\n    value: refresh,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/globals.css",
    "content": ":root {\n  --background: #ffffff;\n  --foreground: #171717;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --background: #0a0a0a;\n    --foreground: #ededed;\n  }\n}\n\nhtml,\nbody {\n  max-width: 100vw;\n  overflow-x: hidden;\n}\n\nbody {\n  color: var(--foreground);\n  background: var(--background);\n  font-family: Arial, Helvetica, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n  padding: 0;\n  margin: 0;\n}\n\na {\n  color: inherit;\n  text-decoration: none;\n}\n\n@media (prefers-color-scheme: dark) {\n  html {\n    color-scheme: dark;\n  }\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/layout.tsx",
    "content": "import type { Metadata } from \"next\"\nimport { Geist, Geist_Mono } from \"next/font/google\"\nimport \"./globals.css\"\n\nconst geistSans = Geist({\n  variable: \"--font-geist-sans\",\n  subsets: [\"latin\"],\n})\n\nconst geistMono = Geist_Mono({\n  variable: \"--font-geist-mono\",\n  subsets: [\"latin\"],\n})\n\nexport const metadata: Metadata = {\n  title: \"Create Next App\",\n  description: \"Generated by create next app\",\n}\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode\n}>) {\n  return (\n    <html lang=\"en\">\n      <body className={`${geistSans.variable} ${geistMono.variable}`}>\n        {children}\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/page.module.css",
    "content": ".page {\n  --gray-rgb: 0, 0, 0;\n  --gray-alpha-200: rgba(var(--gray-rgb), 0.08);\n  --gray-alpha-100: rgba(var(--gray-rgb), 0.05);\n\n  --button-primary-hover: #383838;\n  --button-secondary-hover: #f2f2f2;\n\n  display: grid;\n  grid-template-rows: 20px 1fr 20px;\n  align-items: center;\n  justify-items: center;\n  min-height: 100svh;\n  padding: 80px;\n  gap: 64px;\n  font-family: var(--font-geist-sans);\n}\n\n@media (prefers-color-scheme: dark) {\n  .page {\n    --gray-rgb: 255, 255, 255;\n    --gray-alpha-200: rgba(var(--gray-rgb), 0.145);\n    --gray-alpha-100: rgba(var(--gray-rgb), 0.06);\n\n    --button-primary-hover: #ccc;\n    --button-secondary-hover: #1a1a1a;\n  }\n}\n\n.main {\n  display: flex;\n  flex-direction: column;\n  gap: 32px;\n  grid-row-start: 2;\n}\n\n.main ol {\n  font-family: var(--font-geist-mono);\n  padding-left: 0;\n  margin: 0;\n  font-size: 14px;\n  line-height: 24px;\n  letter-spacing: -0.01em;\n  list-style-position: inside;\n}\n\n.main li:not(:last-of-type) {\n  margin-bottom: 8px;\n}\n\n.main code {\n  font-family: inherit;\n  background: var(--gray-alpha-100);\n  padding: 2px 4px;\n  border-radius: 4px;\n  font-weight: 600;\n}\n\n.ctas {\n  display: flex;\n  gap: 16px;\n}\n\n.ctas button {\n  appearance: none;\n  background: transparent;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\nbutton.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\nbutton.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n\n.footer {\n  grid-row-start: 3;\n  display: flex;\n  gap: 24px;\n}\n\n.footer a {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.footer img {\n  flex-shrink: 0;\n}\n\n/* Enable hover only on non-touch devices */\n@media (hover: hover) and (pointer: fine) {\n  a.primary:hover {\n    background: var(--button-primary-hover);\n    border-color: transparent;\n  }\n\n  a.secondary:hover {\n    background: var(--button-secondary-hover);\n    border-color: transparent;\n  }\n\n  .footer a:hover {\n    text-decoration: underline;\n    text-underline-offset: 4px;\n  }\n}\n\n@media (max-width: 600px) {\n  .page {\n    padding: 32px;\n    padding-bottom: 80px;\n  }\n\n  .main {\n    align-items: center;\n  }\n\n  .main ol {\n    text-align: center;\n  }\n\n  .ctas {\n    flex-direction: column;\n  }\n\n  .ctas a {\n    font-size: 14px;\n    height: 40px;\n    padding: 0 16px;\n  }\n\n  a.secondary {\n    min-width: auto;\n  }\n\n  .footer {\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  .logo {\n    filter: invert();\n  }\n}\n"
  },
  {
    "path": "examples/client/nextjs/app/page.tsx",
    "content": "import Image from \"next/image\"\nimport { auth, login, logout } from \"./actions\"\nimport styles from \"./page.module.css\"\n\nexport default async function Home() {\n  const subject = await auth()\n\n  return (\n    <div className={styles.page}>\n      <main className={styles.main}>\n        <Image\n          className={styles.logo}\n          src=\"/next.svg\"\n          alt=\"Next.js logo\"\n          width={180}\n          height={38}\n          priority\n        />\n        <ol>\n          {subject ? (\n            <>\n              <li>\n                Logged in as <code>{subject.properties.id}</code>.\n              </li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          ) : (\n            <>\n              <li>Login with your email and password.</li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          )}\n        </ol>\n\n        <div className={styles.ctas}>\n          {subject ? (\n            <form action={logout}>\n              <button className={styles.secondary}>Logout</button>\n            </form>\n          ) : (\n            <form action={login}>\n              <button className={styles.primary}>Login with OpenAuth</button>\n            </form>\n          )}\n        </div>\n      </main>\n      <footer className={styles.footer}>\n        <a\n          href=\"https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          <Image\n            aria-hidden\n            src=\"/file.svg\"\n            alt=\"File icon\"\n            width={16}\n            height={16}\n          />\n          Learn\n        </a>\n        <a\n          href=\"https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          <Image\n            aria-hidden\n            src=\"/window.svg\"\n            alt=\"Window icon\"\n            width={16}\n            height={16}\n          />\n          Examples\n        </a>\n        <a\n          href=\"https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          <Image\n            aria-hidden\n            src=\"/globe.svg\"\n            alt=\"Globe icon\"\n            width={16}\n            height={16}\n          />\n          Go to nextjs.org →\n        </a>\n      </footer>\n    </div>\n  )\n}\n"
  },
  {
    "path": "examples/client/nextjs/next.config.ts",
    "content": "import type { NextConfig } from \"next\"\n\nconst nextConfig: NextConfig = {\n  /* config options here */\n}\n\nexport default nextConfig\n"
  },
  {
    "path": "examples/client/nextjs/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-nextjs\",\n  \"version\": \"0.1.6\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\",\n    \"next\": \"15.1.0\",\n    \"react\": \"19.0.0\",\n    \"react-dom\": \"19.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"22.10.1\",\n    \"@types/react\": \"19.0.1\",\n    \"@types/react-dom\": \"19.0.2\",\n    \"typescript\": \"5.6.3\"\n  }\n}\n"
  },
  {
    "path": "examples/client/nextjs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/client/react/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/client/react/README.md",
    "content": "# React SPA Auth\n\nThis uses the token + pkce flow to authenticate a user. Start it using.\n\n```bash\nbun run dev\n```\n\nThen visit `http://localhost:5173` in your browser.\n\nIt needs the OpenAuth server running at `http://localhost:3000`. Start it from the `examples/` dir using.\n\n```bash\nbun run --hot issuer/bun/issuer.ts\n```\n\nYou might have to install some workspace packages first, run this in the root:\n\n```bash\n$ bun install\n$ cd packages/openauth\n$ bun run build\n```\n\nAnd optionally a JWT API running to get the user subject on `http://localhost:3001`. Start it using.\n\n```bash\nbun run --hot client/jwt-api/index.ts\n```\n"
  },
  {
    "path": "examples/client/react/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/client/react/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-react\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\",\n    \"react\": \"19.0.0\",\n    \"react-dom\": \"19.0.0\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.15.0\",\n    \"@types/react\": \"19.0.1\",\n    \"@types/react-dom\": \"19.0.2\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"eslint\": \"^9.15.0\",\n    \"eslint-plugin-react-hooks\": \"^5.0.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.14\",\n    \"globals\": \"^15.12.0\",\n    \"typescript\": \"5.6.3\",\n    \"typescript-eslint\": \"^8.15.0\",\n    \"vite\": \"^6.0.1\"\n  }\n}\n"
  },
  {
    "path": "examples/client/react/src/App.tsx",
    "content": "import { useState } from \"react\"\nimport { useAuth } from \"./AuthContext\"\n\nfunction App() {\n  const auth = useAuth()\n  const [status, setStatus] = useState(\"\")\n\n  async function callApi() {\n    const res = await fetch(\"http://localhost:3001/\", {\n      headers: {\n        Authorization: `Bearer ${await auth.getToken()}`,\n      },\n    })\n\n    setStatus(res.ok ? \"success\" : \"error\")\n  }\n\n  return !auth.loaded ? (\n    <div>Loading...</div>\n  ) : (\n    <div>\n      {auth.loggedIn ? (\n        <div>\n          <p>\n            <span>Logged in</span>\n            {auth.userId && <span> as {auth.userId}</span>}\n          </p>\n          {status !== \"\" && <p>API call: {status}</p>}\n          <button onClick={callApi}>Call API</button>\n          <button onClick={auth.logout}>Logout</button>\n        </div>\n      ) : (\n        <button onClick={auth.login}>Login with OAuth</button>\n      )}\n    </div>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "examples/client/react/src/AuthContext.tsx",
    "content": "import {\n  useRef,\n  useState,\n  ReactNode,\n  useEffect,\n  useContext,\n  createContext,\n} from \"react\"\nimport { createClient } from \"@openauthjs/openauth/client\"\n\nconst client = createClient({\n  clientID: \"react\",\n  issuer: \"http://localhost:3000\",\n})\n\ninterface AuthContextType {\n  userId?: string\n  loaded: boolean\n  loggedIn: boolean\n  logout: () => void\n  login: () => Promise<void>\n  getToken: () => Promise<string | undefined>\n}\n\nconst AuthContext = createContext({} as AuthContextType)\n\nexport function AuthProvider({ children }: { children: ReactNode }) {\n  const initializing = useRef(true)\n  const [loaded, setLoaded] = useState(false)\n  const [loggedIn, setLoggedIn] = useState(false)\n  const token = useRef<string | undefined>(undefined)\n  const [userId, setUserId] = useState<string | undefined>()\n\n  useEffect(() => {\n    const hash = new URLSearchParams(location.search.slice(1))\n    const code = hash.get(\"code\")\n    const state = hash.get(\"state\")\n\n    if (!initializing.current) {\n      return\n    }\n\n    initializing.current = false\n\n    if (code && state) {\n      callback(code, state)\n      return\n    }\n\n    auth()\n  }, [])\n\n  async function auth() {\n    const token = await refreshTokens()\n\n    if (token) {\n      await user()\n    }\n\n    setLoaded(true)\n  }\n\n  async function refreshTokens() {\n    const refresh = localStorage.getItem(\"refresh\")\n    if (!refresh) return\n    const next = await client.refresh(refresh, {\n      access: token.current,\n    })\n    if (next.err) return\n    if (!next.tokens) return token.current\n\n    localStorage.setItem(\"refresh\", next.tokens.refresh)\n    token.current = next.tokens.access\n\n    return next.tokens.access\n  }\n\n  async function getToken() {\n    const token = await refreshTokens()\n\n    if (!token) {\n      await login()\n      return\n    }\n\n    return token\n  }\n\n  async function login() {\n    const { challenge, url } = await client.authorize(location.origin, \"code\", {\n      pkce: true,\n    })\n    sessionStorage.setItem(\"challenge\", JSON.stringify(challenge))\n    location.href = url\n  }\n\n  async function callback(code: string, state: string) {\n    const challenge = JSON.parse(sessionStorage.getItem(\"challenge\")!)\n    if (code) {\n      if (state === challenge.state && challenge.verifier) {\n        const exchanged = await client.exchange(\n          code!,\n          location.origin,\n          challenge.verifier,\n        )\n        if (!exchanged.err) {\n          token.current = exchanged.tokens?.access\n          localStorage.setItem(\"refresh\", exchanged.tokens.refresh)\n        }\n      }\n      window.location.replace(\"/\")\n    }\n  }\n\n  async function user() {\n    const res = await fetch(\"http://localhost:3001/\", {\n      headers: {\n        Authorization: `Bearer ${token.current}`,\n      },\n    })\n\n    if (res.ok) {\n      setUserId(await res.text())\n      setLoggedIn(true)\n    }\n  }\n\n  function logout() {\n    localStorage.removeItem(\"refresh\")\n    token.current = undefined\n\n    window.location.replace(\"/\")\n  }\n\n  return (\n    <AuthContext.Provider\n      value={{\n        login,\n        logout,\n        userId,\n        loaded,\n        loggedIn,\n        getToken,\n      }}\n    >\n      {children}\n    </AuthContext.Provider>\n  )\n}\n\nexport function useAuth() {\n  return useContext(AuthContext)\n}\n"
  },
  {
    "path": "examples/client/react/src/main.tsx",
    "content": "import { StrictMode } from \"react\"\nimport { createRoot } from \"react-dom/client\"\nimport { AuthProvider } from \"./AuthContext\"\nimport App from \"./App\"\n\ncreateRoot(document.getElementById(\"root\")!).render(\n  <StrictMode>\n    <AuthProvider>\n      <App />\n    </AuthProvider>\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/client/react/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/client/react/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/client/react/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.app.json\"\n    },\n    {\n      \"path\": \"./tsconfig.node.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "examples/client/react/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/client/react/vite.config.ts",
    "content": "import { defineConfig } from \"vite\"\nimport react from \"@vitejs/plugin-react\"\n\n// https://vite.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/client/sveltekit/.npmrc",
    "content": "engine-strict=true\n"
  },
  {
    "path": "examples/client/sveltekit/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-client-sveltekit\",\n  \"type\": \"module\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"dev\": \"vite dev\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"prepare\": \"svelte-kit sync || echo ''\",\n    \"check\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json\",\n    \"check:watch\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch\"\n  },\n  \"devDependencies\": {\n    \"@openauthjs/openauth\": \"^0.4.3\",\n    \"@sveltejs/adapter-auto\": \"^4.0.0\",\n    \"@sveltejs/kit\": \"^2.16.0\",\n    \"@sveltejs/vite-plugin-svelte\": \"^5.0.0\",\n    \"svelte\": \"^5.0.0\",\n    \"svelte-check\": \"^4.0.0\",\n    \"typescript\": \"5.6.3\",\n    \"valibot\": \"1.0.0-beta.15\",\n    \"vite\": \"^6.0.1\"\n  }\n}\n"
  },
  {
    "path": "examples/client/sveltekit/src/app.d.ts",
    "content": "// See https://svelte.dev/docs/kit/types#app.d.ts\n// for information about these interfaces\ndeclare global {\n  namespace App {\n    // interface Error {}\n    interface Locals {\n      session: { id: string }\n    }\n    // interface PageData {}\n    // interface PageState {}\n    // interface Platform {}\n  }\n}\n\nexport {}\n"
  },
  {
    "path": "examples/client/sveltekit/src/app.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"%sveltekit.assets%/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t%sveltekit.head%\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">%sveltekit.body%</div>\n\t</body>\n</html>\n"
  },
  {
    "path": "examples/client/sveltekit/src/hooks.server.ts",
    "content": "import { redirect, type Handle } from \"@sveltejs/kit\"\nimport { createAuthClient, setTokens } from \"$lib/auth.server\"\nimport { subjects } from \"../../../subjects\"\n\nexport const handle: Handle = async ({ event, resolve }) => {\n  if (event.url.pathname === \"/callback\") {\n    return resolve(event)\n  }\n\n  const client = createAuthClient(event)\n  try {\n    const accessToken = event.cookies.get(\"access_token\")\n    if (accessToken) {\n      const refreshToken = event.cookies.get(\"refresh_token\")\n      const verified = await client.verify(subjects, accessToken, {\n        refresh: refreshToken,\n      })\n      if (!verified.err) {\n        if (verified.tokens)\n          setTokens(event, verified.tokens.access, verified.tokens.refresh)\n        event.locals.session = verified.subject.properties\n        return resolve(event)\n      }\n    }\n  } catch (e) {}\n\n  const { url } = await client.authorize(event.url.origin + \"/callback\", \"code\")\n  return redirect(302, url)\n}\n"
  },
  {
    "path": "examples/client/sveltekit/src/lib/auth.server.ts",
    "content": "import { createClient } from \"@openauthjs/openauth/client\"\nimport type { RequestEvent } from \"@sveltejs/kit\"\n\nexport function createAuthClient(event: RequestEvent) {\n  return createClient({\n    clientID: \"openauth-sveltekit-example\",\n    issuer: \"http://localhost:3000\",\n    fetch: event.fetch,\n  })\n}\n\nexport function setTokens(\n  event: RequestEvent,\n  access: string,\n  refresh: string,\n) {\n  event.cookies.set(\"refresh_token\", refresh, {\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  event.cookies.set(\"access_token\", access, {\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n"
  },
  {
    "path": "examples/client/sveltekit/src/routes/+page.server.ts",
    "content": "export async function load(event) {\n  return {\n    subject: event.locals.session,\n  }\n}\n"
  },
  {
    "path": "examples/client/sveltekit/src/routes/+page.svelte",
    "content": "<script lang=\"ts\">\n    const { data } = $props();\n</script>\n\n<h1>Hello {data.subject.id}</h1>\n"
  },
  {
    "path": "examples/client/sveltekit/src/routes/callback/+server.ts",
    "content": "import { redirect } from \"@sveltejs/kit\"\nimport { createAuthClient, setTokens } from \"$lib/auth.server.js\"\n\nexport async function GET(event) {\n  const code = event.url.searchParams.get(\"code\")\n  const authClient = createAuthClient(event)\n  const tokens = await authClient.exchange(\n    code!,\n    event.url.origin + \"/callback\",\n  )\n  if (!tokens.err) {\n    setTokens(event, tokens.tokens.access, tokens.tokens.refresh)\n  } else {\n    throw tokens.err\n  }\n  return redirect(302, `/`)\n}\n"
  },
  {
    "path": "examples/client/sveltekit/svelte.config.js",
    "content": "import adapter from \"@sveltejs/adapter-auto\"\nimport { vitePreprocess } from \"@sveltejs/vite-plugin-svelte\"\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  // Consult https://svelte.dev/docs/kit/integrations\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n\n  kit: {\n    // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.\n    // If your environment is not supported, or you settled on a specific environment, switch out the adapter.\n    // See https://svelte.dev/docs/kit/adapters for more information about adapters.\n    adapter: adapter(),\n  },\n}\n\nexport default config\n"
  },
  {
    "path": "examples/client/sveltekit/tsconfig.json",
    "content": "{\n  \"extends\": \"./.svelte-kit/tsconfig.json\",\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"moduleResolution\": \"bundler\"\n  }\n  // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias\n  // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files\n  //\n  // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes\n  // from the referenced tsconfig.json - TypeScript does not merge them in\n}\n"
  },
  {
    "path": "examples/client/sveltekit/vite.config.ts",
    "content": "import { sveltekit } from \"@sveltejs/kit/vite\"\nimport { defineConfig } from \"vite\"\n\nexport default defineConfig({\n  plugins: [sveltekit()],\n})\n"
  },
  {
    "path": "examples/issuer/bun/.gitignore",
    "content": "persist.json\n"
  },
  {
    "path": "examples/issuer/bun/issuer.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\nimport { subjects } from \"../../subjects.js\"\n\nasync function getUser(email: string) {\n  // Get user from database\n  // Return user ID\n  return \"123\"\n}\n\nexport default issuer({\n  subjects,\n  storage: MemoryStorage({\n    persist: \"./persist.json\",\n  }),\n  providers: {\n    password: PasswordProvider(\n      PasswordUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n        validatePassword: (password) => {\n          if (password.length < 8) {\n            return \"Password must be at least 8 characters\"\n          }\n        },\n      }),\n    ),\n  },\n  async allow() {\n    return true\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"password\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n"
  },
  {
    "path": "examples/issuer/bun/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-issuer-bun\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "examples/issuer/cloudflare/issuer.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { CloudflareStorage } from \"@openauthjs/openauth/storage/cloudflare\"\nimport {\n  type ExecutionContext,\n  type KVNamespace,\n} from \"@cloudflare/workers-types\"\nimport { subjects } from \"../../subjects.js\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\n\ninterface Env {\n  CloudflareAuthKV: KVNamespace\n}\n\nasync function getUser(email: string) {\n  // Get user from database\n  // Return user ID\n  return \"123\"\n}\n\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    return issuer({\n      storage: CloudflareStorage({\n        namespace: env.CloudflareAuthKV,\n      }),\n      subjects,\n      providers: {\n        password: PasswordProvider(\n          PasswordUI({\n            sendCode: async (email, code) => {\n              console.log(email, code)\n            },\n          }),\n        ),\n      },\n      success: async (ctx, value) => {\n        if (value.provider === \"password\") {\n          return ctx.subject(\"user\", {\n            id: await getUser(value.email),\n          })\n        }\n        throw new Error(\"Invalid provider\")\n      },\n    }).fetch(request, env, ctx)\n  },\n}\n"
  },
  {
    "path": "examples/issuer/cloudflare/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-issuer-cloudflare\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\",\n    \"sst\": \"3.5.1\"\n  }\n}\n"
  },
  {
    "path": "examples/issuer/cloudflare/sst-env.d.ts",
    "content": "/* This file is auto-generated by SST. Do not edit. */\n/* tslint:disable */\n/* eslint-disable */\n/* deno-fmt-ignore-file */\nimport \"sst\"\nexport {}\ndeclare module \"sst\" {\n  export interface Resource {\n    CloudflareAuth: {\n      type: \"sst.cloudflare.Worker\"\n      url: string\n    }\n    CloudflareAuthKV: {\n      type: \"sst.cloudflare.Kv\"\n    }\n  }\n}\n"
  },
  {
    "path": "examples/issuer/cloudflare/sst.config.ts",
    "content": "/// <reference path=\"./.sst/platform/config.d.ts\" />\nexport default $config({\n  app(input) {\n    return {\n      name: \"openauth-example-cloudflare\",\n      removal: input?.stage === \"production\" ? \"retain\" : \"remove\",\n      home: \"cloudflare\",\n    }\n  },\n  async run() {\n    // cloudflare\n    const kv = new sst.cloudflare.Kv(\"CloudflareAuthKV\")\n    const auth = new sst.cloudflare.Worker(\"CloudflareAuth\", {\n      handler: \"./issuer.ts\",\n      link: [kv],\n      url: true,\n    })\n\n    return {\n      url: auth.url,\n    }\n  },\n})\n"
  },
  {
    "path": "examples/issuer/custom-frontend/auth/issuer.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\nimport { subjects } from \"../../../subjects.js\"\n\nasync function getUser(email: string) {\n  // Get user from database\n  // Return user ID\n  return \"123\"\n}\n\nexport default issuer({\n  subjects,\n  storage: MemoryStorage({\n    persist: \"./persist.json\",\n  }),\n  providers: {\n    code: CodeProvider({\n      sendCode: async (claims, code) => {\n        console.log(claims.email, code)\n      },\n      async request(req, state, _form, error) {\n        const url = new URL(`http://localhost:3001`)\n        url.pathname = `/auth/${state.type}`\n        if (error) url.searchParams.set(\"error\", error.type)\n        return new Response(null, {\n          status: 302,\n          headers: {\n            Location: url.toString(),\n          },\n        })\n      },\n    }),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"code\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.claims.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n"
  },
  {
    "path": "examples/issuer/custom-frontend/auth/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-custom-frontend-issuer\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "examples/issuer/custom-frontend/frontend/frontend.tsx",
    "content": "/** @jsx jsx */\n/** @jsxImportSource hono/jsx */\n\nimport { Hono } from \"hono\"\nimport { PropsWithChildren } from \"hono/jsx\"\n\nfunction Layout(props: PropsWithChildren) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charSet=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>Issuer</title>\n      </head>\n      <body>{props.children}</body>\n    </html>\n  )\n}\n\nconst app = new Hono()\n  .get(\"/auth/start\", async (c) => {\n    return c.html(\n      <Layout>\n        <h1>Issuer</h1>\n        <form method=\"post\" action=\"http://localhost:3000/code/authorize\">\n          <input type=\"hidden\" name=\"action\" value=\"request\" />\n          <label for=\"email\">Email</label>\n          <input type=\"email\" name=\"email\" id=\"email\" />\n          <button type=\"submit\">Request</button>\n        </form>\n      </Layout>,\n    )\n  })\n  .get(\"/auth/code\", async (c) => {\n    return c.html(\n      <Layout>\n        <h1>Issuer</h1>\n        <form method=\"post\" action=\"http://localhost:3000/code/authorize\">\n          <input type=\"hidden\" name=\"action\" value=\"verify\" />\n          <label for=\"code\">Code</label>\n          <input\n            type=\"text\"\n            name=\"code\"\n            id=\"code\"\n            minLength={6}\n            maxLength={6}\n          />\n          <button type=\"submit\">Verify</button>\n        </form>\n      </Layout>,\n    )\n  })\n\nexport default {\n  port: 3001,\n  fetch: app.fetch,\n}\n"
  },
  {
    "path": "examples/issuer/custom-frontend/frontend/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-custom-frontend\",\n  \"scripts\": {\n    \"dev\": \"bun run --hot frontend.tsx\"\n  }\n}\n"
  },
  {
    "path": "examples/issuer/custom-frontend/package.json",
    "content": "{\n  \"name\": \"custom-frontend\",\n  \"version\": \"0.0.0\",\n  \"private\": true\n}\n"
  },
  {
    "path": "examples/issuer/lambda/issuer.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { handle } from \"hono/aws-lambda\"\nimport { subjects } from \"../../subjects.js\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nasync function getUser(email: string) {\n  // Get user from database\n  // Return user ID\n  return \"123\"\n}\n\nconst app = issuer({\n  subjects,\n  providers: {\n    password: PasswordProvider(\n      PasswordUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"password\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n\n// @ts-ignore\nexport const handler = handle(app)\n"
  },
  {
    "path": "examples/issuer/lambda/package.json",
    "content": "{\n  \"name\": \"@openauthjs/example-issuer-aws\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"workspace:*\",\n    \"sst\": \"3.5.1\"\n  }\n}\n"
  },
  {
    "path": "examples/issuer/lambda/sst-env.d.ts",
    "content": "/* This file is auto-generated by SST. Do not edit. */\n/* tslint:disable */\n/* eslint-disable */\n/* deno-fmt-ignore-file */\nimport \"sst\"\nexport {}\ndeclare module \"sst\" {\n  export interface Resource {}\n}\n"
  },
  {
    "path": "examples/issuer/lambda/sst.config.ts",
    "content": "/// <reference path=\"./.sst/platform/config.d.ts\" />\nexport default $config({\n  app(input) {\n    return {\n      name: \"openauth-example-lambda\",\n      removal: input?.stage === \"production\" ? \"retain\" : \"remove\",\n      home: \"aws\",\n    }\n  },\n  async run() {\n    const auth = new sst.aws.Auth(\"Auth\", {\n      issuer: \"./issuer.handler\",\n    })\n  },\n})\n"
  },
  {
    "path": "examples/issuer/node/.gitignore",
    "content": "persist.json\n"
  },
  {
    "path": "examples/issuer/node/authorizer.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\nimport { serve } from \"@hono/node-server\"\nimport { subjects } from \"../../subjects\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nasync function getUser(email: string) {\n  // Get user from database\n  // Return user ID\n  return \"123\"\n}\n\nconst app = issuer({\n  subjects,\n  storage: MemoryStorage({\n    persist: \"./persist.json\",\n  }),\n  providers: {\n    password: PasswordProvider(\n      PasswordUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"password\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n\nserve(app)\n"
  },
  {
    "path": "examples/issuer/node/package.json",
    "content": "{\n  \"name\": \"node\",\n  \"version\": \"0.0.0\",\n  \"private\": true\n}\n"
  },
  {
    "path": "examples/quickstart/sst/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n\n# sst\n.sst\n\n# open-next\n.open-next\n"
  },
  {
    "path": "examples/quickstart/sst/README.md",
    "content": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.\n"
  },
  {
    "path": "examples/quickstart/sst/app/actions.ts",
    "content": "\"use server\"\n\nimport { redirect } from \"next/navigation\"\nimport { headers as getHeaders, cookies as getCookies } from \"next/headers\"\nimport { subjects } from \"../auth/subjects\"\nimport { client, setTokens } from \"./auth\"\n\nexport async function auth() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (!accessToken) {\n    return false\n  }\n\n  const verified = await client.verify(subjects, accessToken.value, {\n    refresh: refreshToken?.value,\n  })\n\n  if (verified.err) {\n    return false\n  }\n  if (verified.tokens) {\n    await setTokens(verified.tokens.access, verified.tokens.refresh)\n  }\n\n  return verified.subject\n}\n\nexport async function login() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (accessToken) {\n    const verified = await client.verify(subjects, accessToken.value, {\n      refresh: refreshToken?.value,\n    })\n    if (!verified.err && verified.tokens) {\n      await setTokens(verified.tokens.access, verified.tokens.refresh)\n      redirect(\"/\")\n    }\n  }\n\n  const headers = await getHeaders()\n  const host = headers.get(\"host\")\n  const protocol = host?.includes(\"localhost\") ? \"http\" : \"https\"\n  const { url } = await client.authorize(\n    `${protocol}://${host}/api/callback`,\n    \"code\",\n  )\n  redirect(url)\n}\n\nexport async function logout() {\n  const cookies = await getCookies()\n  cookies.delete(\"access_token\")\n  cookies.delete(\"refresh_token\")\n\n  redirect(\"/\")\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/api/callback/route.ts",
    "content": "import { client, setTokens } from \"../../auth\"\nimport { type NextRequest, NextResponse } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  const url = new URL(req.url)\n  const code = url.searchParams.get(\"code\")\n\n  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)\n\n  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })\n\n  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)\n\n  return NextResponse.redirect(`${url.origin}/`)\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/auth.ts",
    "content": "import { Resource } from \"sst\"\nimport { createClient } from \"@openauthjs/openauth/client\"\nimport { cookies as getCookies } from \"next/headers\"\n\nexport const client = createClient({\n  clientID: \"nextjs\",\n  issuer: Resource.MyAuth.url,\n})\n\nexport async function setTokens(access: string, refresh: string) {\n  const cookies = await getCookies()\n\n  cookies.set({\n    name: \"access_token\",\n    value: access,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  cookies.set({\n    name: \"refresh_token\",\n    value: refresh,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/globals.css",
    "content": ":root {\n  --background: #ffffff;\n  --foreground: #171717;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --background: #0a0a0a;\n    --foreground: #ededed;\n  }\n}\n\nhtml,\nbody {\n  max-width: 100vw;\n  overflow-x: hidden;\n}\n\nbody {\n  color: var(--foreground);\n  background: var(--background);\n  font-family: Arial, Helvetica, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n  padding: 0;\n  margin: 0;\n}\n\na {\n  color: inherit;\n  text-decoration: none;\n}\n\n@media (prefers-color-scheme: dark) {\n  html {\n    color-scheme: dark;\n  }\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/layout.tsx",
    "content": "import type { Metadata } from \"next\"\nimport { Geist, Geist_Mono } from \"next/font/google\"\nimport \"./globals.css\"\n\nconst geistSans = Geist({\n  variable: \"--font-geist-sans\",\n  subsets: [\"latin\"],\n})\n\nconst geistMono = Geist_Mono({\n  variable: \"--font-geist-mono\",\n  subsets: [\"latin\"],\n})\n\nexport const metadata: Metadata = {\n  title: \"Create Next App\",\n  description: \"Generated by create next app\",\n}\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode\n}>) {\n  return (\n    <html lang=\"en\">\n      <body className={`${geistSans.variable} ${geistMono.variable}`}>\n        {children}\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/page.module.css",
    "content": ".page {\n  --gray-rgb: 0, 0, 0;\n  --gray-alpha-200: rgba(var(--gray-rgb), 0.08);\n  --gray-alpha-100: rgba(var(--gray-rgb), 0.05);\n\n  --button-primary-hover: #383838;\n  --button-secondary-hover: #f2f2f2;\n\n  display: grid;\n  grid-template-rows: 20px 1fr 20px;\n  align-items: center;\n  justify-items: center;\n  min-height: 100svh;\n  padding: 80px;\n  gap: 64px;\n  font-family: var(--font-geist-sans);\n}\n\n@media (prefers-color-scheme: dark) {\n  .page {\n    --gray-rgb: 255, 255, 255;\n    --gray-alpha-200: rgba(var(--gray-rgb), 0.145);\n    --gray-alpha-100: rgba(var(--gray-rgb), 0.06);\n\n    --button-primary-hover: #ccc;\n    --button-secondary-hover: #1a1a1a;\n  }\n}\n\n.main {\n  display: flex;\n  flex-direction: column;\n  gap: 32px;\n  grid-row-start: 2;\n}\n\n.main ol {\n  font-family: var(--font-geist-mono);\n  padding-left: 0;\n  margin: 0;\n  font-size: 14px;\n  line-height: 24px;\n  letter-spacing: -0.01em;\n  list-style-position: inside;\n}\n\n.main li:not(:last-of-type) {\n  margin-bottom: 8px;\n}\n\n.main code {\n  font-family: inherit;\n  background: var(--gray-alpha-100);\n  padding: 2px 4px;\n  border-radius: 4px;\n  font-weight: 600;\n}\n\n.ctas {\n  display: flex;\n  gap: 16px;\n}\n\n.ctas a {\n  appearance: none;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\na.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\na.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n\n.footer {\n  grid-row-start: 3;\n  display: flex;\n  gap: 24px;\n}\n\n.footer a {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.footer img {\n  flex-shrink: 0;\n}\n\n/* Enable hover only on non-touch devices */\n@media (hover: hover) and (pointer: fine) {\n  a.primary:hover {\n    background: var(--button-primary-hover);\n    border-color: transparent;\n  }\n\n  a.secondary:hover {\n    background: var(--button-secondary-hover);\n    border-color: transparent;\n  }\n\n  .footer a:hover {\n    text-decoration: underline;\n    text-underline-offset: 4px;\n  }\n}\n\n@media (max-width: 600px) {\n  .page {\n    padding: 32px;\n    padding-bottom: 80px;\n  }\n\n  .main {\n    align-items: center;\n  }\n\n  .main ol {\n    text-align: center;\n  }\n\n  .ctas {\n    flex-direction: column;\n  }\n\n  .ctas a {\n    font-size: 14px;\n    height: 40px;\n    padding: 0 16px;\n  }\n\n  a.secondary {\n    min-width: auto;\n  }\n\n  .footer {\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  .logo {\n    filter: invert();\n  }\n}\n\n.ctas button {\n  appearance: none;\n  background: transparent;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\nbutton.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\nbutton.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n"
  },
  {
    "path": "examples/quickstart/sst/app/page.tsx",
    "content": "import Image from \"next/image\"\nimport styles from \"./page.module.css\"\nimport { auth, login, logout } from \"./actions\"\n\nexport default async function Home() {\n  const subject = await auth()\n\n  return (\n    <div className={styles.page}>\n      <main className={styles.main}>\n        <Image\n          className={styles.logo}\n          src=\"/next.svg\"\n          alt=\"Next.js logo\"\n          width={180}\n          height={38}\n          priority\n        />\n        <ol>\n          {subject ? (\n            <>\n              <li>\n                Logged in as <code>{subject.properties.id}</code>.\n              </li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          ) : (\n            <>\n              <li>Login with your email and password.</li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          )}\n        </ol>\n\n        <div className={styles.ctas}>\n          {subject ? (\n            <form action={logout}>\n              <button className={styles.secondary}>Logout</button>\n            </form>\n          ) : (\n            <form action={login}>\n              <button className={styles.primary}>Login with OpenAuth</button>\n            </form>\n          )}\n        </div>\n      </main>\n    </div>\n  )\n}\n"
  },
  {
    "path": "examples/quickstart/sst/auth/index.ts",
    "content": "import { handle } from \"hono/aws-lambda\"\nimport { issuer } from \"@openauthjs/openauth\"\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { subjects } from \"./subjects\"\n\nasync function getUser(email: string) {\n  // Get user from database and return user ID\n  return \"123\"\n}\n\nconst app = issuer({\n  subjects,\n  storage: MemoryStorage(),\n  // Remove after setting custom domain\n  allow: async () => true,\n  providers: {\n    code: CodeProvider(\n      CodeUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"code\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.claims.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n\nexport const handler = handle(app)\n"
  },
  {
    "path": "examples/quickstart/sst/auth/subjects.ts",
    "content": "import { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nexport const subjects = createSubjects({\n  user: object({\n    id: string(),\n  }),\n})\n"
  },
  {
    "path": "examples/quickstart/sst/next.config.ts",
    "content": "import type { NextConfig } from \"next\"\n\nconst nextConfig: NextConfig = {\n  /* config options here */\n}\n\nexport default nextConfig\n"
  },
  {
    "path": "examples/quickstart/sst/package.json",
    "content": "{\n  \"name\": \"oa-nextjs\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"next build\",\n    \"dev\": \"next dev\",\n    \"lint\": \"next lint\",\n    \"start\": \"next start\"\n  },\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"^0.3.2\",\n    \"hono\": \"^4.6.16\",\n    \"next\": \"15.1.4\",\n    \"react\": \"^19.0.0\",\n    \"react-dom\": \"^19.0.0\",\n    \"sst\": \"latest\",\n    \"valibot\": \"^1.0.0-beta.11\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"^19\",\n    \"@types/react-dom\": \"^19\",\n    \"typescript\": \"^5\"\n  }\n}\n"
  },
  {
    "path": "examples/quickstart/sst/sst-env.d.ts",
    "content": "/* This file is auto-generated by SST. Do not edit. */\n/* tslint:disable */\n/* eslint-disable */\n/* deno-fmt-ignore-file */\nimport \"sst\"\nexport {}\ndeclare module \"sst\" {\n  export interface Resource {\n    MyAuth: {\n      type: \"sst.aws.Auth\"\n      url: string\n    }\n    MyWeb: {\n      type: \"sst.aws.Nextjs\"\n      url: string\n    }\n  }\n}\n"
  },
  {
    "path": "examples/quickstart/sst/sst.config.ts",
    "content": "/// <reference path=\"./.sst/platform/config.d.ts\" />\n\nexport default $config({\n  app(input) {\n    return {\n      name: \"oa-nextjs\",\n      removal: input?.stage === \"production\" ? \"retain\" : \"remove\",\n      protect: [\"production\"].includes(input?.stage),\n      home: \"aws\",\n    }\n  },\n  async run() {\n    const auth = new sst.aws.Auth(\"MyAuth\", {\n      issuer: \"auth/index.handler\",\n    })\n\n    new sst.aws.Nextjs(\"MyWeb\", {\n      link: [auth],\n    })\n  },\n})\n"
  },
  {
    "path": "examples/quickstart/sst/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\", \"sst.config.ts\"]\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "examples/quickstart/standalone/README.md",
    "content": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.\n"
  },
  {
    "path": "examples/quickstart/standalone/app/actions.ts",
    "content": "\"use server\"\n\nimport { redirect } from \"next/navigation\"\nimport { headers as getHeaders, cookies as getCookies } from \"next/headers\"\nimport { subjects } from \"../auth/subjects\"\nimport { client, setTokens } from \"./auth\"\n\nexport async function auth() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (!accessToken) {\n    return false\n  }\n\n  const verified = await client.verify(subjects, accessToken.value, {\n    refresh: refreshToken?.value,\n  })\n\n  if (verified.err) {\n    return false\n  }\n  if (verified.tokens) {\n    await setTokens(verified.tokens.access, verified.tokens.refresh)\n  }\n\n  return verified.subject\n}\n\nexport async function login() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (accessToken) {\n    const verified = await client.verify(subjects, accessToken.value, {\n      refresh: refreshToken?.value,\n    })\n    if (!verified.err && verified.tokens) {\n      await setTokens(verified.tokens.access, verified.tokens.refresh)\n      redirect(\"/\")\n    }\n  }\n\n  const headers = await getHeaders()\n  const host = headers.get(\"host\")\n  const protocol = host?.includes(\"localhost\") ? \"http\" : \"https\"\n  const { url } = await client.authorize(\n    `${protocol}://${host}/api/callback`,\n    \"code\",\n  )\n  redirect(url)\n}\n\nexport async function logout() {\n  const cookies = await getCookies()\n  cookies.delete(\"access_token\")\n  cookies.delete(\"refresh_token\")\n\n  redirect(\"/\")\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/api/callback/route.ts",
    "content": "import { client, setTokens } from \"../../auth\"\nimport { type NextRequest, NextResponse } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  const url = new URL(req.url)\n  const code = url.searchParams.get(\"code\")\n\n  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)\n\n  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })\n\n  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)\n\n  return NextResponse.redirect(`${url.origin}/`)\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/auth.ts",
    "content": "import { createClient } from \"@openauthjs/openauth/client\"\nimport { cookies as getCookies } from \"next/headers\"\n\nexport const client = createClient({\n  clientID: \"nextjs\",\n  issuer: \"http://localhost:3001\",\n})\n\nexport async function setTokens(access: string, refresh: string) {\n  const cookies = await getCookies()\n\n  cookies.set({\n    name: \"access_token\",\n    value: access,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  cookies.set({\n    name: \"refresh_token\",\n    value: refresh,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/globals.css",
    "content": ":root {\n  --background: #ffffff;\n  --foreground: #171717;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --background: #0a0a0a;\n    --foreground: #ededed;\n  }\n}\n\nhtml,\nbody {\n  max-width: 100vw;\n  overflow-x: hidden;\n}\n\nbody {\n  color: var(--foreground);\n  background: var(--background);\n  font-family: Arial, Helvetica, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n  padding: 0;\n  margin: 0;\n}\n\na {\n  color: inherit;\n  text-decoration: none;\n}\n\n@media (prefers-color-scheme: dark) {\n  html {\n    color-scheme: dark;\n  }\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/layout.tsx",
    "content": "import type { Metadata } from \"next\"\nimport { Geist, Geist_Mono } from \"next/font/google\"\nimport \"./globals.css\"\n\nconst geistSans = Geist({\n  variable: \"--font-geist-sans\",\n  subsets: [\"latin\"],\n})\n\nconst geistMono = Geist_Mono({\n  variable: \"--font-geist-mono\",\n  subsets: [\"latin\"],\n})\n\nexport const metadata: Metadata = {\n  title: \"Create Next App\",\n  description: \"Generated by create next app\",\n}\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode\n}>) {\n  return (\n    <html lang=\"en\">\n      <body className={`${geistSans.variable} ${geistMono.variable}`}>\n        {children}\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/page.module.css",
    "content": ".page {\n  --gray-rgb: 0, 0, 0;\n  --gray-alpha-200: rgba(var(--gray-rgb), 0.08);\n  --gray-alpha-100: rgba(var(--gray-rgb), 0.05);\n\n  --button-primary-hover: #383838;\n  --button-secondary-hover: #f2f2f2;\n\n  display: grid;\n  grid-template-rows: 20px 1fr 20px;\n  align-items: center;\n  justify-items: center;\n  min-height: 100svh;\n  padding: 80px;\n  gap: 64px;\n  font-family: var(--font-geist-sans);\n}\n\n@media (prefers-color-scheme: dark) {\n  .page {\n    --gray-rgb: 255, 255, 255;\n    --gray-alpha-200: rgba(var(--gray-rgb), 0.145);\n    --gray-alpha-100: rgba(var(--gray-rgb), 0.06);\n\n    --button-primary-hover: #ccc;\n    --button-secondary-hover: #1a1a1a;\n  }\n}\n\n.main {\n  display: flex;\n  flex-direction: column;\n  gap: 32px;\n  grid-row-start: 2;\n}\n\n.main ol {\n  font-family: var(--font-geist-mono);\n  padding-left: 0;\n  margin: 0;\n  font-size: 14px;\n  line-height: 24px;\n  letter-spacing: -0.01em;\n  list-style-position: inside;\n}\n\n.main li:not(:last-of-type) {\n  margin-bottom: 8px;\n}\n\n.main code {\n  font-family: inherit;\n  background: var(--gray-alpha-100);\n  padding: 2px 4px;\n  border-radius: 4px;\n  font-weight: 600;\n}\n\n.ctas {\n  display: flex;\n  gap: 16px;\n}\n\n.ctas a {\n  appearance: none;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\na.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\na.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n\n.footer {\n  grid-row-start: 3;\n  display: flex;\n  gap: 24px;\n}\n\n.footer a {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.footer img {\n  flex-shrink: 0;\n}\n\n/* Enable hover only on non-touch devices */\n@media (hover: hover) and (pointer: fine) {\n  a.primary:hover {\n    background: var(--button-primary-hover);\n    border-color: transparent;\n  }\n\n  a.secondary:hover {\n    background: var(--button-secondary-hover);\n    border-color: transparent;\n  }\n\n  .footer a:hover {\n    text-decoration: underline;\n    text-underline-offset: 4px;\n  }\n}\n\n@media (max-width: 600px) {\n  .page {\n    padding: 32px;\n    padding-bottom: 80px;\n  }\n\n  .main {\n    align-items: center;\n  }\n\n  .main ol {\n    text-align: center;\n  }\n\n  .ctas {\n    flex-direction: column;\n  }\n\n  .ctas a {\n    font-size: 14px;\n    height: 40px;\n    padding: 0 16px;\n  }\n\n  a.secondary {\n    min-width: auto;\n  }\n\n  .footer {\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  .logo {\n    filter: invert();\n  }\n}\n\n.ctas button {\n  appearance: none;\n  background: transparent;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\nbutton.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\nbutton.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/app/page.tsx",
    "content": "import Image from \"next/image\"\nimport styles from \"./page.module.css\"\nimport { auth, login, logout } from \"./actions\"\n\nexport default async function Home() {\n  const subject = await auth()\n\n  return (\n    <div className={styles.page}>\n      <main className={styles.main}>\n        <Image\n          className={styles.logo}\n          src=\"/next.svg\"\n          alt=\"Next.js logo\"\n          width={180}\n          height={38}\n          priority\n        />\n        <ol>\n          {subject ? (\n            <>\n              <li>\n                Logged in as <code>{subject.properties.id}</code>.\n              </li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          ) : (\n            <>\n              <li>Login with your email and password.</li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          )}\n        </ol>\n\n        <div className={styles.ctas}>\n          {subject ? (\n            <form action={logout}>\n              <button className={styles.secondary}>Logout</button>\n            </form>\n          ) : (\n            <form action={login}>\n              <button className={styles.primary}>Login with OpenAuth</button>\n            </form>\n          )}\n        </div>\n      </main>\n    </div>\n  )\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/auth/index.ts",
    "content": "import { issuer } from \"@openauthjs/openauth\"\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { subjects } from \"./subjects\"\n\nasync function getUser(email: string) {\n  // Get user from database and return user ID\n  return \"123\"\n}\n\nexport default issuer({\n  subjects,\n  storage: MemoryStorage(),\n  providers: {\n    code: CodeProvider(\n      CodeUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"code\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.claims.email),\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n"
  },
  {
    "path": "examples/quickstart/standalone/auth/subjects.ts",
    "content": "import { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nexport const subjects = createSubjects({\n  user: object({\n    id: string(),\n  }),\n})\n"
  },
  {
    "path": "examples/quickstart/standalone/next.config.ts",
    "content": "import type { NextConfig } from \"next\"\n\nconst nextConfig: NextConfig = {\n  /* config options here */\n}\n\nexport default nextConfig\n"
  },
  {
    "path": "examples/quickstart/standalone/package.json",
    "content": "{\n  \"name\": \"oa-nextjs\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"dev:auth\": \"PORT=3001 bun run --hot auth/index.ts\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@openauthjs/openauth\": \"^0.3.2\",\n    \"next\": \"15.1.4\",\n    \"react\": \"^19.0.0\",\n    \"react-dom\": \"^19.0.0\",\n    \"valibot\": \"^1.0.0-beta.11\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5\",\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"^19\",\n    \"@types/react-dom\": \"^19\"\n  }\n}\n"
  },
  {
    "path": "examples/quickstart/standalone/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/subjects.ts",
    "content": "import { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nexport const subjects = createSubjects({\n  user: object({\n    id: string(),\n  }),\n})\n"
  },
  {
    "path": "examples/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/node22/tsconfig.json\",\n  \"compilerOptions\": {\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"Bundler\",\n    \"strict\": true,\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"hono/jsx\"\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"openauthjs\",\n  \"module\": \"index.ts\",\n  \"type\": \"module\",\n  \"workspaces\": [\n    \"packages/openauth\",\n    \"examples/issuer/*\",\n    \"examples/client/*\"\n  ],\n  \"scripts\": {\n    \"release\": \"bun run --filter=\\\"@openauthjs/openauth\\\" build && changeset publish\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node22\": \"22.0.0\",\n    \"@types/bun\": \"latest\"\n  },\n  \"dependencies\": {\n    \"@changesets/cli\": \"2.27.10\",\n    \"prettier\": \"3.4.2\",\n    \"typescript\": \"5.6.3\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "packages/openauth/CHANGELOG.md",
    "content": "# @openauthjs/openauth\n\n## 0.4.3\n\n### Patch Changes\n\n- ec8ca65: include expires_in for refresh response\n\n## 0.4.2\n\n### Patch Changes\n\n- a03e510: fix for fetch timeout, wrap everything in lazy\n\n## 0.4.1\n\n### Patch Changes\n\n- 33959c3: better logging on oidc wellknown errors\n\n## 0.4.0\n\n### Minor Changes\n\n- 4e38fa6: feat: Return expires_in from /token endpoint\n- fcaafcf: Return signing alg from jwks.json endpoint\n\n### Patch Changes\n\n- 9e3c2ac: Call password validation callback on password reset\n- dc40b02: Fix providers client id case from `clientId` to `clientID`\n\n## 0.3.9\n\n### Patch Changes\n\n- 40f6033: enable logger by default\n- 3ce40fd: log dynamo error cause\n\n## 0.3.8\n\n### Patch Changes\n\n- c75005b: retry failed dynamo calls\n\n## 0.3.7\n\n### Patch Changes\n\n- 9036544: Add PKCE option to Oauth2Provider\n- 8f214e3: Import only hono type in util.ts\n- 4cd9e96: add provider logos for apple, x, facebook, microsoft and slack\n- 3e3c9e6: Add password validation callback\n- f46946c: Add use: sig to jwks.\n- 7d39e76: Add way to modify the dynamo ttl attribute name\n- 754d776: Supports forwarded protocol and forwarded port in the relative URL\n- 1b5525b: add ability to resend verification code during registration\n\n## 0.3.6\n\n### Patch Changes\n\n- f7bd440: Adding a new default openauth theme\n\n## 0.3.5\n\n### Patch Changes\n\n- b22fb30: fix: enable CORS on well-known routes\n\n## 0.3.4\n\n### Patch Changes\n\n- 34ca2b0: remove catch all route so hono instance can be extended\n\n## 0.3.3\n\n### Patch Changes\n\n- 9712422: fix: add charset meta tag to ui/base.tsx\n- 92e7170: Adds support for refresh token reuse interval and reuse detection\n\n  Also fixes an issue with token invalidation, where removing keys while scanning\n  may cause some refresh tokens to be skipped (depending on storage provider.)\n\n## 0.3.2\n\n### Patch Changes\n\n- 03da3e0: fix issue with oidc adapter\n\n## 0.3.1\n\n### Patch Changes\n\n- 8764ed4: support specify custom subject\n\n## 0.3.0\n\n### Minor Changes\n\n- b2af22a: renamed authorizer -> issuer and adapter -> provider\n\n  this should be a superficial change, but it's a breaking change\n\n  previously you imported adapters like this:\n\n  ```js\n  import { PasswordAdapter } from \"@openauth/openauth/adapter/password\"\n  ```\n\n  update it to this:\n\n  ```js\n  import { PasswordProvider } from \"@openauth/openauth/provider/password\"\n  ```\n\n  for the authorizer, you import it like this:\n\n  ```js\n  import { authorizer } from \"@openauth/openauth\"\n  ```\n\n  update it to this:\n\n  ```js\n  import { issuer } from \"@openauth/openauth\"\n  ```\n\n  also subjects should be imported deeply like this:\n\n  ```js\n  import { createSubjects } from \"@openauth/openauth\"\n  ```\n\n  update it to this:\n\n  ```js\n  import { createSubjects } from \"@openauth/openauth/subject\"\n  ```\n\n## 0.2.7\n\n### Patch Changes\n\n- 3004802: refactor: export `AuthorizationState` for better reusability\n- 2975608: switching signing key algorithm to es256. generate seperate keys for symmetrical encryption. old keys will automatically be marked expired and not used\n- c92604b: Adds support for a custom DynamoDB endpoint which enables use of a amazon/dynamodb-local container.\n\n  Usabe example:\n\n  ```ts\n    storage: DynamoStorage({\n      table: 'openauth-users',\n      endpoint: 'http://localhost:8000',\n    }),\n  ```\n\n## 0.2.6\n\n### Patch Changes\n\n- ca0df5d: ui: support phone mode for code ui\n- d8d1580: Add slack adapter to the list of available adapters.\n- ce44ed6: fix for password adapter not redirecting to the right place after change password flow\n- 4940bef: fix: add `node:` prefix for built-in modules\n\n## 0.2.5\n\n### Patch Changes\n\n- 8d6a243: fix: eliminate OTP bias and timing attack vulnerability\n- 873d1af: support specifying granular ttl for access/refresh token\n\n## 0.2.4\n\n### Patch Changes\n\n- 8b5f490: feat: Add copy customization to Code UI component\n\n## 0.2.3\n\n### Patch Changes\n\n- 80238de: return aud field when verifying token\n\n## 0.2.2\n\n### Patch Changes\n\n- 6da8647: fix copy for code resend\n\n## 0.2.1\n\n### Patch Changes\n\n- 83125f1: Remove predefined scopes from Spotify adapter to allow user-defined scopes\n\n## 0.2.0\n\n### Minor Changes\n\n- 8c3f050: BREAKING CHANGE: `client.exchange` and `client.authorize` signatures have changed.\n\n  `client.exchange` will now return an `{ err, tokens }` object. check `if (result.err)` for errors.\n  `client.authorize` now accepts `pkce: true` as an option. it is now async and returns a promise with `{ challenge, url}`. the `challenge` contains the `state` and `verifier` if using `pkce`\n\n  all exchanges have been updated to reflect this if you would like to reference\n\n### Patch Changes\n\n- 0f93def: refactor: update storage adapters to use Date for expiry\n\n## 0.1.2\n\n### Patch Changes\n\n- 584728f: Add common ColorScheme\n- 41acdc2: ui: missing copy in password.tsx\n- 2aa531b: Add GitHub Actions workflow for running tests\n\n## 0.1.1\n\n### Patch Changes\n\n- 04cd031: if only single provider is configured, skip provider selection\n\n## 0.1.0\n\n### Minor Changes\n\n- 3c8cdf8: BREAKING CHANGE:\n\n  The api for `client` has changed. It no longer throws errors and instead returns an `err` field that you must check or ignore.\n\n  All the examples have been updated to reflect this change.\n\n## 0.0.26\n\n### Patch Changes\n\n- 5dd6aa4: feature: add twitter adapter\n\n## 0.0.25\n\n### Patch Changes\n\n- 7e3fa38: feat(cognito): add CognitoAdapter\n- f496e3a: Set input autocomplete attribute in password UI\n\n## 0.0.24\n\n### Patch Changes\n\n- f695881: feature: added apple adapter\n\n## 0.0.23\n\n### Patch Changes\n\n- a585875: remove console.log\n- 079c514: feat: add JumpCloud\n\n## 0.0.22\n\n### Patch Changes\n\n- d3391f4: do not import createClient from root - it causes some bundlers to include too much code\n\n## 0.0.21\n\n### Patch Changes\n\n- acc2c5f: add tests for memory adapter and fixed issues with ttl\n- 7630c87: added facebook, discord, and keycloak adapter\n\n## 0.0.20\n\n### Patch Changes\n\n- 1a0ff69: fix for theme not being applied\n\n## 0.0.19\n\n### Patch Changes\n\n- 0864481: allow configuring storage through environment\n\n## 0.0.18\n\n### Patch Changes\n\n- bbf90c5: fix type issues when using ui components\n\n## 0.0.17\n\n### Patch Changes\n\n- f43e320: test\n- c10dfdd: test\n- c10dfdd: test\n- c10dfdd: test\n- 2d81677: test changeset\n\n## 0.0.16\n\n### Patch Changes\n\n- 515635f: rename package\n"
  },
  {
    "path": "packages/openauth/bunfig.toml",
    "content": "[test]\nroot = \"./test\"\n"
  },
  {
    "path": "packages/openauth/package.json",
    "content": "{\n  \"name\": \"@openauthjs/openauth\",\n  \"version\": \"0.4.3\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"bun run script/build.ts\",\n    \"test\": \"bun test\"\n  },\n  \"sideEffects\": false,\n  \"devDependencies\": {\n    \"@cloudflare/workers-types\": \"4.20241205.0\",\n    \"@tsconfig/node22\": \"22.0.0\",\n    \"@types/node\": \"22.10.1\",\n    \"arctic\": \"2.2.2\",\n    \"hono\": \"4.6.9\",\n    \"typescript\": \"5.6.3\",\n    \"valibot\": \"1.0.0-beta.15\"\n  },\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/esm/index.js\",\n      \"types\": \"./dist/types/index.d.ts\"\n    },\n    \"./*\": {\n      \"import\": \"./dist/esm/*.js\",\n      \"types\": \"./dist/types/*.d.ts\"\n    },\n    \"./ui\": {\n      \"import\": \"./dist/esm/ui/index.js\",\n      \"types\": \"./dist/types/ui/index.d.ts\"\n    }\n  },\n  \"peerDependencies\": {\n    \"arctic\": \"^2.2.2\",\n    \"hono\": \"^4.0.0\"\n  },\n  \"dependencies\": {\n    \"@standard-schema/spec\": \"1.0.0-beta.3\",\n    \"aws4fetch\": \"1.0.20\",\n    \"jose\": \"5.9.6\"\n  },\n  \"files\": [\n    \"src\",\n    \"dist\"\n  ]\n}\n"
  },
  {
    "path": "packages/openauth/script/build.ts",
    "content": "import { Glob, $ } from \"bun\"\nimport pkg from \"../package.json\"\n\nawait $`rm -rf dist`\nconst files = new Glob(\"./src/**/*.{ts,tsx}\").scan()\nfor await (const file of files) {\n  await Bun.build({\n    format: \"esm\",\n    outdir: \"dist/esm\",\n    external: [\"*\"],\n    root: \"src\",\n    entrypoints: [file],\n  })\n}\nawait Bun.build({\n  format: \"esm\",\n  outdir: \"dist/esm\",\n  external: [\n    ...Object.keys(pkg.dependencies),\n    ...Object.keys(pkg.peerDependencies),\n  ],\n  root: \"src\",\n  entrypoints: [\"./src/ui/base.tsx\"],\n})\nawait $`tsc --outDir dist/types --declaration --emitDeclarationOnly --declarationMap`\n"
  },
  {
    "path": "packages/openauth/src/client.ts",
    "content": "/**\n * Use the OpenAuth client kick off your OAuth flows, exchange tokens, refresh tokens,\n * and verify tokens.\n *\n * First, create a client.\n *\n * ```ts title=\"client.ts\"\n * import { createClient } from \"@openauthjs/openauth/client\"\n *\n * const client = createClient({\n *   clientID: \"my-client\",\n *   issuer: \"https://auth.myserver.com\"\n * })\n * ```\n *\n * Kick off the OAuth flow by calling `authorize`.\n *\n * ```ts\n * const redirect_uri = \"https://myserver.com/callback\"\n *\n * const { url } = await client.authorize(\n *   redirect_uri,\n *   \"code\"\n * )\n * ```\n *\n * When the user completes the flow, `exchange` the code for tokens.\n *\n * ```ts\n * const tokens = await client.exchange(query.get(\"code\"), redirect_uri)\n * ```\n *\n * And `verify` the tokens.\n *\n * ```ts\n * const verified = await client.verify(subjects, tokens.access)\n * ```\n *\n * @packageDocumentation\n */\nimport {\n  createLocalJWKSet,\n  errors,\n  JSONWebKeySet,\n  jwtVerify,\n  decodeJwt,\n} from \"jose\"\nimport { SubjectSchema } from \"./subject.js\"\nimport type { v1 } from \"@standard-schema/spec\"\nimport {\n  InvalidAccessTokenError,\n  InvalidAuthorizationCodeError,\n  InvalidRefreshTokenError,\n  InvalidSubjectError,\n} from \"./error.js\"\nimport { generatePKCE } from \"./pkce.js\"\n\n/**\n * The well-known information for an OAuth 2.0 authorization server.\n * @internal\n */\nexport interface WellKnown {\n  /**\n   * The URI to the JWKS endpoint.\n   */\n  jwks_uri: string\n  /**\n   * The URI to the token endpoint.\n   */\n  token_endpoint: string\n  /**\n   * The URI to the authorization endpoint.\n   */\n  authorization_endpoint: string\n}\n\n/**\n * The tokens returned by the auth server.\n */\nexport interface Tokens {\n  /**\n   * The access token.\n   */\n  access: string\n  /**\n   * The refresh token.\n   */\n  refresh: string\n\n  /**\n   * The number of seconds until the access token expires.\n   */\n  expiresIn: number\n}\n\ninterface ResponseLike {\n  json(): Promise<unknown>\n  ok: Response[\"ok\"]\n}\ntype FetchLike = (...args: any[]) => Promise<ResponseLike>\n\n/**\n * The challenge that you can use to verify the code.\n */\nexport type Challenge = {\n  /**\n   * The state that was sent to the redirect URI.\n   */\n  state: string\n  /**\n   * The verifier that was sent to the redirect URI.\n   */\n  verifier?: string\n}\n\n/**\n * Configure the client.\n */\nexport interface ClientInput {\n  /**\n   * The client ID. This is just a string to identify your app.\n   *\n   * If you have a web app and a mobile app, you want to use different client IDs both.\n   *\n   * @example\n   * ```ts\n   * {\n   *   clientID: \"my-client\"\n   * }\n   * ```\n   */\n  clientID: string\n  /**\n   * The URL of your OpenAuth server.\n   *\n   * @example\n   * ```ts\n   * {\n   *   issuer: \"https://auth.myserver.com\"\n   * }\n   * ```\n   */\n  issuer?: string\n  /**\n   * Optionally, override the internally used fetch function.\n   *\n   * This is useful if you are using a polyfilled fetch function in your application and you\n   * want the client to use it too.\n   */\n  fetch?: FetchLike\n}\n\nexport interface AuthorizeOptions {\n  /**\n   * Enable the PKCE flow. This is for SPA apps.\n   *\n   * ```ts\n   * {\n   *   pkce: true\n   * }\n   * ```\n   *\n   * @default false\n   */\n  pkce?: boolean\n  /**\n   * The provider you want to use for the OAuth flow.\n   *\n   * ```ts\n   * {\n   *   provider: \"google\"\n   * }\n   * ```\n   *\n   * If no provider is specified, the user is directed to a page where they can select from the\n   * list of configured providers.\n   *\n   * If there's only one provider configured, the user will be redirected to that.\n   */\n  provider?: string\n}\n\nexport interface AuthorizeResult {\n  /**\n   * The challenge that you can use to verify the code. This is for the PKCE flow for SPA apps.\n   *\n   * This is an object that you _stringify_ and store it in session storage.\n   *\n   * ```ts\n   * sessionStorage.setItem(\"challenge\", JSON.stringify(challenge))\n   * ```\n   */\n  challenge: Challenge\n  /**\n   * The URL to redirect the user to. This starts the OAuth flow.\n   *\n   * For example, for SPA apps.\n   *\n   * ```ts\n   * location.href = url\n   * ```\n   */\n  url: string\n}\n\n/**\n * Returned when the exchange is successful.\n */\nexport interface ExchangeSuccess {\n  /**\n   * This is always `false` when the exchange is successful.\n   */\n  err: false\n  /**\n   * The access and refresh tokens.\n   */\n  tokens: Tokens\n}\n\n/**\n * Returned when the exchange fails.\n */\nexport interface ExchangeError {\n  /**\n   * The type of error that occurred. You can handle this by checking the type.\n   *\n   * @example\n   * ```ts\n   * import { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n   *\n   * console.log(err instanceof InvalidAuthorizationCodeError)\n   *```\n   */\n  err: InvalidAuthorizationCodeError\n}\n\nexport interface RefreshOptions {\n  /**\n   * Optionally, pass in the access token.\n   */\n  access?: string\n}\n\n/**\n * Returned when the refresh is successful.\n */\nexport interface RefreshSuccess {\n  /**\n   * This is always `false` when the refresh is successful.\n   */\n  err: false\n  /**\n   * Returns the refreshed tokens only if they've been refreshed.\n   *\n   * If they are still valid, this will be `undefined`.\n   */\n  tokens?: Tokens\n}\n\n/**\n * Returned when the refresh fails.\n */\nexport interface RefreshError {\n  /**\n   * The type of error that occurred. You can handle this by checking the type.\n   *\n   * @example\n   * ```ts\n   * import { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n   *\n   * console.log(err instanceof InvalidRefreshTokenError)\n   *```\n   */\n  err: InvalidRefreshTokenError | InvalidAccessTokenError\n}\n\nexport interface VerifyOptions {\n  /**\n   * Optionally, pass in the refresh token.\n   *\n   * If passed in, this will automatically refresh the access token if it has expired.\n   */\n  refresh?: string\n  /**\n   * @internal\n   */\n  issuer?: string\n  /**\n   * @internal\n   */\n  audience?: string\n  /**\n   * Optionally, override the internally used fetch function.\n   *\n   * This is useful if you are using a polyfilled fetch function in your application and you\n   * want the client to use it too.\n   */\n  fetch?: FetchLike\n}\n\nexport interface VerifyResult<T extends SubjectSchema> {\n  /**\n   * This is always `undefined` when the verify is successful.\n   */\n  err?: undefined\n  /**\n   * Returns the refreshed tokens only if they’ve been refreshed.\n   *\n   * If they are still valid, this will be undefined.\n   */\n  tokens?: Tokens\n  /**\n   * @internal\n   */\n  aud: string\n  /**\n   * The decoded subjects from the access token.\n   *\n   * Has the same shape as the subjects you defined when creating the issuer.\n   */\n  subject: {\n    [type in keyof T]: { type: type; properties: v1.InferOutput<T[type]> }\n  }[keyof T]\n}\n\n/**\n * Returned when the verify call fails.\n */\nexport interface VerifyError {\n  /**\n   * The type of error that occurred. You can handle this by checking the type.\n   *\n   * @example\n   * ```ts\n   * import { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n   *\n   * console.log(err instanceof InvalidRefreshTokenError)\n   *```\n   */\n  err: InvalidRefreshTokenError | InvalidAccessTokenError\n}\n\n/**\n * An instance of the OpenAuth client contains the following methods.\n */\nexport interface Client {\n  /**\n   * Start the autorization flow. For example, in SSR sites.\n   *\n   * ```ts\n   * const { url } = await client.authorize(<redirect_uri>, \"code\")\n   * ```\n   *\n   * This takes a redirect URI and the type of flow you want to use. The redirect URI is the\n   * location where the user will be redirected to after the flow is complete.\n   *\n   * Supports both the _code_ and _token_ flows. We recommend using the _code_ flow as it's more\n   * secure.\n   *\n   * :::tip\n   * This returns a URL to redirect the user to. This starts the OAuth flow.\n   * :::\n   *\n   * This returns a URL to the auth server. You can redirect the user to the URL to start the\n   * OAuth flow.\n   *\n   * For SPA apps, we recommend using the PKCE flow.\n   *\n   * ```ts {4}\n   * const { challenge, url } = await client.authorize(\n   *   <redirect_uri>,\n   *   \"code\",\n   *   { pkce: true }\n   * )\n   * ```\n   *\n   * This returns a redirect URL and a challenge that you need to use later to verify the code.\n   */\n  authorize(\n    redirectURI: string,\n    response: \"code\" | \"token\",\n    opts?: AuthorizeOptions,\n  ): Promise<AuthorizeResult>\n  /**\n   * Exchange the code for access and refresh tokens.\n   *\n   * ```ts\n   * const exchanged = await client.exchange(<code>, <redirect_uri>)\n   * ```\n   *\n   * You call this after the user has been redirected back to your app after the OAuth flow.\n   *\n   * :::tip\n   * For SSR sites, the code is returned in the query parameter.\n   * :::\n   *\n   * So the code comes from the query parameter in the redirect URI. The redirect URI here is\n   * the one that you passed in to the `authorize` call when starting the flow.\n   *\n   * :::tip\n   * For SPA sites, the code is returned through the URL hash.\n   * :::\n   *\n   * If you used the PKCE flow for an SPA app, the code is returned as a part of the redirect URL\n   * hash.\n   *\n   * ```ts {4}\n   * const exchanged = await client.exchange(\n   *   <code>,\n   *   <redirect_uri>,\n   *   <challenge.verifier>\n   * )\n   * ```\n   *\n   * You also need to pass in the previously stored challenge verifier.\n   *\n   * This method returns the access and refresh tokens. Or if it fails, it returns an error that\n   * you can handle depending on the error.\n   *\n   * ```ts\n   * import { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n   *\n   * if (exchanged.err) {\n   *   if (exchanged.err instanceof InvalidAuthorizationCodeError) {\n   *     // handle invalid code error\n   *   }\n   *   else {\n   *     // handle other errors\n   *   }\n   * }\n   *\n   * const { access, refresh } = exchanged.tokens\n   * ```\n   */\n  exchange(\n    code: string,\n    redirectURI: string,\n    verifier?: string,\n  ): Promise<ExchangeSuccess | ExchangeError>\n  /**\n   * Refreshes the tokens if they have expired. This is used in an SPA app to maintain the\n   * session, without logging the user out.\n   *\n   * ```ts\n   * const next = await client.refresh(<refresh_token>)\n   * ```\n   *\n   * Can optionally take the access token as well. If passed in, this will skip the refresh\n   * if the access token is still valid.\n   *\n   * ```ts\n   * const next = await client.refresh(<refresh_token>, { access: <access_token> })\n   * ```\n   *\n   * This returns the refreshed tokens only if they've been refreshed.\n   *\n   * ```ts\n   * if (!next.err) {\n   *   // tokens are still valid\n   * }\n   * if (next.tokens) {\n   *   const { access, refresh } = next.tokens\n   * }\n   * ```\n   *\n   * Or if it fails, it returns an error that you can handle depending on the error.\n   *\n   * ```ts\n   * import { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n   *\n   * if (next.err) {\n   *   if (next.err instanceof InvalidRefreshTokenError) {\n   *     // handle invalid refresh token error\n   *   }\n   *   else {\n   *     // handle other errors\n   *   }\n   * }\n   * ```\n   */\n  refresh(\n    refresh: string,\n    opts?: RefreshOptions,\n  ): Promise<RefreshSuccess | RefreshError>\n  /**\n   * Verify the token in the incoming request.\n   *\n   * This is typically used for SSR sites where the token is stored in an HTTP only cookie. And\n   * is passed to the server on every request.\n   *\n   * ```ts\n   * const verified = await client.verify(<subjects>, <token>)\n   * ```\n   *\n   * This takes the subjects that you had previously defined when creating the issuer.\n   *\n   * :::tip\n   * If the refresh token is passed in, it'll automatically refresh the access token.\n   * :::\n   *\n   * This can optionally take the refresh token as well. If passed in, it'll automatically\n   * refresh the access token if it has expired.\n   *\n   * ```ts\n   * const verified = await client.verify(<subjects>, <token>, { refresh: <refresh_token> })\n   * ```\n   *\n   * This returns the decoded subjects from the access token. And the tokens if they've been\n   * refreshed.\n   *\n   * ```ts\n   * // based on the subjects you defined earlier\n   * console.log(verified.subject.properties.userID)\n   *\n   * if (verified.tokens) {\n   *   const { access, refresh } = verified.tokens\n   * }\n   * ```\n   *\n   * Or if it fails, it returns an error that you can handle depending on the error.\n   *\n   * ```ts\n   * import { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n   *\n   * if (verified.err) {\n   *   if (verified.err instanceof InvalidRefreshTokenError) {\n   *     // handle invalid refresh token error\n   *   }\n   *   else {\n   *     // handle other errors\n   *   }\n   * }\n   * ```\n   */\n  verify<T extends SubjectSchema>(\n    subjects: T,\n    token: string,\n    options?: VerifyOptions,\n  ): Promise<VerifyResult<T> | VerifyError>\n}\n\n/**\n * Create an OpenAuth client.\n *\n * @param input - Configure the client.\n */\nexport function createClient(input: ClientInput): Client {\n  const jwksCache = new Map<string, ReturnType<typeof createLocalJWKSet>>()\n  const issuerCache = new Map<string, WellKnown>()\n  const issuer = input.issuer || process.env.OPENAUTH_ISSUER\n  if (!issuer) throw new Error(\"No issuer\")\n  const f = input.fetch ?? fetch\n\n  async function getIssuer() {\n    const cached = issuerCache.get(issuer!)\n    if (cached) return cached\n    const wellKnown = (await (f || fetch)(\n      `${issuer}/.well-known/oauth-authorization-server`,\n    ).then((r) => r.json())) as WellKnown\n    issuerCache.set(issuer!, wellKnown)\n    return wellKnown\n  }\n\n  async function getJWKS() {\n    const wk = await getIssuer()\n    const cached = jwksCache.get(issuer!)\n    if (cached) return cached\n    const keyset = (await (f || fetch)(wk.jwks_uri).then((r) =>\n      r.json(),\n    )) as JSONWebKeySet\n    const result = createLocalJWKSet(keyset)\n    jwksCache.set(issuer!, result)\n    return result\n  }\n\n  const result = {\n    async authorize(\n      redirectURI: string,\n      response: \"code\" | \"token\",\n      opts?: AuthorizeOptions,\n    ) {\n      const result = new URL(issuer + \"/authorize\")\n      const challenge: Challenge = {\n        state: crypto.randomUUID(),\n      }\n      result.searchParams.set(\"client_id\", input.clientID)\n      result.searchParams.set(\"redirect_uri\", redirectURI)\n      result.searchParams.set(\"response_type\", response)\n      result.searchParams.set(\"state\", challenge.state)\n      if (opts?.provider) result.searchParams.set(\"provider\", opts.provider)\n      if (opts?.pkce && response === \"code\") {\n        const pkce = await generatePKCE()\n        result.searchParams.set(\"code_challenge_method\", \"S256\")\n        result.searchParams.set(\"code_challenge\", pkce.challenge)\n        challenge.verifier = pkce.verifier\n      }\n      return {\n        challenge,\n        url: result.toString(),\n      }\n    },\n    /**\n     * @deprecated use `authorize` instead, it will do pkce by default unless disabled with `opts.pkce = false`\n     */\n    async pkce(\n      redirectURI: string,\n      opts?: {\n        provider?: string\n      },\n    ) {\n      const result = new URL(issuer + \"/authorize\")\n      if (opts?.provider) result.searchParams.set(\"provider\", opts.provider)\n      result.searchParams.set(\"client_id\", input.clientID)\n      result.searchParams.set(\"redirect_uri\", redirectURI)\n      result.searchParams.set(\"response_type\", \"code\")\n      const pkce = await generatePKCE()\n      result.searchParams.set(\"code_challenge_method\", \"S256\")\n      result.searchParams.set(\"code_challenge\", pkce.challenge)\n      return [pkce.verifier, result.toString()]\n    },\n    async exchange(\n      code: string,\n      redirectURI: string,\n      verifier?: string,\n    ): Promise<ExchangeSuccess | ExchangeError> {\n      const tokens = await f(issuer + \"/token\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\n        },\n        body: new URLSearchParams({\n          code,\n          redirect_uri: redirectURI,\n          grant_type: \"authorization_code\",\n          client_id: input.clientID,\n          code_verifier: verifier || \"\",\n        }).toString(),\n      })\n      const json = (await tokens.json()) as any\n      if (!tokens.ok) {\n        return {\n          err: new InvalidAuthorizationCodeError(),\n        }\n      }\n      return {\n        err: false,\n        tokens: {\n          access: json.access_token as string,\n          refresh: json.refresh_token as string,\n          expiresIn: json.expires_in as number,\n        },\n      }\n    },\n    async refresh(\n      refresh: string,\n      opts?: RefreshOptions,\n    ): Promise<RefreshSuccess | RefreshError> {\n      if (opts && opts.access) {\n        const decoded = decodeJwt(opts.access)\n        if (!decoded) {\n          return {\n            err: new InvalidAccessTokenError(),\n          }\n        }\n        // allow 30s window for expiration\n        if ((decoded.exp || 0) > Date.now() / 1000 + 30) {\n          return {\n            err: false,\n          }\n        }\n      }\n      const tokens = await f(issuer + \"/token\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\n        },\n        body: new URLSearchParams({\n          grant_type: \"refresh_token\",\n          refresh_token: refresh,\n        }).toString(),\n      })\n      const json = (await tokens.json()) as any\n      if (!tokens.ok) {\n        return {\n          err: new InvalidRefreshTokenError(),\n        }\n      }\n      return {\n        err: false,\n        tokens: {\n          access: json.access_token as string,\n          refresh: json.refresh_token as string,\n          expiresIn: json.expires_in as number,\n        },\n      }\n    },\n    async verify<T extends SubjectSchema>(\n      subjects: T,\n      token: string,\n      options?: VerifyOptions,\n    ): Promise<VerifyResult<T> | VerifyError> {\n      const jwks = await getJWKS()\n      try {\n        const result = await jwtVerify<{\n          mode: \"access\"\n          type: keyof T\n          properties: v1.InferInput<T[keyof T]>\n        }>(token, jwks, {\n          issuer,\n        })\n        const validated = await subjects[result.payload.type][\n          \"~standard\"\n        ].validate(result.payload.properties)\n        if (!validated.issues && result.payload.mode === \"access\")\n          return {\n            aud: result.payload.aud as string,\n            subject: {\n              type: result.payload.type,\n              properties: validated.value,\n            } as any,\n          }\n        return {\n          err: new InvalidSubjectError(),\n        }\n      } catch (e) {\n        if (e instanceof errors.JWTExpired && options?.refresh) {\n          const refreshed = await this.refresh(options.refresh)\n          if (refreshed.err) return refreshed\n          const verified = await result.verify(\n            subjects,\n            refreshed.tokens!.access,\n            {\n              refresh: refreshed.tokens!.refresh,\n              issuer,\n              fetch: options?.fetch,\n            },\n          )\n          if (verified.err) return verified\n          verified.tokens = refreshed.tokens\n          return verified\n        }\n        return {\n          err: new InvalidAccessTokenError(),\n        }\n      }\n    },\n  }\n  return result\n}\n"
  },
  {
    "path": "packages/openauth/src/css.d.ts",
    "content": "declare module \"*.css\" {\n  const css: string\n  export default css\n}\n"
  },
  {
    "path": "packages/openauth/src/error.ts",
    "content": "/**\n * A list of errors that can be thrown by OpenAuth.\n *\n * You can use these errors to check the type of error and handle it. For example.\n *\n * ```ts\n * import { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n *\n * if (err instanceof InvalidAuthorizationCodeError) {\n *   // handle invalid code error\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * The OAuth server returned an error.\n */\nexport class OauthError extends Error {\n  constructor(\n    public error:\n      | \"invalid_request\"\n      | \"invalid_grant\"\n      | \"unauthorized_client\"\n      | \"access_denied\"\n      | \"unsupported_grant_type\"\n      | \"server_error\"\n      | \"temporarily_unavailable\",\n    public description: string,\n  ) {\n    super(error + \" - \" + description)\n  }\n}\n\n/**\n * The `provider` needs to be passed in.\n */\nexport class MissingProviderError extends OauthError {\n  constructor() {\n    super(\n      \"invalid_request\",\n      \"Must specify `provider` query parameter if `select` callback on issuer is not specified\",\n    )\n  }\n}\n\n/**\n * The given parameter is missing.\n */\nexport class MissingParameterError extends OauthError {\n  constructor(public parameter: string) {\n    super(\"invalid_request\", \"Missing parameter: \" + parameter)\n  }\n}\n\n/**\n * The given client is not authorized to use the redirect URI that was passed in.\n */\nexport class UnauthorizedClientError extends OauthError {\n  constructor(\n    public clientID: string,\n    redirectURI: string,\n  ) {\n    super(\n      \"unauthorized_client\",\n      `Client ${clientID} is not authorized to use this redirect_uri: ${redirectURI}`,\n    )\n  }\n}\n\n/**\n * The browser was in an unknown state.\n *\n * This can happen when certain cookies have expired. Or the browser was switched in the middle\n * of the authentication flow.\n */\nexport class UnknownStateError extends Error {\n  constructor() {\n    super(\n      \"The browser was in an unknown state. This could be because certain cookies expired or the browser was switched in the middle of an authentication flow.\",\n    )\n  }\n}\n\n/**\n * The given subject is invalid.\n */\nexport class InvalidSubjectError extends Error {\n  constructor() {\n    super(\"Invalid subject\")\n  }\n}\n\n/**\n * The given refresh token is invalid.\n */\nexport class InvalidRefreshTokenError extends Error {\n  constructor() {\n    super(\"Invalid refresh token\")\n  }\n}\n\n/**\n * The given access token is invalid.\n */\nexport class InvalidAccessTokenError extends Error {\n  constructor() {\n    super(\"Invalid access token\")\n  }\n}\n\n/**\n * The given authorization code is invalid.\n */\nexport class InvalidAuthorizationCodeError extends Error {\n  constructor() {\n    super(\"Invalid authorization code\")\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/index.ts",
    "content": "export {\n  /**\n   * @deprecated\n   * Use `import { createClient } from \"@openauthjs/openauth/client\"` instead - it will tree shake better\n   */\n  createClient,\n} from \"./client.js\"\n\nexport {\n  /**\n   * @deprecated\n   * Use `import { createSubjects } from \"@openauthjs/openauth/subject\"` instead - it will tree shake better\n   */\n  createSubjects,\n} from \"./subject.js\"\n\nimport { issuer } from \"./issuer.js\"\n\nexport {\n  /**\n   * @deprecated\n   * Use `import { issuer } from \"@openauthjs/openauth\"` instead, it was renamed\n   */\n  issuer as authorizer,\n  issuer,\n}\n"
  },
  {
    "path": "packages/openauth/src/issuer.ts",
    "content": "/**\n * The `issuer` create an OpentAuth server, a [Hono](https://hono.dev) app that's\n * designed to run anywhere.\n *\n * The `issuer` function requires a few things:\n *\n * ```ts title=\"issuer.ts\"\n * import { issuer } from \"@openauthjs/openauth\"\n *\n * const app = issuer({\n *   providers: { ... },\n *   storage,\n *   subjects,\n *   success: async (ctx, value) => { ... }\n * })\n * ```\n *\n * #### Add providers\n *\n * You start by specifying the auth providers you are going to use. Let's say you want your users\n * to be able to authenticate with GitHub and with their email and password.\n *\n * ```ts title=\"issuer.ts\"\n * import { GithubProvider } from \"@openauthjs/openauth/provider/github\"\n * import { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n *\n * const app = issuer({\n *   providers: {\n *     github: GithubProvider({\n *       // ...\n *     }),\n *     password: PasswordProvider({\n *       // ...\n *     }),\n *   },\n * })\n * ```\n *\n * #### Handle success\n *\n * The `success` callback receives the payload when a user completes a provider's auth flow.\n *\n * ```ts title=\"issuer.ts\"\n * const 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 = ... // lookup user or create them\n *     }\n *     if (value.provider === \"github\") {\n *       console.log(value.tokenset.access)\n *       userID = ... // lookup user or create them\n *     }\n *     return ctx.subject(\"user\", {\n *       userID\n *     })\n *   }\n * })\n * ```\n *\n * Once complete, the `issuer` issues the access tokens that a client can use. The `ctx.subject`\n * call is what is placed in the access token as a JWT.\n *\n * #### Define subjects\n *\n * You define the shape of these in the `subjects` field.\n *\n * ```ts title=\"subjects.ts\"\n * import { object, string } from \"valibot\"\n * import { createSubjects } from \"@openauthjs/openauth/subject\"\n *\n * const subjects = createSubjects({\n *   user: object({\n *     userID: string()\n *   })\n * })\n * ```\n *\n * It's good to place this in a separate file since this'll be used in your client apps as well.\n *\n * ```ts title=\"issuer.ts\"\n * import { subjects } from \"./subjects.js\"\n *\n * const app = issuer({\n *   providers: { ... },\n *   subjects,\n *   // ...\n * })\n * ```\n *\n * #### Deploy\n *\n * Since `issuer` is a Hono app, you can deploy it anywhere Hono supports.\n *\n * <Tabs>\n *   <TabItem label=\"Node\">\n *   ```ts title=\"issuer.ts\"\n *   import { serve } from \"@hono/node-server\"\n *\n *   serve(app)\n *   ```\n *   </TabItem>\n *   <TabItem label=\"Lambda\">\n *   ```ts title=\"issuer.ts\"\n *   import { handle } from \"hono/aws-lambda\"\n *\n *   export const handler = handle(app)\n *   ```\n *   </TabItem>\n *   <TabItem label=\"Bun\">\n *   ```ts title=\"issuer.ts\"\n *   export default app\n *   ```\n *   </TabItem>\n *   <TabItem label=\"Workers\">\n *   ```ts title=\"issuer.ts\"\n *   export default app\n *   ```\n *   </TabItem>\n * </Tabs>\n *\n * @packageDocumentation\n */\nimport { Provider, ProviderOptions } from \"./provider/provider.js\"\nimport { SubjectPayload, SubjectSchema } from \"./subject.js\"\nimport { Hono } from \"hono/tiny\"\nimport { handle as awsHandle } from \"hono/aws-lambda\"\nimport { Context } from \"hono\"\nimport { deleteCookie, getCookie, setCookie } from \"hono/cookie\"\nimport type { v1 } from \"@standard-schema/spec\"\n\n/**\n * Sets the subject payload in the JWT token and returns the response.\n *\n * ```ts\n * ctx.subject(\"user\", {\n *   userID\n * })\n * ```\n */\nexport interface OnSuccessResponder<\n  T extends { type: string; properties: any },\n> {\n  /**\n   * The `type` is the type of the subject, that was defined in the `subjects` field.\n   *\n   * The `properties` are the properties of the subject. This is the shape of the subject that\n   * you defined in the `subjects` field.\n   */\n  subject<Type extends T[\"type\"]>(\n    type: Type,\n    properties: Extract<T, { type: Type }>[\"properties\"],\n    opts?: {\n      ttl?: {\n        access?: number\n        refresh?: number\n      }\n      subject?: string\n    },\n  ): Promise<Response>\n}\n\n/**\n * @internal\n */\nexport interface AuthorizationState {\n  redirect_uri: string\n  response_type: string\n  state: string\n  client_id: string\n  audience?: string\n  pkce?: {\n    challenge: string\n    method: \"S256\"\n  }\n}\n\n/**\n * @internal\n */\nexport type Prettify<T> = {\n  [K in keyof T]: T[K]\n} & {}\n\nimport {\n  MissingParameterError,\n  OauthError,\n  UnauthorizedClientError,\n  UnknownStateError,\n} from \"./error.js\"\nimport { compactDecrypt, CompactEncrypt, jwtVerify, SignJWT } from \"jose\"\nimport { Storage, StorageAdapter } from \"./storage/storage.js\"\nimport { encryptionKeys, legacySigningKeys, signingKeys } from \"./keys.js\"\nimport { validatePKCE } from \"./pkce.js\"\nimport { Select } from \"./ui/select.js\"\nimport { setTheme, Theme } from \"./ui/theme.js\"\nimport { getRelativeUrl, isDomainMatch, lazy } from \"./util.js\"\nimport { DynamoStorage } from \"./storage/dynamo.js\"\nimport { MemoryStorage } from \"./storage/memory.js\"\nimport { cors } from \"hono/cors\"\nimport { logger } from \"hono/logger\"\n\n/** @internal */\nexport const aws = awsHandle\n\nexport interface IssuerInput<\n  Providers extends Record<string, Provider<any>>,\n  Subjects extends SubjectSchema,\n  Result = {\n    [key in keyof Providers]: Prettify<\n      {\n        provider: key\n      } & (Providers[key] extends Provider<infer T> ? T : {})\n    >\n  }[keyof Providers],\n> {\n  /**\n   * The shape of the subjects that you want to return.\n   *\n   * @example\n   *\n   * ```ts title=\"issuer.ts\"\n   * import { object, string } from \"valibot\"\n   * import { createSubjects } from \"@openauthjs/openauth/subject\"\n   *\n   * issuer({\n   *   subjects: createSubjects({\n   *     user: object({\n   *       userID: string()\n   *     })\n   *   })\n   *   // ...\n   * })\n   * ```\n   */\n  subjects: Subjects\n  /**\n   * The storage adapter that you want to use.\n   *\n   * @example\n   * ```ts title=\"issuer.ts\"\n   * import { DynamoStorage } from \"@openauthjs/openauth/storage/dynamo\"\n   *\n   * issuer({\n   *   storage: DynamoStorage()\n   *   // ...\n   * })\n   * ```\n   */\n  storage?: StorageAdapter\n  /**\n   * The providers that you want your OpenAuth server to support.\n   *\n   * @example\n   *\n   * ```ts title=\"issuer.ts\"\n   * import { GithubProvider } from \"@openauthjs/openauth/provider/github\"\n   *\n   * issuer({\n   *   providers: {\n   *     github: GithubProvider()\n   *   }\n   * })\n   * ```\n   *\n   * The key is just a string that you can use to identify the provider. It's passed back to\n   * the `success` callback.\n   *\n   * You can also specify multiple providers.\n   *\n   * ```ts\n   * {\n   *   providers: {\n   *     github: GithubProvider(),\n   *     google: GoogleProvider()\n   *   }\n   * }\n   * ```\n   */\n  providers: Providers\n  /**\n   * The theme you want to use for the UI.\n   *\n   * This includes the UI the user sees when selecting a provider. And the `PasswordUI` and\n   * `CodeUI` that are used by the `PasswordProvider` and `CodeProvider`.\n   *\n   * @example\n   * ```ts title=\"issuer.ts\"\n   * import { THEME_SST } from \"@openauthjs/openauth/ui/theme\"\n   *\n   * issuer({\n   *   theme: THEME_SST\n   *   // ...\n   * })\n   * ```\n   *\n   * Or define your own.\n   *\n   * ```ts title=\"issuer.ts\"\n   * import type { Theme } from \"@openauthjs/openauth/ui/theme\"\n   *\n   * const MY_THEME: Theme = {\n   *   // ...\n   * }\n   *\n   * issuer({\n   *   theme: MY_THEME\n   *   // ...\n   * })\n   * ```\n   */\n  theme?: Theme\n  /**\n   * Set the TTL, in seconds, for access and refresh tokens.\n   *\n   * @example\n   * ```ts\n   * {\n   *   ttl: {\n   *     access: 60 * 60 * 24 * 30,\n   *     refresh: 60 * 60 * 24 * 365\n   *   }\n   * }\n   * ```\n   */\n  ttl?: {\n    /**\n     * Interval in seconds where the access token is valid.\n     * @default 30d\n     */\n    access?: number\n    /**\n     * Interval in seconds where the refresh token is valid.\n     * @default 1y\n     */\n    refresh?: number\n    /**\n     * Interval in seconds where refresh token reuse is allowed. This helps mitigrate\n     * concurrency issues.\n     * @default 60s\n     */\n    reuse?: number\n    /**\n     * Interval in seconds to retain refresh tokens for reuse detection.\n     * @default 0s\n     */\n    retention?: number\n  }\n  /**\n   * Optionally, configure the UI that's displayed when the user visits the root URL of the\n   * of the OpenAuth server.\n   *\n   * ```ts title=\"issuer.ts\"\n   * import { Select } from \"@openauthjs/openauth/ui/select\"\n   *\n   * issuer({\n   *   select: Select({\n   *     providers: {\n   *       github: { hide: true },\n   *       google: { display: \"Google\" }\n   *     }\n   *   })\n   *   // ...\n   * })\n   * ```\n   *\n   * @default Select()\n   */\n  select?(providers: Record<string, string>, req: Request): Promise<Response>\n  /**\n   * @internal\n   */\n  start?(req: Request): Promise<void>\n  /**\n   * The success callback that's called when the user completes the flow.\n   *\n   * This is called after the user has been redirected back to your app after the OAuth flow.\n   *\n   * @example\n   * ```ts\n   * {\n   *   success: async (ctx, value) => {\n   *     let userID\n   *     if (value.provider === \"password\") {\n   *       console.log(value.email)\n   *       userID = ... // lookup user or create them\n   *     }\n   *     if (value.provider === \"github\") {\n   *       console.log(value.tokenset.access)\n   *       userID = ... // lookup user or create them\n   *     }\n   *     return ctx.subject(\"user\", {\n   *       userID\n   *     })\n   *   },\n   *   // ...\n   * }\n   * ```\n   */\n  success(\n    response: OnSuccessResponder<SubjectPayload<Subjects>>,\n    input: Result,\n    req: Request,\n  ): Promise<Response>\n  /**\n   * @internal\n   */\n  error?(error: UnknownStateError, req: Request): Promise<Response>\n  /**\n   * Override the logic for whether a client request is allowed to call the issuer.\n   *\n   * By default, it uses the following:\n   *\n   * - Allow if the `redirectURI` is localhost.\n   * - Compare `redirectURI` to the request's hostname or the `x-forwarded-host` header. If they\n   *   are from the same sub-domain level, then allow.\n   *\n   * @example\n   * ```ts\n   * {\n   *   allow: async (input, req) => {\n   *     // Allow all clients\n   *     return true\n   *   }\n   * }\n   * ```\n   */\n  allow?(\n    input: {\n      clientID: string\n      redirectURI: string\n      audience?: string\n    },\n    req: Request,\n  ): Promise<boolean>\n}\n\n/**\n * Create an OpenAuth server, a Hono app.\n */\nexport function issuer<\n  Providers extends Record<string, Provider<any>>,\n  Subjects extends SubjectSchema,\n  Result = {\n    [key in keyof Providers]: Prettify<\n      {\n        provider: key\n      } & (Providers[key] extends Provider<infer T> ? T : {})\n    >\n  }[keyof Providers],\n>(input: IssuerInput<Providers, Subjects, Result>) {\n  const error =\n    input.error ??\n    function (err) {\n      return new Response(err.message, {\n        status: 400,\n        headers: {\n          \"Content-Type\": \"text/plain\",\n        },\n      })\n    }\n  const ttlAccess = input.ttl?.access ?? 60 * 60 * 24 * 30\n  const ttlRefresh = input.ttl?.refresh ?? 60 * 60 * 24 * 365\n  const ttlRefreshReuse = input.ttl?.reuse ?? 60\n  const ttlRefreshRetention = input.ttl?.retention ?? 0\n  if (input.theme) {\n    setTheme(input.theme)\n  }\n\n  const select = lazy(() => input.select ?? Select())\n  const allow = lazy(\n    () =>\n      input.allow ??\n      (async (input: any, req: Request) => {\n        const redir = new URL(input.redirectURI).hostname\n        if (redir === \"localhost\" || redir === \"127.0.0.1\") {\n          return true\n        }\n        const forwarded = req.headers.get(\"x-forwarded-host\")\n        const host = forwarded\n          ? new URL(`https://${forwarded}`).hostname\n          : new URL(req.url).hostname\n\n        return isDomainMatch(redir, host)\n      }),\n  )\n\n  let storage = input.storage\n  if (process.env.OPENAUTH_STORAGE) {\n    const parsed = JSON.parse(process.env.OPENAUTH_STORAGE)\n    if (parsed.type === \"dynamo\") storage = DynamoStorage(parsed.options)\n    if (parsed.type === \"memory\") storage = MemoryStorage()\n    if (parsed.type === \"cloudflare\")\n      throw new Error(\n        \"Cloudflare storage cannot be configured through env because it requires bindings.\",\n      )\n  }\n  if (!storage)\n    throw new Error(\n      \"Store is not configured. Either set the `storage` option or set `OPENAUTH_STORAGE` environment variable.\",\n    )\n  const allSigning = lazy(() =>\n    Promise.all([signingKeys(storage), legacySigningKeys(storage)]).then(\n      ([a, b]) => [...a, ...b],\n    ),\n  )\n  const allEncryption = lazy(() => encryptionKeys(storage))\n  const signingKey = lazy(() => allSigning().then((all) => all[0]))\n  const encryptionKey = lazy(() => allEncryption().then((all) => all[0]))\n\n  const auth: Omit<ProviderOptions<any>, \"name\"> = {\n    async success(ctx: Context, properties: any, successOpts) {\n      return await input.success(\n        {\n          async subject(type, properties, subjectOpts) {\n            const authorization = await getAuthorization(ctx)\n            const subject = subjectOpts?.subject\n              ? subjectOpts.subject\n              : await resolveSubject(type, properties)\n            await successOpts?.invalidate?.(\n              await resolveSubject(type, properties),\n            )\n            if (authorization.response_type === \"token\") {\n              const location = new URL(authorization.redirect_uri)\n              const tokens = await generateTokens(ctx, {\n                subject,\n                type: type as string,\n                properties,\n                clientID: authorization.client_id,\n                ttl: {\n                  access: subjectOpts?.ttl?.access ?? ttlAccess,\n                  refresh: subjectOpts?.ttl?.refresh ?? ttlRefresh,\n                },\n              })\n              location.hash = new URLSearchParams({\n                access_token: tokens.access,\n                refresh_token: tokens.refresh,\n                state: authorization.state || \"\",\n              }).toString()\n              await auth.unset(ctx, \"authorization\")\n              return ctx.redirect(location.toString(), 302)\n            }\n            if (authorization.response_type === \"code\") {\n              const code = crypto.randomUUID()\n              await Storage.set(\n                storage,\n                [\"oauth:code\", code],\n                {\n                  type,\n                  properties,\n                  subject,\n                  redirectURI: authorization.redirect_uri,\n                  clientID: authorization.client_id,\n                  pkce: authorization.pkce,\n                  ttl: {\n                    access: subjectOpts?.ttl?.access ?? ttlAccess,\n                    refresh: subjectOpts?.ttl?.refresh ?? ttlRefresh,\n                  },\n                },\n                60,\n              )\n              const location = new URL(authorization.redirect_uri)\n              location.searchParams.set(\"code\", code)\n              location.searchParams.set(\"state\", authorization.state || \"\")\n              await auth.unset(ctx, \"authorization\")\n              return ctx.redirect(location.toString(), 302)\n            }\n            throw new OauthError(\n              \"invalid_request\",\n              `Unsupported response_type: ${authorization.response_type}`,\n            )\n          },\n        },\n        {\n          provider: ctx.get(\"provider\"),\n          ...properties,\n        },\n        ctx.req.raw,\n      )\n    },\n    forward(ctx, response) {\n      return ctx.newResponse(\n        response.body,\n        response.status as any,\n        Object.fromEntries(response.headers.entries()),\n      )\n    },\n    async set(ctx, key, maxAge, value) {\n      setCookie(ctx, key, await encrypt(value), {\n        maxAge,\n        httpOnly: true,\n        ...(ctx.req.url.startsWith(\"https://\")\n          ? { secure: true, sameSite: \"None\" }\n          : {}),\n      })\n    },\n    async get(ctx: Context, key: string) {\n      const raw = getCookie(ctx, key)\n      if (!raw) return\n      return decrypt(raw).catch((ex) => {\n        console.error(\"failed to decrypt\", key, ex)\n      })\n    },\n    async unset(ctx: Context, key: string) {\n      deleteCookie(ctx, key)\n    },\n    async invalidate(subject: string) {\n      // Resolve the scan in case modifications interfere with iteration\n      const keys = await Array.fromAsync(\n        Storage.scan(this.storage, [\"oauth:refresh\", subject]),\n      )\n      for (const [key] of keys) {\n        await Storage.remove(this.storage, key)\n      }\n    },\n    storage,\n  }\n\n  async function getAuthorization(ctx: Context) {\n    const match =\n      (await auth.get(ctx, \"authorization\")) || ctx.get(\"authorization\")\n    if (!match) throw new UnknownStateError()\n    return match as AuthorizationState\n  }\n\n  async function encrypt(value: any) {\n    return await new CompactEncrypt(\n      new TextEncoder().encode(JSON.stringify(value)),\n    )\n      .setProtectedHeader({ alg: \"RSA-OAEP-512\", enc: \"A256GCM\" })\n      .encrypt(await encryptionKey().then((k) => k.public))\n  }\n\n  async function resolveSubject(type: string, properties: any) {\n    const jsonString = JSON.stringify(properties)\n    const encoder = new TextEncoder()\n    const data = encoder.encode(jsonString)\n    const hashBuffer = await crypto.subtle.digest(\"SHA-1\", data)\n    const hashArray = Array.from(new Uint8Array(hashBuffer))\n    const hashHex = hashArray\n      .map((b) => b.toString(16).padStart(2, \"0\"))\n      .join(\"\")\n    return `${type}:${hashHex.slice(0, 16)}`\n  }\n\n  async function generateTokens(\n    ctx: Context,\n    value: {\n      type: string\n      properties: any\n      subject: string\n      clientID: string\n      ttl: {\n        access: number\n        refresh: number\n      }\n      timeUsed?: number\n      nextToken?: string\n    },\n    opts?: {\n      generateRefreshToken?: boolean\n    },\n  ) {\n    const refreshToken = value.nextToken ?? crypto.randomUUID()\n    if (opts?.generateRefreshToken ?? true) {\n      /**\n       * Generate and store the next refresh token after the one we are currently returning.\n       * Reserving these in advance avoids concurrency issues with multiple refreshes.\n       * Similar treatment should be given to any other values that may have race conditions,\n       * for example if a jti claim was added to the access token.\n       */\n      const refreshValue = {\n        ...value,\n        nextToken: crypto.randomUUID(),\n      }\n      delete refreshValue.timeUsed\n      await Storage.set(\n        storage!,\n        [\"oauth:refresh\", value.subject, refreshToken],\n        refreshValue,\n        value.ttl.refresh,\n      )\n    }\n    const accessTimeUsed = Math.floor((value.timeUsed ?? Date.now()) / 1000)\n    return {\n      access: await new SignJWT({\n        mode: \"access\",\n        type: value.type,\n        properties: value.properties,\n        aud: value.clientID,\n        iss: issuer(ctx),\n        sub: value.subject,\n      })\n        .setExpirationTime(Math.floor(accessTimeUsed + value.ttl.access))\n        .setProtectedHeader(\n          await signingKey().then((k) => ({\n            alg: k.alg,\n            kid: k.id,\n            typ: \"JWT\",\n          })),\n        )\n        .sign(await signingKey().then((item) => item.private)),\n      expiresIn: Math.floor(\n        accessTimeUsed + value.ttl.access - Date.now() / 1000,\n      ),\n      refresh: [value.subject, refreshToken].join(\":\"),\n    }\n  }\n\n  async function decrypt(value: string) {\n    return JSON.parse(\n      new TextDecoder().decode(\n        await compactDecrypt(\n          value,\n          await encryptionKey().then((v) => v.private),\n        ).then((value) => value.plaintext),\n      ),\n    )\n  }\n\n  function issuer(ctx: Context) {\n    return new URL(getRelativeUrl(ctx, \"/\")).origin\n  }\n\n  const app = new Hono<{\n    Variables: {\n      authorization: AuthorizationState\n    }\n  }>().use(logger())\n\n  for (const [name, value] of Object.entries(input.providers)) {\n    const route = new Hono<any>()\n    route.use(async (c, next) => {\n      c.set(\"provider\", name)\n      await next()\n    })\n    value.init(route, {\n      name,\n      ...auth,\n    })\n    app.route(`/${name}`, route)\n  }\n\n  app.get(\n    \"/.well-known/jwks.json\",\n    cors({\n      origin: \"*\",\n      allowHeaders: [\"*\"],\n      allowMethods: [\"GET\"],\n      credentials: false,\n    }),\n    async (c) => {\n      const all = await allSigning()\n      return c.json({\n        keys: all.map((item) => ({\n          ...item.jwk,\n          alg: item.alg,\n          exp: item.expired\n            ? Math.floor(item.expired.getTime() / 1000)\n            : undefined,\n        })),\n      })\n    },\n  )\n\n  app.get(\n    \"/.well-known/oauth-authorization-server\",\n    cors({\n      origin: \"*\",\n      allowHeaders: [\"*\"],\n      allowMethods: [\"GET\"],\n      credentials: false,\n    }),\n    async (c) => {\n      const iss = issuer(c)\n      return c.json({\n        issuer: iss,\n        authorization_endpoint: `${iss}/authorize`,\n        token_endpoint: `${iss}/token`,\n        jwks_uri: `${iss}/.well-known/jwks.json`,\n        response_types_supported: [\"code\", \"token\"],\n      })\n    },\n  )\n\n  app.post(\n    \"/token\",\n    cors({\n      origin: \"*\",\n      allowHeaders: [\"*\"],\n      allowMethods: [\"POST\"],\n      credentials: false,\n    }),\n    async (c) => {\n      const form = await c.req.formData()\n      const grantType = form.get(\"grant_type\")\n\n      if (grantType === \"authorization_code\") {\n        const code = form.get(\"code\")\n        if (!code)\n          return c.json(\n            {\n              error: \"invalid_request\",\n              error_description: \"Missing code\",\n            },\n            400,\n          )\n        const key = [\"oauth:code\", code.toString()]\n        const payload = await Storage.get<{\n          type: string\n          properties: any\n          clientID: string\n          redirectURI: string\n          subject: string\n          ttl: {\n            access: number\n            refresh: number\n          }\n          pkce?: AuthorizationState[\"pkce\"]\n        }>(storage, key)\n        if (!payload) {\n          return c.json(\n            {\n              error: \"invalid_grant\",\n              error_description: \"Authorization code has been used or expired\",\n            },\n            400,\n          )\n        }\n        if (payload.redirectURI !== form.get(\"redirect_uri\")) {\n          return c.json(\n            {\n              error: \"invalid_redirect_uri\",\n              error_description: \"Redirect URI mismatch\",\n            },\n            400,\n          )\n        }\n        if (payload.clientID !== form.get(\"client_id\")) {\n          return c.json(\n            {\n              error: \"unauthorized_client\",\n              error_description:\n                \"Client is not authorized to use this authorization code\",\n            },\n            403,\n          )\n        }\n\n        if (payload.pkce) {\n          const codeVerifier = form.get(\"code_verifier\")?.toString()\n          if (!codeVerifier)\n            return c.json(\n              {\n                error: \"invalid_grant\",\n                error_description: \"Missing code_verifier\",\n              },\n              400,\n            )\n\n          if (\n            !(await validatePKCE(\n              codeVerifier,\n              payload.pkce.challenge,\n              payload.pkce.method,\n            ))\n          ) {\n            return c.json(\n              {\n                error: \"invalid_grant\",\n                error_description: \"Code verifier does not match\",\n              },\n              400,\n            )\n          }\n        }\n        const tokens = await generateTokens(c, payload)\n        await Storage.remove(storage, key)\n        return c.json({\n          access_token: tokens.access,\n          expires_in: tokens.expiresIn,\n          refresh_token: tokens.refresh,\n        })\n      }\n\n      if (grantType === \"refresh_token\") {\n        const refreshToken = form.get(\"refresh_token\")\n        if (!refreshToken)\n          return c.json(\n            {\n              error: \"invalid_request\",\n              error_description: \"Missing refresh_token\",\n            },\n            400,\n          )\n        const splits = refreshToken.toString().split(\":\")\n        const token = splits.pop()!\n        const subject = splits.join(\":\")\n        const key = [\"oauth:refresh\", subject, token]\n        const payload = await Storage.get<{\n          type: string\n          properties: any\n          clientID: string\n          subject: string\n          ttl: {\n            access: number\n            refresh: number\n          }\n          nextToken: string\n          timeUsed?: number\n        }>(storage, key)\n        if (!payload) {\n          return c.json(\n            {\n              error: \"invalid_grant\",\n              error_description: \"Refresh token has been used or expired\",\n            },\n            400,\n          )\n        }\n        const generateRefreshToken = !payload.timeUsed\n        if (ttlRefreshReuse <= 0) {\n          // no reuse interval, remove the refresh token immediately\n          await Storage.remove(storage, key)\n        } else if (!payload.timeUsed) {\n          payload.timeUsed = Date.now()\n          await Storage.set(\n            storage,\n            key,\n            payload,\n            ttlRefreshReuse + ttlRefreshRetention,\n          )\n        } else if (Date.now() > payload.timeUsed + ttlRefreshReuse * 1000) {\n          // token was reused past the allowed interval\n          await auth.invalidate(subject)\n          return c.json(\n            {\n              error: \"invalid_grant\",\n              error_description: \"Refresh token has been used or expired\",\n            },\n            400,\n          )\n        }\n        const tokens = await generateTokens(c, payload, {\n          generateRefreshToken,\n        })\n        return c.json({\n          access_token: tokens.access,\n          refresh_token: tokens.refresh,\n          expires_in: tokens.expiresIn,\n        })\n      }\n\n      if (grantType === \"client_credentials\") {\n        const provider = form.get(\"provider\")\n        if (!provider)\n          return c.json({ error: \"missing `provider` form value\" }, 400)\n        const match = input.providers[provider.toString()]\n        if (!match)\n          return c.json({ error: \"invalid `provider` query parameter\" }, 400)\n        if (!match.client)\n          return c.json(\n            { error: \"this provider does not support client_credentials\" },\n            400,\n          )\n        const clientID = form.get(\"client_id\")\n        const clientSecret = form.get(\"client_secret\")\n        if (!clientID)\n          return c.json({ error: \"missing `client_id` form value\" }, 400)\n        if (!clientSecret)\n          return c.json({ error: \"missing `client_secret` form value\" }, 400)\n        const response = await match.client({\n          clientID: clientID.toString(),\n          clientSecret: clientSecret.toString(),\n          params: Object.fromEntries(form) as Record<string, string>,\n        })\n        return input.success(\n          {\n            async subject(type, properties, opts) {\n              const tokens = await generateTokens(c, {\n                type: type as string,\n                subject:\n                  opts?.subject || (await resolveSubject(type, properties)),\n                properties,\n                clientID: clientID.toString(),\n                ttl: {\n                  access: opts?.ttl?.access ?? ttlAccess,\n                  refresh: opts?.ttl?.refresh ?? ttlRefresh,\n                },\n              })\n              return c.json({\n                access_token: tokens.access,\n                refresh_token: tokens.refresh,\n              })\n            },\n          },\n          {\n            provider: provider.toString(),\n            ...response,\n          },\n          c.req.raw,\n        )\n      }\n\n      throw new Error(\"Invalid grant_type\")\n    },\n  )\n\n  app.get(\"/authorize\", async (c) => {\n    const provider = c.req.query(\"provider\")\n    const response_type = c.req.query(\"response_type\")\n    const redirect_uri = c.req.query(\"redirect_uri\")\n    const state = c.req.query(\"state\")\n    const client_id = c.req.query(\"client_id\")\n    const audience = c.req.query(\"audience\")\n    const code_challenge = c.req.query(\"code_challenge\")\n    const code_challenge_method = c.req.query(\"code_challenge_method\")\n    const authorization: AuthorizationState = {\n      response_type,\n      redirect_uri,\n      state,\n      client_id,\n      audience,\n      pkce:\n        code_challenge && code_challenge_method\n          ? {\n              challenge: code_challenge,\n              method: code_challenge_method,\n            }\n          : undefined,\n    } as AuthorizationState\n    c.set(\"authorization\", authorization)\n\n    if (!redirect_uri) {\n      return c.text(\"Missing redirect_uri\", { status: 400 })\n    }\n\n    if (!response_type) {\n      throw new MissingParameterError(\"response_type\")\n    }\n\n    if (!client_id) {\n      throw new MissingParameterError(\"client_id\")\n    }\n\n    if (input.start) {\n      await input.start(c.req.raw)\n    }\n\n    if (\n      !(await allow()(\n        {\n          clientID: client_id,\n          redirectURI: redirect_uri,\n          audience,\n        },\n        c.req.raw,\n      ))\n    )\n      throw new UnauthorizedClientError(client_id, redirect_uri)\n    await auth.set(c, \"authorization\", 60 * 60 * 24, authorization)\n    if (provider) return c.redirect(`/${provider}/authorize`)\n    const providers = Object.keys(input.providers)\n    if (providers.length === 1) return c.redirect(`/${providers[0]}/authorize`)\n    return auth.forward(\n      c,\n      await select()(\n        Object.fromEntries(\n          Object.entries(input.providers).map(([key, value]) => [\n            key,\n            value.type,\n          ]),\n        ),\n        c.req.raw,\n      ),\n    )\n  })\n\n  app.get(\"/userinfo\", async (c) => {\n    const header = c.req.header(\"Authorization\")\n\n    if (!header) {\n      return c.json(\n        {\n          error: \"invalid_request\",\n          error_description: \"Missing Authorization header\",\n        },\n        400,\n      )\n    }\n\n    const [type, token] = header.split(\" \")\n\n    if (type !== \"Bearer\") {\n      return c.json(\n        {\n          error: \"invalid_request\",\n          error_description: \"Missing or invalid Authorization header\",\n        },\n        400,\n      )\n    }\n\n    if (!token) {\n      return c.json(\n        {\n          error: \"invalid_request\",\n          error_description: \"Missing token\",\n        },\n        400,\n      )\n    }\n\n    const result = await jwtVerify<{\n      mode: \"access\"\n      type: keyof SubjectSchema\n      properties: v1.InferInput<SubjectSchema[keyof SubjectSchema]>\n    }>(token, () => signingKey().then((item) => item.public), {\n      issuer: issuer(c),\n    })\n\n    const validated = await input.subjects[result.payload.type][\n      \"~standard\"\n    ].validate(result.payload.properties)\n\n    if (!validated.issues && result.payload.mode === \"access\") {\n      return c.json(validated.value as SubjectSchema)\n    }\n\n    return c.json({\n      error: \"invalid_token\",\n      error_description: \"Invalid token\",\n    })\n  })\n\n  app.onError(async (err, c) => {\n    console.error(err)\n    if (err instanceof UnknownStateError) {\n      return auth.forward(c, await error(err, c.req.raw))\n    }\n    const authorization = await getAuthorization(c)\n    const url = new URL(authorization.redirect_uri)\n    const oauth =\n      err instanceof OauthError\n        ? err\n        : new OauthError(\"server_error\", err.message)\n    url.searchParams.set(\"error\", oauth.error)\n    url.searchParams.set(\"error_description\", oauth.description)\n    return c.redirect(url.toString())\n  })\n\n  return app\n}\n"
  },
  {
    "path": "packages/openauth/src/jwt.ts",
    "content": "import { JWTPayload, jwtVerify, KeyLike, SignJWT } from \"jose\"\n\nexport namespace jwt {\n  export function create(\n    payload: JWTPayload,\n    algorithm: string,\n    privateKey: KeyLike,\n  ) {\n    return new SignJWT(payload)\n      .setProtectedHeader({ alg: algorithm, typ: \"JWT\", kid: \"sst\" })\n      .sign(privateKey)\n  }\n\n  export function verify<T>(token: string, publicKey: KeyLike) {\n    return jwtVerify<T>(token, publicKey)\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/keys.ts",
    "content": "import {\n  exportJWK,\n  exportPKCS8,\n  exportSPKI,\n  generateKeyPair,\n  importPKCS8,\n  importSPKI,\n  JWK,\n  KeyLike,\n} from \"jose\"\nimport { Storage, StorageAdapter } from \"./storage/storage.js\"\n\nconst signingAlg = \"ES256\"\nconst encryptionAlg = \"RSA-OAEP-512\"\n\ninterface SerializedKeyPair {\n  id: string\n  publicKey: string\n  privateKey: string\n  created: number\n  alg: string\n  expired?: number\n}\n\nexport interface KeyPair {\n  id: string\n  alg: string\n  public: KeyLike\n  private: KeyLike\n  created: Date\n  expired?: Date\n  jwk: JWK\n}\n\n/**\n * @deprecated use `signingKeys` instead\n */\nexport async function legacySigningKeys(\n  storage: StorageAdapter,\n): Promise<KeyPair[]> {\n  const alg = \"RS512\"\n  const results = [] as KeyPair[]\n  const scanner = Storage.scan<SerializedKeyPair>(storage, [\"oauth:key\"])\n  for await (const [_key, value] of scanner) {\n    const publicKey = await importSPKI(value.publicKey, alg, {\n      extractable: true,\n    })\n    const privateKey = await importPKCS8(value.privateKey, alg)\n    const jwk = await exportJWK(publicKey)\n    jwk.kid = value.id\n    results.push({\n      id: value.id,\n      alg,\n      created: new Date(value.created),\n      public: publicKey,\n      private: privateKey,\n      expired: new Date(1735858114000),\n      jwk,\n    })\n  }\n  return results\n}\n\nexport async function signingKeys(storage: StorageAdapter): Promise<KeyPair[]> {\n  const results = [] as KeyPair[]\n  const scanner = Storage.scan<SerializedKeyPair>(storage, [\"signing:key\"])\n  for await (const [_key, value] of scanner) {\n    const publicKey = await importSPKI(value.publicKey, value.alg, {\n      extractable: true,\n    })\n    const privateKey = await importPKCS8(value.privateKey, value.alg)\n    const jwk = await exportJWK(publicKey)\n    jwk.kid = value.id\n    jwk.use = \"sig\"\n    results.push({\n      id: value.id,\n      alg: signingAlg,\n      created: new Date(value.created),\n      expired: value.expired ? new Date(value.expired) : undefined,\n      public: publicKey,\n      private: privateKey,\n      jwk,\n    })\n  }\n  results.sort((a, b) => b.created.getTime() - a.created.getTime())\n  if (results.filter((item) => !item.expired).length) return results\n\n  const key = await generateKeyPair(signingAlg, {\n    extractable: true,\n  })\n  const serialized: SerializedKeyPair = {\n    id: crypto.randomUUID(),\n    publicKey: await exportSPKI(key.publicKey),\n    privateKey: await exportPKCS8(key.privateKey),\n    created: Date.now(),\n    alg: signingAlg,\n  }\n  await Storage.set(storage, [\"signing:key\", serialized.id], serialized)\n  return signingKeys(storage)\n}\n\nexport async function encryptionKeys(\n  storage: StorageAdapter,\n): Promise<KeyPair[]> {\n  const results = [] as KeyPair[]\n  const scanner = Storage.scan<SerializedKeyPair>(storage, [\"encryption:key\"])\n  for await (const [_key, value] of scanner) {\n    const publicKey = await importSPKI(value.publicKey, value.alg, {\n      extractable: true,\n    })\n    const privateKey = await importPKCS8(value.privateKey, value.alg)\n    const jwk = await exportJWK(publicKey)\n    jwk.kid = value.id\n    results.push({\n      id: value.id,\n      alg: encryptionAlg,\n      created: new Date(value.created),\n      expired: value.expired ? new Date(value.expired) : undefined,\n      public: publicKey,\n      private: privateKey,\n      jwk,\n    })\n  }\n  results.sort((a, b) => b.created.getTime() - a.created.getTime())\n  if (results.filter((item) => !item.expired).length) return results\n\n  const key = await generateKeyPair(encryptionAlg, {\n    extractable: true,\n  })\n  const serialized: SerializedKeyPair = {\n    id: crypto.randomUUID(),\n    publicKey: await exportSPKI(key.publicKey),\n    privateKey: await exportPKCS8(key.privateKey),\n    created: Date.now(),\n    alg: encryptionAlg,\n  }\n  await Storage.set(storage, [\"encryption:key\", serialized.id], serialized)\n  return encryptionKeys(storage)\n}\n"
  },
  {
    "path": "packages/openauth/src/pkce.ts",
    "content": "import { base64url } from \"jose\"\n\nfunction generateVerifier(length: number): string {\n  const buffer = new Uint8Array(length)\n  crypto.getRandomValues(buffer)\n  return base64url.encode(buffer)\n}\n\nasync function generateChallenge(verifier: string, method: \"S256\" | \"plain\") {\n  if (method === \"plain\") return verifier\n  const encoder = new TextEncoder()\n  const data = encoder.encode(verifier)\n  const hash = await crypto.subtle.digest(\"SHA-256\", data)\n  return base64url.encode(new Uint8Array(hash))\n}\n\nexport async function generatePKCE(length: number = 64) {\n  if (length < 43 || length > 128) {\n    throw new Error(\n      \"Code verifier length must be between 43 and 128 characters\",\n    )\n  }\n  const verifier = generateVerifier(length)\n  const challenge = await generateChallenge(verifier, \"S256\")\n  return {\n    verifier,\n    challenge,\n    method: \"S256\",\n  }\n}\n\nexport async function validatePKCE(\n  verifier: string,\n  challenge: string,\n  method: \"S256\" | \"plain\" = \"S256\",\n) {\n  const generatedChallenge = await generateChallenge(verifier, method)\n  // timing safe equals?\n  return generatedChallenge === challenge\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/apple.ts",
    "content": "/**\n * Use this provider to authenticate with Apple. Supports both OAuth2 and OIDC.\n *\n * #### Using OAuth\n *\n * ```ts {5-8}\n * import { AppleProvider } from \"@openauthjs/openauth/provider/apple\"\n *\n * export default issuer({\n *   providers: {\n *     apple: AppleProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * #### Using OAuth with form_post response mode\n *\n * When requesting name or email scopes from Apple, you must use form_post response mode:\n *\n * ```ts {5-9}\n * import { AppleProvider } from \"@openauthjs/openauth/provider/apple\"\n *\n * export default issuer({\n *   providers: {\n *     apple: AppleProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\",\n *       responseMode: \"form_post\"\n *     })\n *   }\n * })\n * ```\n *\n * #### Using OIDC\n *\n * ```ts {5-7}\n * import { AppleOidcProvider } from \"@openauthjs/openauth/provider/apple\"\n *\n * export default issuer({\n *   providers: {\n *     apple: AppleOidcProvider({\n *       clientID: \"1234567890\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\nimport { OidcProvider, OidcWrappedConfig } from \"./oidc.js\"\n\nexport interface AppleConfig extends Oauth2WrappedConfig {\n  /**\n   * The response mode to use for the authorization request.\n   * Apple requires 'form_post' response mode when requesting name or email scopes.\n   * @default \"query\"\n   */\n  responseMode?: \"query\" | \"form_post\"\n}\nexport interface AppleOidcConfig extends OidcWrappedConfig {}\n\n/**\n * Create an Apple OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * // Using default query response mode (GET callback)\n * AppleProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n *\n * // Using form_post response mode (POST callback)\n * // Required when requesting name or email scope\n * AppleProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\",\n *   responseMode: \"form_post\",\n *   scopes: [\"name\", \"email\"]\n * })\n * ```\n */\nexport function AppleProvider(config: AppleConfig) {\n  const { responseMode, ...restConfig } = config\n  const additionalQuery =\n    responseMode === \"form_post\"\n      ? { response_mode: \"form_post\", ...config.query }\n      : config.query || {}\n\n  return Oauth2Provider({\n    ...restConfig,\n    type: \"apple\" as const,\n    endpoint: {\n      authorization: \"https://appleid.apple.com/auth/authorize\",\n      token: \"https://appleid.apple.com/auth/token\",\n      jwks: \"https://appleid.apple.com/auth/keys\",\n    },\n    query: additionalQuery,\n  })\n}\n\n/**\n * Create an Apple OIDC provider.\n *\n * This is useful if you just want to verify the user's email address.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * AppleOidcProvider({\n *   clientID: \"1234567890\"\n * })\n * ```\n */\nexport function AppleOidcProvider(config: AppleOidcConfig) {\n  return OidcProvider({\n    ...config,\n    type: \"apple\" as const,\n    issuer: \"https://appleid.apple.com\",\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/arctic.ts",
    "content": "import type { OAuth2Tokens } from \"arctic\"\nimport { Context } from \"hono\"\nimport { Provider } from \"./provider.js\"\nimport { OauthError } from \"../error.js\"\nimport { getRelativeUrl } from \"../util.js\"\n\nexport interface ArcticProviderOptions {\n  scopes: string[]\n  clientID: string\n  clientSecret: string\n  query?: Record<string, string>\n}\n\ninterface ProviderState {\n  state: string\n}\n\nexport function ArcticProvider(\n  provider: new (\n    clientID: string,\n    clientSecret: string,\n    callback: string,\n  ) => {\n    createAuthorizationURL(state: string, scopes: string[]): URL\n    validateAuthorizationCode(code: string): Promise<OAuth2Tokens>\n    refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>\n  },\n  config: ArcticProviderOptions,\n): Provider<{\n  tokenset: OAuth2Tokens\n}> {\n  function getClient(c: Context) {\n    const callback = new URL(c.req.url)\n    const pathname = callback.pathname.replace(/authorize.*$/, \"callback\")\n    const url = getRelativeUrl(c, pathname)\n    return new provider(config.clientID, config.clientSecret, url)\n  }\n  return {\n    type: \"arctic\",\n    init(routes, ctx) {\n      routes.get(\"/authorize\", async (c) => {\n        const client = getClient(c)\n        const state = crypto.randomUUID()\n        await ctx.set(c, \"provider\", 60 * 10, {\n          state,\n        })\n        return c.redirect(client.createAuthorizationURL(state, config.scopes))\n      })\n\n      routes.get(\"/callback\", async (c) => {\n        const client = getClient(c)\n        const provider = (await ctx.get(c, \"provider\")) as ProviderState\n        if (!provider) return c.redirect(\"../authorize\")\n        const code = c.req.query(\"code\")\n        const state = c.req.query(\"state\")\n        if (!code) throw new Error(\"Missing code\")\n        if (state !== provider.state)\n          throw new OauthError(\"invalid_request\", \"Invalid state\")\n        const tokens = await client.validateAuthorizationCode(code)\n        return ctx.success(c, {\n          tokenset: tokens,\n        })\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/code.ts",
    "content": "/**\n * Configures a provider that supports pin code authentication. This is usually paired with the\n * `CodeUI`.\n *\n * ```ts\n * import { CodeUI } from \"@openauthjs/openauth/ui/code\"\n * import { CodeProvider } from \"@openauthjs/openauth/provider/code\"\n *\n * export default issuer({\n *   providers: {\n *     code: CodeProvider(\n *       CodeUI({\n *         copy: {\n *           code_info: \"We'll send a pin code to your email\"\n *         },\n *         sendCode: (claims, code) => console.log(claims.email, code)\n *       })\n *     )\n *   },\n *   // ...\n * })\n * ```\n *\n * You can customize the provider using.\n *\n * ```ts {7-9}\n * const ui = CodeUI({\n *   // ...\n * })\n *\n * export default issuer({\n *   providers: {\n *     code: CodeProvider(\n *       { ...ui, length: 4 }\n *     )\n *   },\n *   // ...\n * })\n * ```\n *\n * Behind the scenes, the `CodeProvider` expects callbacks that implements request handlers\n * that generate the UI for the following.\n *\n * ```ts\n * CodeProvider({\n *   // ...\n *   request: (req, state, form, error) => Promise<Response>\n * })\n * ```\n *\n * This allows you to create your own UI.\n *\n * @packageDocumentation\n */\nimport { Context } from \"hono\"\nimport { Provider } from \"./provider.js\"\nimport { generateUnbiasedDigits, timingSafeCompare } from \"../random.js\"\n\nexport interface CodeProviderConfig<\n  Claims extends Record<string, string> = Record<string, string>,\n> {\n  /**\n   * The length of the pin code.\n   *\n   * @default 6\n   */\n  length?: number\n  /**\n   * The request handler to generate the UI for the code flow.\n   *\n   * Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n   * and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\n   * ojects.\n   *\n   * Also passes in the current `state` of the flow and any `error` that occurred.\n   *\n   * Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\n   * in return.\n   */\n  request: (\n    req: Request,\n    state: CodeProviderState,\n    form?: FormData,\n    error?: CodeProviderError,\n  ) => Promise<Response>\n  /**\n   * Callback to send the pin code to the user.\n   *\n   * @example\n   * ```ts\n   * {\n   *   sendCode: async (claims, code) => {\n   *     // Send the code through the email or phone number based on the claims\n   *   }\n   * }\n   * ```\n   */\n  sendCode: (claims: Claims, code: string) => Promise<void | CodeProviderError>\n}\n\n/**\n * The state of the code flow.\n *\n * | State | Description |\n * | ----- | ----------- |\n * | `start` | The user is asked to enter their email address or phone number to start the flow. |\n * | `code` | The user needs to enter the pin code to verify their _claim_. |\n */\nexport type CodeProviderState =\n  | {\n      type: \"start\"\n    }\n  | {\n      type: \"code\"\n      resend?: boolean\n      code: string\n      claims: Record<string, string>\n    }\n\n/**\n * The errors that can happen on the code flow.\n *\n * | Error | Description |\n * | ----- | ----------- |\n * | `invalid_code` | The code is invalid. |\n * | `invalid_claim` | The _claim_, email or phone number, is invalid. |\n */\nexport type CodeProviderError =\n  | {\n      type: \"invalid_code\"\n    }\n  | {\n      type: \"invalid_claim\"\n      key: string\n      value: string\n    }\n\nexport function CodeProvider<\n  Claims extends Record<string, string> = Record<string, string>,\n>(config: CodeProviderConfig<Claims>): Provider<{ claims: Claims }> {\n  const length = config.length || 6\n  function generate() {\n    return generateUnbiasedDigits(length)\n  }\n\n  return {\n    type: \"code\",\n    init(routes, ctx) {\n      async function transition(\n        c: Context,\n        next: CodeProviderState,\n        fd?: FormData,\n        err?: CodeProviderError,\n      ) {\n        await ctx.set<CodeProviderState>(c, \"provider\", 60 * 60 * 24, next)\n        const resp = ctx.forward(\n          c,\n          await config.request(c.req.raw, next, fd, err),\n        )\n        return resp\n      }\n      routes.get(\"/authorize\", async (c) => {\n        const resp = await transition(c, {\n          type: \"start\",\n        })\n        return resp\n      })\n\n      routes.post(\"/authorize\", async (c) => {\n        const code = generate()\n        const fd = await c.req.formData()\n        const state = await ctx.get<CodeProviderState>(c, \"provider\")\n        const action = fd.get(\"action\")?.toString()\n\n        if (action === \"request\" || action === \"resend\") {\n          const claims = Object.fromEntries(fd) as Claims\n          delete claims.action\n          const err = await config.sendCode(claims, code)\n          if (err) return transition(c, { type: \"start\" }, fd, err)\n          return transition(\n            c,\n            {\n              type: \"code\",\n              resend: action === \"resend\",\n              claims,\n              code,\n            },\n            fd,\n          )\n        }\n\n        if (\n          fd.get(\"action\")?.toString() === \"verify\" &&\n          state.type === \"code\"\n        ) {\n          const fd = await c.req.formData()\n          const compare = fd.get(\"code\")?.toString()\n          if (\n            !state.code ||\n            !compare ||\n            !timingSafeCompare(state.code, compare)\n          ) {\n            return transition(\n              c,\n              {\n                ...state,\n                resend: false,\n              },\n              fd,\n              { type: \"invalid_code\" },\n            )\n          }\n          await ctx.unset(c, \"provider\")\n          return ctx.forward(\n            c,\n            await ctx.success(c, { claims: state.claims as Claims }),\n          )\n        }\n      })\n    },\n  }\n}\n\n/**\n * @internal\n */\nexport type CodeProviderOptions = Parameters<typeof CodeProvider>[0]\n"
  },
  {
    "path": "packages/openauth/src/provider/cognito.ts",
    "content": "/**\n * Use this provider to authenticate with a Cognito OAuth endpoint.\n *\n * ```ts {5-10}\n * import { CognitoProvider } from \"@openauthjs/openauth/provider/cognito\"\n *\n * export default issuer({\n *   providers: {\n *     cognito: CognitoProvider({\n *       domain: \"your-domain.auth.us-east-1.amazoncognito.com\",\n *       region: \"us-east-1\",\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface CognitoConfig extends Oauth2WrappedConfig {\n  /**\n   * The domain of the Cognito User Pool.\n   *\n   * @example\n   * ```ts\n   * {\n   *   domain: \"your-domain.auth.us-east-1.amazoncognito.com\"\n   * }\n   * ```\n   */\n  domain: string\n  /**\n   * The region the Cognito User Pool is in.\n   *\n   * @example\n   * ```ts\n   * {\n   *   region: \"us-east-1\"\n   * }\n   * ```\n   */\n  region: string\n}\n\n/**\n * Create a Cognito OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * CognitoProvider({\n *   domain: \"your-domain.auth.us-east-1.amazoncognito.com\",\n *   region: \"us-east-1\",\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function CognitoProvider(config: CognitoConfig) {\n  const domain = `${config.domain}.auth.${config.region}.amazoncognito.com`\n\n  return Oauth2Provider({\n    type: \"cognito\",\n    ...config,\n    endpoint: {\n      authorization: `https://${domain}/oauth2/authorize`,\n      token: `https://${domain}/oauth2/token`,\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/discord.ts",
    "content": "/**\n * Use this provider to authenticate with Discord.\n *\n * ```ts {5-8}\n * import { DiscordProvider } from \"@openauthjs/openauth/provider/discord\"\n *\n * export default issuer({\n *   providers: {\n *     discord: DiscordProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface DiscordConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a Discord OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * DiscordProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function DiscordProvider(config: DiscordConfig) {\n  return Oauth2Provider({\n    type: \"discord\",\n    ...config,\n    endpoint: {\n      authorization: \"https://discord.com/oauth2/authorize\",\n      token: \"https://discord.com/api/oauth2/token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/facebook.ts",
    "content": "/**\n * Use this provider to authenticate with Facebook. Supports both OAuth2 and OIDC.\n *\n * #### Using OAuth\n *\n * ```ts {5-8}\n * import { FacebookProvider } from \"@openauthjs/openauth/provider/facebook\"\n *\n * export default issuer({\n *   providers: {\n *     facebook: FacebookProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * #### Using OIDC\n *\n * ```ts {5-7}\n * import { FacebookOidcProvider } from \"@openauthjs/openauth/provider/facebook\"\n *\n * export default issuer({\n *   providers: {\n *     facebook: FacebookOidcProvider({\n *       clientID: \"1234567890\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\nimport { OidcProvider, OidcWrappedConfig } from \"./oidc.js\"\n\nexport interface FacebookConfig extends Oauth2WrappedConfig {}\nexport interface FacebookOidcConfig extends OidcWrappedConfig {}\n\n/**\n * Create a Facebook OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * FacebookProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function FacebookProvider(config: FacebookConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"facebook\",\n    endpoint: {\n      authorization: \"https://www.facebook.com/v12.0/dialog/oauth\",\n      token: \"https://graph.facebook.com/v12.0/oauth/access_token\",\n    },\n  })\n}\n\n/**\n * Create a Facebook OIDC provider.\n *\n * This is useful if you just want to verify the user's email address.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * FacebookOidcProvider({\n *   clientID: \"1234567890\"\n * })\n * ```\n */\nexport function FacebookOidcProvider(config: FacebookOidcConfig) {\n  return OidcProvider({\n    ...config,\n    type: \"facebook\",\n    issuer: \"https://graph.facebook.com\",\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/github.ts",
    "content": "/**\n * Use this provider to authenticate with Github.\n *\n * ```ts {5-8}\n * import { GithubProvider } from \"@openauthjs/openauth/provider/github\"\n *\n * export default issuer({\n *   providers: {\n *     github: GithubProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface GithubConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a Github OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * GithubProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function GithubProvider(config: GithubConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"github\",\n    endpoint: {\n      authorization: \"https://github.com/login/oauth/authorize\",\n      token: \"https://github.com/login/oauth/access_token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/google.ts",
    "content": "/**\n * Use this provider to authenticate with Google. Supports both OAuth2 and OIDC.\n *\n * #### Using OAuth\n *\n * ```ts {5-8}\n * import { GoogleProvider } from \"@openauthjs/openauth/provider/google\"\n *\n * export default issuer({\n *   providers: {\n *     google: GoogleProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * #### Using OIDC\n *\n * ```ts {5-7}\n * import { GoogleOidcProvider } from \"@openauthjs/openauth/provider/google\"\n *\n * export default issuer({\n *   providers: {\n *     google: GoogleOidcProvider({\n *       clientID: \"1234567890\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\nimport { OidcProvider, OidcWrappedConfig } from \"./oidc.js\"\n\nexport interface GoogleConfig extends Oauth2WrappedConfig {}\nexport interface GoogleOidcConfig extends OidcWrappedConfig {}\n\n/**\n * Create a Google OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * GoogleProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function GoogleProvider(config: GoogleConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"google\",\n    endpoint: {\n      authorization: \"https://accounts.google.com/o/oauth2/v2/auth\",\n      token: \"https://oauth2.googleapis.com/token\",\n      jwks: \"https://www.googleapis.com/oauth2/v3/certs\",\n    },\n  })\n}\n\n/**\n * Create a Google OIDC provider.\n *\n * This is useful if you just want to verify the user's email address.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * GoogleOidcProvider({\n *   clientID: \"1234567890\"\n * })\n * ```\n */\nexport function GoogleOidcProvider(config: GoogleOidcConfig) {\n  return OidcProvider({\n    ...config,\n    type: \"google\",\n    issuer: \"https://accounts.google.com\",\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/index.ts",
    "content": "export * from \"./code.js\"\nexport type { Provider as Provider } from \"./provider.js\"\nexport * from \"./spotify.js\"\n"
  },
  {
    "path": "packages/openauth/src/provider/jumpcloud.ts",
    "content": "/**\n * Use this provider to authenticate with JumpCloud.\n *\n * ```ts {5-8}\n * import { JumpCloudProvider } from \"@openauthjs/openauth/provider/jumpcloud\"\n *\n * export default issuer({\n *   providers: {\n *     jumpcloud: JumpCloudProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface JumpCloudConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a JumpCloud OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * JumpCloudProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function JumpCloudProvider(config: JumpCloudConfig) {\n  return Oauth2Provider({\n    type: \"jumpcloud\",\n    ...config,\n    endpoint: {\n      authorization: \"https://oauth.id.jumpcloud.com/oauth2/auth\",\n      token: \"https://oauth.id.jumpcloud.com/oauth2/token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/keycloak.ts",
    "content": "/**\n * Use this provider to authenticate with a Keycloak server.\n *\n * ```ts {5-10}\n * import { KeycloakProvider } from \"@openauthjs/openauth/provider/keycloak\"\n *\n * export default issuer({\n *   providers: {\n *     keycloak: KeycloakProvider({\n *       baseUrl: \"https://your-keycloak-domain\",\n *       realm: \"your-realm\",\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface KeycloakConfig extends Oauth2WrappedConfig {\n  /**\n   * The base URL of the Keycloak server.\n   *\n   * @example\n   * ```ts\n   * {\n   *   baseUrl: \"https://your-keycloak-domain\"\n   * }\n   * ```\n   */\n  baseUrl: string\n  /**\n   * The realm in the Keycloak server to authenticate against.\n   *\n   * A realm in Keycloak is like a tenant or namespace that manages a set of\n   * users, credentials, roles, and groups.\n   *\n   * @example\n   * ```ts\n   * {\n   *   realm: \"your-realm\"\n   * }\n   * ```\n   */\n  realm: string\n}\n\n/**\n * Create a Keycloak OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * KeycloakProvider({\n *   baseUrl: \"https://your-keycloak-domain\",\n *   realm: \"your-realm\",\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function KeycloakProvider(config: KeycloakConfig) {\n  const baseConfig = {\n    ...config,\n    endpoint: {\n      authorization: `${config.baseUrl}/realms/${config.realm}/protocol/openid-connect/auth`,\n      token: `${config.baseUrl}/realms/${config.realm}/protocol/openid-connect/token`,\n    },\n  }\n  return Oauth2Provider(baseConfig)\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/linkedin.ts",
    "content": "import { Oauth2Provider, type Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport function LinkedInAdapter(config: Oauth2WrappedConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"linkedin\",\n    endpoint: {\n      authorization: \"https://www.linkedin.com/oauth/v2/authorization\",\n      token: \"https://www.linkedin.com/oauth/v2/accessToken\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/microsoft.ts",
    "content": "/**\n * Use this provider to authenticate with Microsoft. Supports both OAuth2 and OIDC.\n *\n * #### Using OAuth\n *\n * ```ts {5-9}\n * import { MicrosoftProvider } from \"@openauthjs/openauth/provider/microsoft\"\n *\n * export default issuer({\n *   providers: {\n *     microsoft: MicrosoftProvider({\n *       tenant: \"1234567890\",\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * #### Using OIDC\n *\n * ```ts {5-7}\n * import { MicrosoftOidcProvider } from \"@openauthjs/openauth/provider/microsoft\"\n *\n * export default issuer({\n *   providers: {\n *     microsoft: MicrosoftOidcProvider({\n *       clientID: \"1234567890\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\nimport { OidcProvider, OidcWrappedConfig } from \"./oidc.js\"\n\nexport interface MicrosoftConfig extends Oauth2WrappedConfig {\n  /**\n   * The tenant ID of the Microsoft account.\n   *\n   * This is usually the same as the client ID.\n   *\n   * @example\n   * ```ts\n   * {\n   *   tenant: \"1234567890\"\n   * }\n   * ```\n   */\n  tenant: string\n}\nexport interface MicrosoftOidcConfig extends OidcWrappedConfig {}\n\n/**\n * Create a Microsoft OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * MicrosoftProvider({\n *   tenant: \"1234567890\",\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function MicrosoftProvider(config: MicrosoftConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"microsoft\",\n    endpoint: {\n      authorization: `https://login.microsoftonline.com/${config?.tenant}/oauth2/v2.0/authorize`,\n      token: `https://login.microsoftonline.com/${config?.tenant}/oauth2/v2.0/token`,\n    },\n  })\n}\n\n/**\n * Create a Microsoft OIDC provider.\n *\n * This is useful if you just want to verify the user's email address.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * MicrosoftOidcProvider({\n *   clientID: \"1234567890\"\n * })\n * ```\n */\nexport function MicrosoftOidcProvider(config: MicrosoftOidcConfig) {\n  return OidcProvider({\n    ...config,\n    type: \"microsoft\",\n    issuer: \"https://graph.microsoft.com/oidc/userinfo\",\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/oauth2.ts",
    "content": "/**\n * Use this to connect authentication providers that support OAuth 2.0.\n *\n * ```ts {5-12}\n * import { Oauth2Provider } from \"@openauthjs/openauth/provider/oauth2\"\n *\n * export default issuer({\n *   providers: {\n *     oauth2: Oauth2Provider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\",\n *       endpoint: {\n *         authorization: \"https://auth.myserver.com/authorize\",\n *         token: \"https://auth.myserver.com/token\"\n *       }\n *     })\n *   }\n * })\n * ```\n *\n *\n * @packageDocumentation\n */\n\nimport { createRemoteJWKSet, jwtVerify } from \"jose\"\nimport { OauthError } from \"../error.js\"\nimport { generatePKCE } from \"../pkce.js\"\nimport { getRelativeUrl } from \"../util.js\"\nimport { Provider } from \"./provider.js\"\n\nexport interface Oauth2Config {\n  /**\n   * @internal\n   */\n  type?: string\n  /**\n   * The client ID.\n   *\n   * This is just a string to identify your app.\n   *\n   * @example\n   * ```ts\n   * {\n   *   clientID: \"my-client\"\n   * }\n   * ```\n   */\n  clientID: string\n  /**\n   * The client secret.\n   *\n   * This is a private key that's used to authenticate your app. It should be kept secret.\n   *\n   * @example\n   * ```ts\n   * {\n   *   clientSecret: \"0987654321\"\n   * }\n   * ```\n   */\n  clientSecret: string\n  /**\n   * The URLs of the authorization and token endpoints.\n   *\n   * @example\n   * ```ts\n   * {\n   *   endpoint: {\n   *     authorization: \"https://auth.myserver.com/authorize\",\n   *     token: \"https://auth.myserver.com/token\",\n   *     jwks: \"https://auth.myserver.com/auth/keys\"\n   *   }\n   * }\n   * ```\n   */\n  endpoint: {\n    /**\n     * The URL of the authorization endpoint.\n     */\n    authorization: string\n    /**\n     * The URL of the token endpoint.\n     */\n    token: string\n    /**\n     * The URL of the JWKS endpoint.\n     */\n    jwks?: string\n  }\n  /**\n   * A list of OAuth scopes that you want to request.\n   *\n   * @example\n   * ```ts\n   * {\n   *   scopes: [\"email\", \"profile\"]\n   * }\n   * ```\n   */\n  scopes: string[]\n  /**\n   * Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\n   * Some providers like x.com require this.\n   * @default false\n   */\n  pkce?: boolean\n  /**\n   * Any additional parameters that you want to pass to the authorization endpoint.\n   * @example\n   * ```ts\n   * {\n   *   query: {\n   *     access_type: \"offline\",\n   *     prompt: \"consent\"\n   *   }\n   * }\n   * ```\n   */\n  query?: Record<string, string>\n}\n\n/**\n * @internal\n */\nexport type Oauth2WrappedConfig = Omit<Oauth2Config, \"endpoint\" | \"name\">\n\n/**\n * @internal\n */\nexport interface Oauth2Token {\n  access: string\n  refresh: string\n  expiry: number\n  id?: Record<string, any>\n  raw: Record<string, any>\n}\n\ninterface ProviderState {\n  state: string\n  redirect: string\n  codeVerifier?: string\n}\n\nexport function Oauth2Provider(\n  config: Oauth2Config,\n): Provider<{ tokenset: Oauth2Token; clientID: string }> {\n  const query = config.query || {}\n\n  // Helper function to handle token exchange and response building\n  async function handleCallbackLogic(\n    c: any,\n    ctx: any,\n    provider: ProviderState,\n    code: string | undefined,\n  ) {\n    if (!provider || !code) {\n      return c.redirect(getRelativeUrl(c, \"./authorize\"))\n    }\n\n    const body = new URLSearchParams({\n      client_id: config.clientID,\n      client_secret: config.clientSecret,\n      code,\n      grant_type: \"authorization_code\",\n      redirect_uri: provider.redirect,\n      ...(provider.codeVerifier\n        ? { code_verifier: provider.codeVerifier }\n        : {}),\n    })\n\n    const json: any = await fetch(config.endpoint.token, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n        Accept: \"application/json\",\n      },\n      body: body.toString(),\n    }).then((r) => r.json())\n\n    if (\"error\" in json) {\n      throw new OauthError(json.error, json.error_description)\n    }\n\n    let idTokenPayload: Record<string, any> | null = null\n    if (config.endpoint.jwks) {\n      const jwksEndpoint = new URL(config.endpoint.jwks)\n      // @ts-expect-error bun/node mismatch\n      const jwks = createRemoteJWKSet(jwksEndpoint)\n      const { payload } = await jwtVerify(json.id_token, jwks, {\n        audience: config.clientID,\n      })\n      idTokenPayload = payload\n    }\n\n    return ctx.success(c, {\n      clientID: config.clientID,\n      tokenset: {\n        get access() {\n          return json.access_token\n        },\n        get refresh() {\n          return json.refresh_token\n        },\n        get expiry() {\n          return json.expires_in\n        },\n        get id() {\n          if (!idTokenPayload) return null\n          return idTokenPayload\n        },\n        get raw() {\n          return json\n        },\n      },\n    })\n  }\n\n  return {\n    type: config.type || \"oauth2\",\n    init(routes, ctx) {\n      routes.get(\"/authorize\", async (c) => {\n        const state = crypto.randomUUID()\n        const pkce = config.pkce ? await generatePKCE() : undefined\n        await ctx.set<ProviderState>(c, \"provider\", 60 * 10, {\n          state,\n          redirect: getRelativeUrl(c, \"./callback\"),\n          codeVerifier: pkce?.verifier,\n        })\n        const authorization = new URL(config.endpoint.authorization)\n        authorization.searchParams.set(\"client_id\", config.clientID)\n        authorization.searchParams.set(\n          \"redirect_uri\",\n          getRelativeUrl(c, \"./callback\"),\n        )\n        authorization.searchParams.set(\"response_type\", \"code\")\n        authorization.searchParams.set(\"state\", state)\n        authorization.searchParams.set(\"scope\", config.scopes.join(\" \"))\n        if (pkce) {\n          authorization.searchParams.set(\"code_challenge\", pkce.challenge)\n          authorization.searchParams.set(\"code_challenge_method\", pkce.method)\n        }\n        for (const [key, value] of Object.entries(query)) {\n          authorization.searchParams.set(key, value)\n        }\n        return c.redirect(authorization.toString())\n      })\n\n      routes.get(\"/callback\", async (c) => {\n        const provider = (await ctx.get(c, \"provider\")) as ProviderState\n        const code = c.req.query(\"code\")\n        const state = c.req.query(\"state\")\n        const error = c.req.query(\"error\")\n\n        if (error)\n          throw new OauthError(\n            error.toString() as any,\n            c.req.query(\"error_description\")?.toString() || \"\",\n          )\n        if (\n          !provider ||\n          !code ||\n          (provider.state && state !== provider.state)\n        ) {\n          return c.redirect(getRelativeUrl(c, \"./authorize\"))\n        }\n\n        return handleCallbackLogic(c, ctx, provider, code)\n      })\n\n      routes.post(\"/callback\", async (c) => {\n        const provider = (await ctx.get(c, \"provider\")) as ProviderState\n\n        // Handle form data from POST request\n        const formData = await c.req.formData()\n        const code = formData.get(\"code\")?.toString()\n        const state = formData.get(\"state\")?.toString()\n        const error = formData.get(\"error\")?.toString()\n\n        if (error)\n          throw new OauthError(\n            error as any,\n            formData.get(\"error_description\")?.toString() || \"\",\n          )\n\n        if (\n          !provider ||\n          !code ||\n          (provider.state && state !== provider.state)\n        ) {\n          return c.redirect(getRelativeUrl(c, \"./authorize\"))\n        }\n\n        return handleCallbackLogic(c, ctx, provider, code)\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/oidc.ts",
    "content": "/**\n * Use this to connect authentication providers that support OIDC.\n *\n * ```ts {5-8}\n * import { OidcProvider } from \"@openauthjs/openauth/provider/oidc\"\n *\n * export default issuer({\n *   providers: {\n *     oauth2: OidcProvider({\n *       clientId: \"1234567890\",\n *       issuer: \"https://auth.myserver.com\"\n *     })\n *   }\n * })\n * ```\n *\n *\n * @packageDocumentation\n */\n\nimport { createLocalJWKSet, JSONWebKeySet, jwtVerify } from \"jose\"\nimport { WellKnown } from \"../client.js\"\nimport { OauthError } from \"../error.js\"\nimport { Provider } from \"./provider.js\"\nimport { JWTPayload } from \"hono/utils/jwt/types\"\nimport { getRelativeUrl, lazy } from \"../util.js\"\n\nexport interface OidcConfig {\n  /**\n   * @internal\n   */\n  type?: string\n  /**\n   * The client ID.\n   *\n   * This is just a string to identify your app.\n   *\n   * @example\n   * ```ts\n   * {\n   *   clientID: \"my-client\"\n   * }\n   * ```\n   */\n  clientID: string\n  /**\n   * The URL of your authorization server.\n   *\n   * @example\n   * ```ts\n   * {\n   *   issuer: \"https://auth.myserver.com\"\n   * }\n   * ```\n   */\n  issuer: string\n  /**\n   * A list of OIDC scopes that you want to request.\n   *\n   * @example\n   * ```ts\n   * {\n   *   scopes: [\"openid\", \"profile\", \"email\"]\n   * }\n   * ```\n   */\n  scopes?: string[]\n  /**\n   * Any additional parameters that you want to pass to the authorization endpoint.\n   * @example\n   * ```ts\n   * {\n   *   query: {\n   *     prompt: \"consent\"\n   *   }\n   * }\n   * ```\n   */\n  query?: Record<string, string>\n}\n\n/**\n * @internal\n */\nexport type OidcWrappedConfig = Omit<OidcConfig, \"issuer\" | \"name\">\n\ninterface ProviderState {\n  state: string\n  nonce: string\n  redirect: string\n}\n\n/**\n * @internal\n */\nexport interface IdTokenResponse {\n  idToken: string\n  claims: Record<string, any>\n  raw: Record<string, any>\n}\n\nexport function OidcProvider(\n  config: OidcConfig,\n): Provider<{ id: JWTPayload; clientID: string }> {\n  const query = config.query || {}\n  const scopes = config.scopes || []\n\n  const wk = lazy(() =>\n    fetch(config.issuer + \"/.well-known/openid-configuration\").then(\n      async (r) => {\n        if (!r.ok) throw new Error(await r.text())\n        return r.json() as Promise<WellKnown>\n      },\n    ),\n  )\n\n  const jwks = lazy(() =>\n    wk()\n      .then((r) => r.jwks_uri)\n      .then(async (uri) => {\n        const r = await fetch(uri)\n        if (!r.ok) throw new Error(await r.text())\n        return createLocalJWKSet((await r.json()) as JSONWebKeySet)\n      }),\n  )\n\n  return {\n    type: config.type || \"oidc\",\n    init(routes, ctx) {\n      routes.get(\"/authorize\", async (c) => {\n        const provider: ProviderState = {\n          state: crypto.randomUUID(),\n          nonce: crypto.randomUUID(),\n          redirect: getRelativeUrl(c, \"./callback\"),\n        }\n        await ctx.set(c, \"provider\", 60 * 10, provider)\n        const authorization = new URL(\n          await wk().then((r) => r.authorization_endpoint),\n        )\n        authorization.searchParams.set(\"client_id\", config.clientID)\n        authorization.searchParams.set(\"response_type\", \"id_token\")\n        authorization.searchParams.set(\"response_mode\", \"form_post\")\n        authorization.searchParams.set(\"state\", provider.state)\n        authorization.searchParams.set(\"nonce\", provider.nonce)\n        authorization.searchParams.set(\"redirect_uri\", provider.redirect)\n        authorization.searchParams.set(\"scope\", [\"openid\", ...scopes].join(\" \"))\n        for (const [key, value] of Object.entries(query)) {\n          authorization.searchParams.set(key, value)\n        }\n        return c.redirect(authorization.toString())\n      })\n\n      routes.post(\"/callback\", async (c) => {\n        const provider = await ctx.get<ProviderState>(c, \"provider\")\n        if (!provider) return c.redirect(getRelativeUrl(c, \"./authorize\"))\n        const body = await c.req.formData()\n        const error = body.get(\"error\")\n        if (error)\n          throw new OauthError(\n            error.toString() as any,\n            body.get(\"error_description\")?.toString() || \"\",\n          )\n        const idToken = body.get(\"id_token\")\n        if (!idToken)\n          throw new OauthError(\"invalid_request\", \"Missing id_token\")\n        const result = await jwtVerify(idToken.toString(), await jwks(), {\n          audience: config.clientID,\n        })\n        if (result.payload.nonce !== provider.nonce) {\n          throw new OauthError(\"invalid_request\", \"Invalid nonce\")\n        }\n        return ctx.success(c, {\n          id: result.payload,\n          clientID: config.clientID,\n        })\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/password.ts",
    "content": "/**\n * Configures a provider that supports username and password authentication. This is usually\n * paired with the `PasswordUI`.\n *\n * ```ts\n * import { PasswordUI } from \"@openauthjs/openauth/ui/password\"\n * import { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n *\n * export default issuer({\n *   providers: {\n *     password: PasswordProvider(\n *       PasswordUI({\n *         copy: {\n *           error_email_taken: \"This email is already taken.\"\n *         },\n *         sendCode: (email, code) => console.log(email, code)\n *       })\n *     )\n *   },\n *   // ...\n * })\n * ```\n *\n * Behind the scenes, the `PasswordProvider` expects callbacks that implements request handlers\n * that generate the UI for the following.\n *\n * ```ts\n * PasswordProvider({\n *   // ...\n *   login: (req, form, error) => Promise<Response>\n *   register: (req, state, form, error) => Promise<Response>\n *   change: (req, state, form, error) => Promise<Response>\n * })\n * ```\n *\n * This allows you to create your own UI for each of these screens.\n *\n * @packageDocumentation\n */\nimport { UnknownStateError } from \"../error.js\"\nimport { Storage } from \"../storage/storage.js\"\nimport { Provider } from \"./provider.js\"\nimport { generateUnbiasedDigits, timingSafeCompare } from \"../random.js\"\nimport { v1 } from \"@standard-schema/spec\"\n\n/**\n * @internal\n */\nexport interface PasswordHasher<T> {\n  hash(password: string): Promise<T>\n  verify(password: string, compare: T): Promise<boolean>\n}\n\nexport interface PasswordConfig {\n  /**\n   * @internal\n   */\n  length?: number\n  /**\n   * @internal\n   */\n  hasher?: PasswordHasher<any>\n  /**\n   * The request handler to generate the UI for the login screen.\n   *\n   * Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n   * and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\n   * ojects.\n   *\n   * In case of an error, this is called again with the `error`.\n   *\n   * Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\n   * in return.\n   */\n  login: (\n    req: Request,\n    form?: FormData,\n    error?: PasswordLoginError,\n  ) => Promise<Response>\n  /**\n   * The request handler to generate the UI for the register screen.\n   *\n   * Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n   * and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\n   * ojects.\n   *\n   * Also passes in the current `state` of the flow and any `error` that occurred.\n   *\n   * Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\n   * in return.\n   */\n  register: (\n    req: Request,\n    state: PasswordRegisterState,\n    form?: FormData,\n    error?: PasswordRegisterError,\n  ) => Promise<Response>\n  /**\n   * The request handler to generate the UI for the change password screen.\n   *\n   * Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n   * and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\n   * ojects.\n   *\n   * Also passes in the current `state` of the flow and any `error` that occurred.\n   *\n   * Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\n   * in return.\n   */\n  change: (\n    req: Request,\n    state: PasswordChangeState,\n    form?: FormData,\n    error?: PasswordChangeError,\n  ) => Promise<Response>\n  /**\n   * Callback to send the confirmation pin code to the user.\n   *\n   * @example\n   * ```ts\n   * {\n   *   sendCode: async (email, code) => {\n   *     // Send an email with the code\n   *   }\n   * }\n   * ```\n   */\n  sendCode: (email: string, code: string) => Promise<void>\n  /**\n   * Callback to validate the password on sign up and password reset.\n   *\n   * @example\n   * ```ts\n   * {\n   *   validatePassword: (password) => {\n   *      return password.length < 8 ? \"Password must be at least 8 characters\" : undefined\n   *   }\n   * }\n   * ```\n   */\n  validatePassword?:\n    | v1.StandardSchema\n    | ((password: string) => Promise<string | undefined> | string | undefined)\n}\n\n/**\n * The states that can happen on the register screen.\n *\n * | State | Description |\n * | ----- | ----------- |\n * | `start` | The user is asked to enter their email address and password to start the flow. |\n * | `code` | The user needs to enter the pin code to verify their email. |\n */\nexport type PasswordRegisterState =\n  | {\n      type: \"start\"\n    }\n  | {\n      type: \"code\"\n      code: string\n      email: string\n      password: string\n    }\n\n/**\n * The errors that can happen on the register screen.\n *\n * | Error | Description |\n * | ----- | ----------- |\n * | `email_taken` | The email is already taken. |\n * | `invalid_email` | The email is invalid. |\n * | `invalid_code` | The code is invalid. |\n * | `invalid_password` | The password is invalid. |\n * | `password_mismatch` | The passwords do not match. |\n */\nexport type PasswordRegisterError =\n  | {\n      type: \"invalid_code\"\n    }\n  | {\n      type: \"email_taken\"\n    }\n  | {\n      type: \"invalid_email\"\n    }\n  | {\n      type: \"invalid_password\"\n    }\n  | {\n      type: \"password_mismatch\"\n    }\n  | {\n      type: \"validation_error\"\n      message?: string\n    }\n\n/**\n * The state of the password change flow.\n *\n * | State | Description |\n * | ----- | ----------- |\n * | `start` | The user is asked to enter their email address to start the flow. |\n * | `code` | The user needs to enter the pin code to verify their email. |\n * | `update` | The user is asked to enter their new password and confirm it. |\n */\nexport type PasswordChangeState =\n  | {\n      type: \"start\"\n      redirect: string\n    }\n  | {\n      type: \"code\"\n      code: string\n      email: string\n      redirect: string\n    }\n  | {\n      type: \"update\"\n      redirect: string\n      email: string\n    }\n\n/**\n * The errors that can happen on the change password screen.\n *\n * | Error | Description |\n * | ----- | ----------- |\n * | `invalid_email` | The email is invalid. |\n * | `invalid_code` | The code is invalid. |\n * | `invalid_password` | The password is invalid. |\n * | `password_mismatch` | The passwords do not match. |\n */\nexport type PasswordChangeError =\n  | {\n      type: \"invalid_email\"\n    }\n  | {\n      type: \"invalid_code\"\n    }\n  | {\n      type: \"invalid_password\"\n    }\n  | {\n      type: \"password_mismatch\"\n    }\n  | {\n      type: \"validation_error\"\n      message: string\n    }\n\n/**\n * The errors that can happen on the login screen.\n *\n * | Error | Description |\n * | ----- | ----------- |\n * | `invalid_email` | The email is invalid. |\n * | `invalid_password` | The password is invalid. |\n */\nexport type PasswordLoginError =\n  | {\n      type: \"invalid_password\"\n    }\n  | {\n      type: \"invalid_email\"\n    }\n\nexport function PasswordProvider(\n  config: PasswordConfig,\n): Provider<{ email: string }> {\n  const hasher = config.hasher ?? ScryptHasher()\n  function generate() {\n    return generateUnbiasedDigits(6)\n  }\n  return {\n    type: \"password\",\n    init(routes, ctx) {\n      routes.get(\"/authorize\", async (c) =>\n        ctx.forward(c, await config.login(c.req.raw)),\n      )\n\n      routes.post(\"/authorize\", async (c) => {\n        const fd = await c.req.formData()\n        async function error(err: PasswordLoginError) {\n          return ctx.forward(c, await config.login(c.req.raw, fd, err))\n        }\n        const email = fd.get(\"email\")?.toString()?.toLowerCase()\n        if (!email) return error({ type: \"invalid_email\" })\n        const hash = await Storage.get<HashedPassword>(ctx.storage, [\n          \"email\",\n          email,\n          \"password\",\n        ])\n        const password = fd.get(\"password\")?.toString()\n        if (!password || !hash || !(await hasher.verify(password, hash)))\n          return error({ type: \"invalid_password\" })\n        return ctx.success(\n          c,\n          {\n            email: email,\n          },\n          {\n            invalidate: async (subject) => {\n              await Storage.set(\n                ctx.storage,\n                [\"email\", email, \"subject\"],\n                subject,\n              )\n            },\n          },\n        )\n      })\n\n      routes.get(\"/register\", async (c) => {\n        const state: PasswordRegisterState = {\n          type: \"start\",\n        }\n        await ctx.set(c, \"provider\", 60 * 60 * 24, state)\n        return ctx.forward(c, await config.register(c.req.raw, state))\n      })\n\n      routes.post(\"/register\", async (c) => {\n        const fd = await c.req.formData()\n        const email = fd.get(\"email\")?.toString()?.toLowerCase()\n        const action = fd.get(\"action\")?.toString()\n        const provider = await ctx.get<PasswordRegisterState>(c, \"provider\")\n\n        async function transition(\n          next: PasswordRegisterState,\n          err?: PasswordRegisterError,\n        ) {\n          await ctx.set<PasswordRegisterState>(\n            c,\n            \"provider\",\n            60 * 60 * 24,\n            next,\n          )\n          return ctx.forward(c, await config.register(c.req.raw, next, fd, err))\n        }\n\n        if (action === \"register\" && provider.type === \"start\") {\n          const password = fd.get(\"password\")?.toString()\n          const repeat = fd.get(\"repeat\")?.toString()\n          if (!email) return transition(provider, { type: \"invalid_email\" })\n          if (!password)\n            return transition(provider, { type: \"invalid_password\" })\n          if (password !== repeat)\n            return transition(provider, { type: \"password_mismatch\" })\n          if (config.validatePassword) {\n            let validationError: string | undefined\n            try {\n              if (typeof config.validatePassword === \"function\") {\n                validationError = await config.validatePassword(password)\n              } else {\n                const res =\n                  await config.validatePassword[\"~standard\"].validate(password)\n\n                if (res.issues?.length) {\n                  throw new Error(\n                    res.issues.map((issue) => issue.message).join(\", \"),\n                  )\n                }\n              }\n            } catch (error) {\n              validationError =\n                error instanceof Error ? error.message : undefined\n            }\n            if (validationError)\n              return transition(provider, {\n                type: \"validation_error\",\n                message: validationError,\n              })\n          }\n          const existing = await Storage.get(ctx.storage, [\n            \"email\",\n            email,\n            \"password\",\n          ])\n          if (existing) return transition(provider, { type: \"email_taken\" })\n          const code = generate()\n          await config.sendCode(email, code)\n          return transition({\n            type: \"code\",\n            code,\n            password: await hasher.hash(password),\n            email,\n          })\n        }\n\n        if (action === \"register\" && provider.type === \"code\") {\n          const code = generate()\n          await config.sendCode(provider.email, code)\n          return transition({\n            type: \"code\",\n            code,\n            password: provider.password,\n            email: provider.email,\n          })\n        }\n\n        if (action === \"verify\" && provider.type === \"code\") {\n          const code = fd.get(\"code\")?.toString()\n          if (!code || !timingSafeCompare(code, provider.code))\n            return transition(provider, { type: \"invalid_code\" })\n          const existing = await Storage.get(ctx.storage, [\n            \"email\",\n            provider.email,\n            \"password\",\n          ])\n          if (existing)\n            return transition({ type: \"start\" }, { type: \"email_taken\" })\n          await Storage.set(\n            ctx.storage,\n            [\"email\", provider.email, \"password\"],\n            provider.password,\n          )\n          return ctx.success(c, {\n            email: provider.email,\n          })\n        }\n\n        return transition({ type: \"start\" })\n      })\n\n      routes.get(\"/change\", async (c) => {\n        let redirect =\n          c.req.query(\"redirect_uri\") || getRelativeUrl(c, \"./authorize\")\n        const state: PasswordChangeState = {\n          type: \"start\",\n          redirect,\n        }\n        await ctx.set(c, \"provider\", 60 * 60 * 24, state)\n        return ctx.forward(c, await config.change(c.req.raw, state))\n      })\n\n      routes.post(\"/change\", async (c) => {\n        const fd = await c.req.formData()\n        const action = fd.get(\"action\")?.toString()\n        const provider = await ctx.get<PasswordChangeState>(c, \"provider\")\n        if (!provider) throw new UnknownStateError()\n\n        async function transition(\n          next: PasswordChangeState,\n          err?: PasswordChangeError,\n        ) {\n          await ctx.set<PasswordChangeState>(c, \"provider\", 60 * 60 * 24, next)\n          return ctx.forward(c, await config.change(c.req.raw, next, fd, err))\n        }\n\n        if (action === \"code\") {\n          const email = fd.get(\"email\")?.toString()?.toLowerCase()\n          if (!email)\n            return transition(\n              { type: \"start\", redirect: provider.redirect },\n              { type: \"invalid_email\" },\n            )\n          const code = generate()\n          await config.sendCode(email, code)\n\n          return transition({\n            type: \"code\",\n            code,\n            email,\n            redirect: provider.redirect,\n          })\n        }\n\n        if (action === \"verify\" && provider.type === \"code\") {\n          const code = fd.get(\"code\")?.toString()\n          if (!code || !timingSafeCompare(code, provider.code))\n            return transition(provider, { type: \"invalid_code\" })\n          return transition({\n            type: \"update\",\n            email: provider.email,\n            redirect: provider.redirect,\n          })\n        }\n\n        if (action === \"update\" && provider.type === \"update\") {\n          const existing = await Storage.get(ctx.storage, [\n            \"email\",\n            provider.email,\n            \"password\",\n          ])\n          if (!existing) return c.redirect(provider.redirect, 302)\n\n          const password = fd.get(\"password\")?.toString()\n          const repeat = fd.get(\"repeat\")?.toString()\n          if (!password)\n            return transition(provider, { type: \"invalid_password\" })\n          if (password !== repeat)\n            return transition(provider, { type: \"password_mismatch\" })\n\n          if (config.validatePassword) {\n            let validationError: string | undefined\n            try {\n              if (typeof config.validatePassword === \"function\") {\n                validationError = await config.validatePassword(password)\n              } else {\n                const res =\n                  await config.validatePassword[\"~standard\"].validate(password)\n\n                if (res.issues?.length) {\n                  throw new Error(\n                    res.issues.map((issue) => issue.message).join(\", \"),\n                  )\n                }\n              }\n            } catch (error) {\n              validationError =\n                error instanceof Error ? error.message : undefined\n            }\n            if (validationError)\n              return transition(provider, {\n                type: \"validation_error\",\n                message: validationError,\n              })\n          }\n\n          await Storage.set(\n            ctx.storage,\n            [\"email\", provider.email, \"password\"],\n            await hasher.hash(password),\n          )\n          const subject = await Storage.get<string>(ctx.storage, [\n            \"email\",\n            provider.email,\n            \"subject\",\n          ])\n          if (subject) await ctx.invalidate(subject)\n\n          return c.redirect(provider.redirect, 302)\n        }\n\n        return transition({ type: \"start\", redirect: provider.redirect })\n      })\n    },\n  }\n}\n\nimport * as jose from \"jose\"\nimport { TextEncoder } from \"node:util\"\n\ninterface HashedPassword {}\n\n/**\n * @internal\n */\nexport function PBKDF2Hasher(opts?: { iterations?: number }): PasswordHasher<{\n  hash: string\n  salt: string\n  iterations: number\n}> {\n  const iterations = opts?.iterations ?? 600000\n  return {\n    async hash(password) {\n      const encoder = new TextEncoder()\n      const bytes = encoder.encode(password)\n      const salt = crypto.getRandomValues(new Uint8Array(16))\n      const keyMaterial = await crypto.subtle.importKey(\n        \"raw\",\n        bytes,\n        \"PBKDF2\",\n        false,\n        [\"deriveBits\"],\n      )\n      const hash = await crypto.subtle.deriveBits(\n        {\n          name: \"PBKDF2\",\n          hash: \"SHA-256\",\n          salt: salt,\n          iterations,\n        },\n        keyMaterial,\n        256,\n      )\n      const hashBase64 = jose.base64url.encode(new Uint8Array(hash))\n      const saltBase64 = jose.base64url.encode(salt)\n      return {\n        hash: hashBase64,\n        salt: saltBase64,\n        iterations,\n      }\n    },\n    async verify(password, compare) {\n      const encoder = new TextEncoder()\n      const passwordBytes = encoder.encode(password)\n      const salt = jose.base64url.decode(compare.salt)\n      const params = {\n        name: \"PBKDF2\",\n        hash: \"SHA-256\",\n        salt,\n        iterations: compare.iterations,\n      }\n      const keyMaterial = await crypto.subtle.importKey(\n        \"raw\",\n        passwordBytes,\n        \"PBKDF2\",\n        false,\n        [\"deriveBits\"],\n      )\n      const hash = await crypto.subtle.deriveBits(params, keyMaterial, 256)\n      const hashBase64 = jose.base64url.encode(new Uint8Array(hash))\n      return hashBase64 === compare.hash\n    },\n  }\n}\nimport { timingSafeEqual, randomBytes, scrypt } from \"node:crypto\"\nimport { getRelativeUrl } from \"../util.js\"\n\n/**\n * @internal\n */\nexport function ScryptHasher(opts?: {\n  N?: number\n  r?: number\n  p?: number\n}): PasswordHasher<{\n  hash: string\n  salt: string\n  N: number\n  r: number\n  p: number\n}> {\n  const N = opts?.N ?? 16384\n  const r = opts?.r ?? 8\n  const p = opts?.p ?? 1\n\n  return {\n    async hash(password) {\n      const salt = randomBytes(16)\n      const keyLength = 32 // 256 bits\n\n      const derivedKey = await new Promise<Buffer>((resolve, reject) => {\n        scrypt(password, salt, keyLength, { N, r, p }, (err, derivedKey) => {\n          if (err) reject(err)\n          else resolve(derivedKey)\n        })\n      })\n\n      const hashBase64 = derivedKey.toString(\"base64\")\n      const saltBase64 = salt.toString(\"base64\")\n\n      return {\n        hash: hashBase64,\n        salt: saltBase64,\n        N,\n        r,\n        p,\n      }\n    },\n\n    async verify(password, compare) {\n      const salt = Buffer.from(compare.salt, \"base64\")\n      const keyLength = 32 // 256 bits\n\n      const derivedKey = await new Promise<Buffer>((resolve, reject) => {\n        scrypt(\n          password,\n          salt,\n          keyLength,\n          { N: compare.N, r: compare.r, p: compare.p },\n          (err, derivedKey) => {\n            if (err) reject(err)\n            else resolve(derivedKey)\n          },\n        )\n      })\n\n      return timingSafeEqual(derivedKey, Buffer.from(compare.hash, \"base64\"))\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/provider.ts",
    "content": "import type { Context, Hono } from \"hono\"\nimport { StorageAdapter } from \"../storage/storage.js\"\n\nexport type ProviderRoute = Hono\n\nexport interface Provider<Properties = any> {\n  type: string\n  init: (route: ProviderRoute, options: ProviderOptions<Properties>) => void\n  client?: (input: {\n    clientID: string\n    clientSecret: string\n    params: Record<string, string>\n  }) => Promise<Properties>\n}\n\nexport interface ProviderOptions<Properties> {\n  name: string\n  success: (\n    ctx: Context,\n    properties: Properties,\n    opts?: {\n      invalidate?: (subject: string) => Promise<void>\n    },\n  ) => Promise<Response>\n  forward: (ctx: Context, response: Response) => Response\n  set: <T>(ctx: Context, key: string, maxAge: number, value: T) => Promise<void>\n  get: <T>(ctx: Context, key: string) => Promise<T>\n  unset: (ctx: Context, key: string) => Promise<void>\n  invalidate: (subject: string) => Promise<void>\n  storage: StorageAdapter\n}\nexport class ProviderError extends Error {}\nexport class ProviderUnknownError extends ProviderError {}\n"
  },
  {
    "path": "packages/openauth/src/provider/slack.ts",
    "content": "/**\n * Use this provider to authenticate with Slack.\n *\n * ```ts {5-10}\n * import { SlackProvider } from \"@openauthjs/openauth/provider/slack\"\n *\n * export default issuer({\n *   providers: {\n *     slack: SlackProvider({\n *       team: \"T1234567890\",\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\",\n *       scopes: [\"openid\", \"email\", \"profile\"]\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface SlackConfig extends Oauth2WrappedConfig {\n  /**\n   * The workspace the user is intending to authenticate.\n   *\n   * If that workspace has been previously authenticated, the user will be signed in directly,\n   * bypassing the consent screen.\n   */\n  team: string\n  /**\n   * The scopes to request from the user.\n   *\n   * | Scope | Description |\n   * |-|-|\n   * | `email` | Grants permission to access the user's email address. |\n   * | `profile` | Grants permission to access the user's profile information. |\n   * | `openid` | Grants permission to use OpenID Connect to verify the user's identity. |\n   */\n  scopes: (\"email\" | \"profile\" | \"openid\")[]\n}\n\n/**\n * Creates a [Slack OAuth2 provider](https://api.slack.com/authentication/sign-in-with-slack).\n *\n * @param {SlackConfig} config - The config for the provider.\n * @example\n * ```ts\n * SlackProvider({\n *   team: \"T1234567890\",\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\",\n *   scopes: [\"openid\", \"email\", \"profile\"]\n * })\n * ```\n */\nexport function SlackProvider(config: SlackConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"slack\",\n    endpoint: {\n      authorization: \"https://slack.com/openid/connect/authorize\",\n      token: \"https://slack.com/api/openid.connect.token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/spotify.ts",
    "content": "/**\n * Use this provider to authenticate with Spotify.\n *\n * ```ts {5-8}\n * import { SpotifyProvider } from \"@openauthjs/openauth/provider/spotify\"\n *\n * export default issuer({\n *   providers: {\n *     spotify: SpotifyProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, type Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface SpotifyConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a Spotify OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * SpotifyProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function SpotifyProvider(config: SpotifyConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"spotify\",\n    endpoint: {\n      authorization: \"https://accounts.spotify.com/authorize\",\n      token: \"https://accounts.spotify.com/api/token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/twitch.ts",
    "content": "/**\n * Use this provider to authenticate with Twitch.\n *\n * ```ts {5-8}\n * import { TwitchProvider } from \"@openauthjs/openauth/provider/twitch\"\n *\n * export default issuer({\n *   providers: {\n *     twitch: TwitchProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface TwitchConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a Twitch OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * TwitchProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function TwitchProvider(config: TwitchConfig) {\n  return Oauth2Provider({\n    type: \"twitch\",\n    ...config,\n    endpoint: {\n      authorization: \"https://id.twitch.tv/oauth2/authorize\",\n      token: \"https://id.twitch.tv/oauth2/token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/x.ts",
    "content": "/**\n * Use this provider to authenticate with X.com.\n *\n * ```ts {5-8}\n * import { XProvider } from \"@openauthjs/openauth/provider/x\"\n *\n * export default issuer({\n *   providers: {\n *     x: XProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface XProviderConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a X.com OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * XProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function XProvider(config: XProviderConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"x\",\n    endpoint: {\n      authorization: \"https://twitter.com/i/oauth2/authorize\",\n      token: \"https://api.x.com/2/oauth2/token\",\n    },\n    pkce: true,\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/provider/yahoo.ts",
    "content": "/**\n * Use this provider to authenticate with Yahoo.\n *\n * ```ts {5-8}\n * import { YahooProvider } from \"@openauthjs/openauth/provider/yahoo\"\n *\n * export default issuer({\n *   providers: {\n *     yahoo: YahooProvider({\n *       clientID: \"1234567890\",\n *       clientSecret: \"0987654321\"\n *     })\n *   }\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Oauth2Provider, Oauth2WrappedConfig } from \"./oauth2.js\"\n\nexport interface YahooConfig extends Oauth2WrappedConfig {}\n\n/**\n * Create a Yahoo OAuth2 provider.\n *\n * @param config - The config for the provider.\n * @example\n * ```ts\n * YahooProvider({\n *   clientID: \"1234567890\",\n *   clientSecret: \"0987654321\"\n * })\n * ```\n */\nexport function YahooProvider(config: YahooConfig) {\n  return Oauth2Provider({\n    ...config,\n    type: \"yahoo\",\n    endpoint: {\n      authorization: \"https://api.login.yahoo.com/oauth2/request_auth\",\n      token: \"https://api.login.yahoo.com/oauth2/get_token\",\n    },\n  })\n}\n"
  },
  {
    "path": "packages/openauth/src/random.ts",
    "content": "import { timingSafeEqual } from \"node:crypto\"\n\nexport function generateUnbiasedDigits(length: number): string {\n  const result: number[] = []\n  while (result.length < length) {\n    const buffer = crypto.getRandomValues(new Uint8Array(length * 2))\n    for (const byte of buffer) {\n      if (byte < 250 && result.length < length) {\n        result.push(byte % 10)\n      }\n    }\n  }\n  return result.join(\"\")\n}\n\nexport function timingSafeCompare(a: string, b: string): boolean {\n  if (typeof a !== \"string\" || typeof b !== \"string\") {\n    return false\n  }\n  if (a.length !== b.length) {\n    return false\n  }\n  return timingSafeEqual(Buffer.from(a), Buffer.from(b))\n}\n"
  },
  {
    "path": "packages/openauth/src/storage/aws.ts",
    "content": "import { AwsClient } from \"aws4fetch\"\n\ninterface EC2Credentials {\n  AccessKeyId: string\n  SecretAccessKey: string\n  Token: string\n  Expiration: string\n  Type: string\n}\n\nlet cachedCredentials: EC2Credentials | null = null\n\nasync function getCredentials(url: string): Promise<EC2Credentials> {\n  if (cachedCredentials) {\n    const currentTime = new Date()\n    const fiveMinutesFromNow = new Date(currentTime.getTime() + 5 * 60000)\n    const expirationTime = new Date(cachedCredentials.Expiration)\n    if (expirationTime > fiveMinutesFromNow) {\n      return cachedCredentials\n    }\n  }\n\n  const credentials = (await fetch(url).then((res) =>\n    res.json(),\n  )) as EC2Credentials\n  cachedCredentials = credentials\n  return credentials\n}\n\nexport async function client(): Promise<AwsClient> {\n  if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {\n    return new AwsClient({\n      accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n      sessionToken: process.env.AWS_SESSION_TOKEN,\n      region: process.env.AWS_REGION,\n    })\n  }\n\n  if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {\n    const credentials = await getCredentials(\n      \"http://169.254.170.2\" +\n        process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,\n    )\n    return new AwsClient({\n      accessKeyId: credentials.AccessKeyId,\n      secretAccessKey: credentials.SecretAccessKey,\n      sessionToken: credentials.Token,\n      region: process.env.AWS_REGION,\n    })\n  }\n\n  throw new Error(\"No AWS credentials found\")\n}\n\nexport type AwsOptions = Exclude<\n  Parameters<AwsClient[\"fetch\"]>[1],\n  null | undefined\n>[\"aws\"]\n"
  },
  {
    "path": "packages/openauth/src/storage/cloudflare.ts",
    "content": "/**\n * Configure OpenAuth to use [Cloudflare KV](https://developers.cloudflare.com/kv/) as a\n * storage adapter.\n *\n * ```ts\n * import { CloudflareStorage } from \"@openauthjs/openauth/storage/cloudflare\"\n *\n * const storage = CloudflareStorage({\n *   namespace: \"my-namespace\"\n * })\n *\n *\n * export default issuer({\n *   storage,\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\nimport type { KVNamespace } from \"@cloudflare/workers-types\"\nimport { joinKey, splitKey, StorageAdapter } from \"./storage.js\"\n\n/**\n * Configure the Cloudflare KV store that's created.\n */\nexport interface CloudflareStorageOptions {\n  namespace: KVNamespace\n}\n/**\n * Creates a Cloudflare KV store.\n * @param options - The config for the adapter.\n */\nexport function CloudflareStorage(\n  options: CloudflareStorageOptions,\n): StorageAdapter {\n  return {\n    async get(key: string[]) {\n      const value = await options.namespace.get(joinKey(key), \"json\")\n      if (!value) return\n      return value as Record<string, any>\n    },\n\n    async set(key: string[], value: any, expiry?: Date) {\n      await options.namespace.put(joinKey(key), JSON.stringify(value), {\n        expirationTtl: expiry\n          ? Math.max(Math.floor((expiry.getTime() - Date.now()) / 1000), 60)\n          : undefined,\n      })\n    },\n\n    async remove(key: string[]) {\n      await options.namespace.delete(joinKey(key))\n    },\n\n    async *scan(prefix: string[]) {\n      let cursor: string | undefined\n      while (true) {\n        const result = await options.namespace.list({\n          prefix: joinKey([...prefix, \"\"]),\n          cursor,\n        })\n\n        for (const key of result.keys) {\n          const value = await options.namespace.get(key.name, \"json\")\n          if (value !== null) {\n            yield [splitKey(key.name), value]\n          }\n        }\n        if (result.list_complete) {\n          break\n        }\n        cursor = result.cursor\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/storage/dynamo.ts",
    "content": "/**\n * Configure OpenAuth to use [DynamoDB](https://aws.amazon.com/dynamodb/) as a storage adapter.\n *\n * ```ts\n * import { DynamoStorage } from \"@openauthjs/openauth/storage/dynamo\"\n *\n * const storage = DynamoStorage({\n *   table: \"my-table\",\n *   pk: \"pk\",\n *   sk: \"sk\"\n * })\n *\n * export default issuer({\n *   storage,\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport { client } from \"./aws.js\"\nimport { joinKey, StorageAdapter } from \"./storage.js\"\n\n/**\n * Configure the DynamoDB table that's created.\n *\n * @example\n * ```ts\n * {\n *   table: \"my-table\",\n *   pk: \"pk\",\n *   sk: \"sk\"\n * }\n * ```\n */\nexport interface DynamoStorageOptions {\n  /**\n   * The name of the DynamoDB table.\n   */\n  table: string\n  /**\n   * The primary key column name.\n   * @default \"pk\"\n   */\n  pk?: string\n  /**\n   * The sort key column name.\n   * @default \"sk\"\n   */\n  sk?: string\n  /**\n   * Endpoint URL for the DynamoDB service. Useful for local testing.\n   * @default \"https://dynamodb.{region}.amazonaws.com\"\n   */\n  endpoint?: string\n  /**\n   * The name of the time to live attribute.\n   * @default \"expiry\"\n   */\n  ttl?: string\n}\n\n/**\n * Creates a DynamoDB store.\n * @param options - The config for the adapter.\n */\nexport function DynamoStorage(options: DynamoStorageOptions): StorageAdapter {\n  const pk = options.pk || \"pk\"\n  const sk = options.sk || \"sk\"\n  const ttl = options.ttl || \"expiry\"\n  const tableName = options.table\n\n  function parseKey(key: string[]) {\n    if (key.length === 2) {\n      return {\n        pk: key[0],\n        sk: key[1],\n      }\n    }\n    return {\n      pk: joinKey(key.slice(0, 2)),\n      sk: joinKey(key.slice(2)),\n    }\n  }\n\n  async function dynamo(action: string, payload: any) {\n    const c = await client()\n    const endpoint =\n      options.endpoint || `https://dynamodb.${c.region}.amazonaws.com`\n    const response = await c.fetch(endpoint, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/x-amz-json-1.0\",\n        \"X-Amz-Target\": `DynamoDB_20120810.${action}`,\n      },\n      body: JSON.stringify(payload),\n    })\n\n    if (!response.ok) {\n      throw new Error(`DynamoDB request failed: ${response.statusText}`)\n    }\n\n    return response.json() as Promise<any>\n  }\n\n  return {\n    async get(key: string[]) {\n      const { pk: keyPk, sk: keySk } = parseKey(key)\n      const params = {\n        TableName: tableName,\n        Key: {\n          [pk]: { S: keyPk },\n          [sk]: { S: keySk },\n        },\n      }\n      const result = await dynamo(\"GetItem\", params)\n      if (!result.Item) return\n      if (result.Item[ttl] && result.Item[ttl].N < Date.now() / 1000) {\n        return\n      }\n      return JSON.parse(result.Item.value.S)\n    },\n\n    async set(key: string[], value: any, expiry?: Date) {\n      const parsed = parseKey(key)\n      const params = {\n        TableName: tableName,\n        Item: {\n          [pk]: { S: parsed.pk },\n          [sk]: { S: parsed.sk },\n          ...(expiry\n            ? {\n                [ttl]: { N: Math.floor(expiry.getTime() / 1000).toString() },\n              }\n            : {}),\n          value: { S: JSON.stringify(value) },\n        },\n      }\n      await dynamo(\"PutItem\", params)\n    },\n\n    async remove(key: string[]) {\n      const { pk: keyPk, sk: keySk } = parseKey(key)\n      const params = {\n        TableName: tableName,\n        Key: {\n          [pk]: { S: keyPk },\n          [sk]: { S: keySk },\n        },\n      }\n\n      await dynamo(\"DeleteItem\", params)\n    },\n\n    async *scan(prefix: string[]) {\n      const prefixPk =\n        prefix.length >= 2 ? joinKey(prefix.slice(0, 2)) : prefix[0]\n      const prefixSk = prefix.length > 2 ? joinKey(prefix.slice(2)) : \"\"\n      let lastEvaluatedKey = undefined\n      const now = Date.now() / 1000\n      while (true) {\n        const params = {\n          TableName: tableName,\n          ExclusiveStartKey: lastEvaluatedKey,\n          KeyConditionExpression: prefixSk\n            ? `#pk = :pk AND begins_with(#sk, :sk)`\n            : `#pk = :pk`,\n          ExpressionAttributeNames: {\n            \"#pk\": pk,\n            ...(prefixSk && { \"#sk\": sk }),\n          },\n          ExpressionAttributeValues: {\n            \":pk\": { S: prefixPk },\n            ...(prefixSk && { \":sk\": { S: prefixSk } }),\n          },\n        }\n\n        const result = await dynamo(\"Query\", params)\n\n        for (const item of result.Items || []) {\n          if (item[ttl] && item[ttl].N < now) {\n            continue\n          }\n          yield [[item[pk].S, item[sk].S], JSON.parse(item.value.S)]\n        }\n\n        if (!result.LastEvaluatedKey) break\n        lastEvaluatedKey = result.LastEvaluatedKey\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/storage/memory.ts",
    "content": "/**\n * Configure OpenAuth to use a simple in-memory store.\n *\n * :::caution\n * This is not meant to be used in production.\n * :::\n *\n * This is useful for testing and development. It's not meant to be used in production.\n *\n * ```ts\n * import { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\n *\n * const storage = MemoryStorage()\n *\n * export default issuer({\n *   storage,\n *   // ...\n * })\n * ```\n *\n * Optionally, you can persist the store to a file.\n *\n * ```ts\n * MemoryStorage({\n *   persist: \"./persist.json\"\n * })\n * ```\n *\n * @packageDocumentation\n */\nimport { joinKey, splitKey, StorageAdapter } from \"./storage.js\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { writeFile } from \"node:fs/promises\"\n\n/**\n * Configure the memory store.\n */\nexport interface MemoryStorageOptions {\n  /**\n   * Optionally, backup the store to a file. So it'll be persisted when the issuer restarts.\n   *\n   * @example\n   * ```ts\n   * {\n   *   persist: \"./persist.json\"\n   * }\n   * ```\n   */\n  persist?: string\n}\nexport function MemoryStorage(input?: MemoryStorageOptions): StorageAdapter {\n  const store = [] as [\n    string,\n    { value: Record<string, any>; expiry?: number },\n  ][]\n\n  if (input?.persist) {\n    if (existsSync(input.persist)) {\n      const file = readFileSync(input?.persist)\n      store.push(...JSON.parse(file.toString()))\n    }\n  }\n\n  async function save() {\n    if (!input?.persist) return\n    const file = JSON.stringify(store)\n    await writeFile(input.persist, file)\n  }\n\n  function search(key: string) {\n    let left = 0\n    let right = store.length - 1\n    while (left <= right) {\n      const mid = Math.floor((left + right) / 2)\n      const comparison = key.localeCompare(store[mid][0])\n\n      if (comparison === 0) {\n        return { found: true, index: mid }\n      } else if (comparison < 0) {\n        right = mid - 1\n      } else {\n        left = mid + 1\n      }\n    }\n    return { found: false, index: left }\n  }\n  return {\n    async get(key: string[]) {\n      const match = search(joinKey(key))\n      if (!match.found) return undefined\n      const entry = store[match.index][1]\n      if (entry.expiry && Date.now() >= entry.expiry) {\n        store.splice(match.index, 1)\n        await save()\n        return undefined\n      }\n      return entry.value\n    },\n    async set(key: string[], value: any, expiry?: Date) {\n      const joined = joinKey(key)\n      const match = search(joined)\n      // Handle both Date objects and TTL numbers while maintaining Date type in signature\n      const entry = [\n        joined,\n        {\n          value,\n          expiry: expiry ? expiry.getTime() : expiry,\n        },\n      ] as (typeof store)[number]\n      if (!match.found) {\n        store.splice(match.index, 0, entry)\n      } else {\n        store[match.index] = entry\n      }\n      await save()\n    },\n    async remove(key: string[]) {\n      const joined = joinKey(key)\n      const match = search(joined)\n      if (match.found) {\n        store.splice(match.index, 1)\n        await save()\n      }\n    },\n    async *scan(prefix: string[]) {\n      const now = Date.now()\n      const prefixStr = joinKey(prefix)\n      for (const [key, entry] of store) {\n        if (!key.startsWith(prefixStr)) continue\n        if (entry.expiry && now >= entry.expiry) continue\n        yield [splitKey(key), entry.value]\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/storage/storage.ts",
    "content": "export interface StorageAdapter {\n  get(key: string[]): Promise<Record<string, any> | undefined>\n  remove(key: string[]): Promise<void>\n  set(key: string[], value: any, expiry?: Date): Promise<void>\n  scan(prefix: string[]): AsyncIterable<[string[], any]>\n}\n\nconst SEPERATOR = String.fromCharCode(0x1f)\n\nexport function joinKey(key: string[]) {\n  return key.join(SEPERATOR)\n}\n\nexport function splitKey(key: string) {\n  return key.split(SEPERATOR)\n}\n\nexport namespace Storage {\n  function encode(key: string[]) {\n    return key.map((k) => k.replaceAll(SEPERATOR, \"\"))\n  }\n  export function get<T>(adapter: StorageAdapter, key: string[]) {\n    return adapter.get(encode(key)) as Promise<T | null>\n  }\n\n  export function set(\n    adapter: StorageAdapter,\n    key: string[],\n    value: any,\n    ttl?: number,\n  ) {\n    const expiry = ttl ? new Date(Date.now() + ttl * 1000) : undefined\n    return adapter.set(encode(key), value, expiry)\n  }\n\n  export function remove(adapter: StorageAdapter, key: string[]) {\n    return adapter.remove(encode(key))\n  }\n\n  export function scan<T>(\n    adapter: StorageAdapter,\n    key: string[],\n  ): AsyncIterable<[string[], T]> {\n    return adapter.scan(encode(key))\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/subject.ts",
    "content": "/**\n * Subjects are what the access token generated at the end of the auth flow will map to. Under\n * the hood, the access token is a JWT that contains this data.\n *\n * #### Define subjects\n *\n * ```ts title=\"subjects.ts\"\n * import { object, string } from \"valibot\"\n *\n * const subjects = createSubjects({\n *   user: object({\n *     userID: string()\n *   })\n * })\n * ```\n *\n * We are using [valibot](https://github.com/fabian-hiller/valibot) here. You can use any\n * validation library that's following the\n * [standard-schema specification](https://github.com/standard-schema/standard-schema).\n *\n * :::tip\n * You typically want to place subjects in its own file so it can be imported by all of your apps.\n * :::\n *\n * You can start with one subject. Later you can add more for different types of users.\n *\n * #### Set the subjects\n *\n * Then you can pass it to the `issuer`.\n *\n * ```ts title=\"issuer.ts\"\n * import { subjects } from \"./subjects\"\n *\n * const app = issuer({\n *   providers: { ... },\n *   subjects,\n *   // ...\n * })\n * ```\n *\n * #### Add the subject payload\n *\n * When your user completes the flow, you can add the subject payload in the `success` callback.\n *\n * ```ts title=\"issuer.ts\"\n * const 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 = ... // lookup user or create them\n *     }\n *     return ctx.subject(\"user\", {\n *       userID\n *     })\n *   },\n *   // ...\n * })\n * ```\n *\n * Here we are looking up the userID from our database and adding it to the subject payload.\n *\n * :::caution\n * You should only store properties that won't change for the lifetime of the user.\n * :::\n *\n * Since these will be stored in the access token, you should avoid storing information\n * that'll change often. For example, if you store the user's username, you'll need to\n * revoke the access token when the user changes their username.\n *\n * #### Decode the subject\n *\n * Now when your user logs in, you can use the OpenAuth client to decode the subject. For\n * example, in our SSR app we can do the following.\n *\n * ```ts title=\"app/page.tsx\"\n * import { subjects } from \"../subjects\"\n *\n * const verified = await client.verify(subjects, cookies.get(\"access_token\")!)\n * console.log(verified.subject.properties.userID)\n * ```\n *\n * All this is typesafe based on the shape of the subjects you defined.\n *\n * @packageDocumentation\n */\nimport type { v1 } from \"@standard-schema/spec\"\nimport { Prettify } from \"./util.js\"\n\n/**\n * Subject schema is a map of types that are used to define the subjects.\n */\nexport type SubjectSchema = Record<string, v1.StandardSchema>\n\n/** @internal */\nexport type SubjectPayload<T extends SubjectSchema> = Prettify<\n  {\n    [type in keyof T & string]: {\n      type: type\n      properties: v1.InferOutput<T[type]>\n    }\n  }[keyof T & string]\n>\n\n/**\n * Create a subject schema.\n *\n * @example\n * ```ts\n * const subjects = createSubjects({\n *   user: object({\n *     userID: string()\n *   }),\n *   admin: object({\n *     workspaceID: string()\n *   })\n * })\n * ```\n *\n * This is using [valibot](https://github.com/fabian-hiller/valibot) to define the shape of the\n * subjects. You can use any validation library that's following the\n * [standard-schema specification](https://github.com/standard-schema/standard-schema).\n */\nexport function createSubjects<Schema extends SubjectSchema = {}>(\n  types: Schema,\n): Schema {\n  return { ...types }\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/base.tsx",
    "content": "import { PropsWithChildren } from \"hono/jsx\"\nimport css from \"./ui.css\" assert { type: \"text\" }\nimport { getTheme } from \"./theme.js\"\n\nexport function Layout(\n  props: PropsWithChildren<{\n    size?: \"small\"\n  }>,\n) {\n  const theme = getTheme()\n  function get(key: \"primary\" | \"background\" | \"logo\", mode: \"light\" | \"dark\") {\n    if (!theme) return\n    if (!theme[key]) return\n    if (typeof theme[key] === \"string\") return theme[key]\n\n    return theme[key][mode] as string | undefined\n  }\n\n  const radius = (() => {\n    if (theme?.radius === \"none\") return \"0\"\n    if (theme?.radius === \"sm\") return \"1\"\n    if (theme?.radius === \"md\") return \"1.25\"\n    if (theme?.radius === \"lg\") return \"1.5\"\n    if (theme?.radius === \"full\") return \"1000000000001\"\n    return \"1\"\n  })()\n\n  const hasLogo = get(\"logo\", \"light\") && get(\"logo\", \"dark\")\n\n  return (\n    <html\n      style={{\n        \"--color-background-light\": get(\"background\", \"light\"),\n        \"--color-background-dark\": get(\"background\", \"dark\"),\n        \"--color-primary-light\": get(\"primary\", \"light\"),\n        \"--color-primary-dark\": get(\"primary\", \"dark\"),\n        \"--font-family\": theme?.font?.family,\n        \"--font-scale\": theme?.font?.scale,\n        \"--border-radius\": radius,\n      }}\n    >\n      <head>\n        <title>{theme?.title || \"OpenAuthJS\"}</title>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        {theme?.favicon ? (\n          <link rel=\"icon\" href={theme?.favicon} />\n        ) : (\n          <>\n            <link\n              rel=\"icon\"\n              href=\"https://openauth.js.org/favicon.ico\"\n              sizes=\"48x48\"\n            />\n            <link\n              rel=\"icon\"\n              href=\"https://openauth.js.org/favicon.svg\"\n              media=\"(prefers-color-scheme: light)\"\n            />\n            <link\n              rel=\"icon\"\n              href=\"https://openauth.js.org/favicon-dark.svg\"\n              media=\"(prefers-color-scheme: dark)\"\n            />\n            <link\n              rel=\"shortcut icon\"\n              href=\"https://openauth.js.org/favicon.svg\"\n              type=\"image/svg+xml\"\n            />\n          </>\n        )}\n        <style dangerouslySetInnerHTML={{ __html: css }} />\n        {theme?.css && (\n          <style dangerouslySetInnerHTML={{ __html: theme.css }} />\n        )}\n      </head>\n      <body>\n        <div data-component=\"root\">\n          <div data-component=\"center\" data-size={props.size}>\n            {hasLogo ? (\n              <>\n                <img\n                  data-component=\"logo\"\n                  src={get(\"logo\", \"light\")}\n                  data-mode=\"light\"\n                />\n                <img\n                  data-component=\"logo\"\n                  src={get(\"logo\", \"dark\")}\n                  data-mode=\"dark\"\n                />\n              </>\n            ) : (\n              ICON_OPENAUTH\n            )}\n            {props.children}\n          </div>\n        </div>\n      </body>\n    </html>\n  )\n}\n\nconst ICON_OPENAUTH = (\n  <svg\n    data-component=\"logo-default\"\n    width=\"51\"\n    height=\"51\"\n    viewBox=\"0 0 51 51\"\n    fill=\"none\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n  >\n    <path\n      d=\"M0 50.2303V0.12854H50.1017V50.2303H0ZM3.08002 11.8326H11.7041V3.20856H3.08002V11.8326ZM14.8526 11.8326H23.4766V3.20856H14.8526V11.8326ZM26.5566 11.8326H35.1807V3.20856H26.5566V11.8326ZM38.3292 11.8326H47.0217V3.20856H38.3292V11.8326ZM3.08002 23.6052H11.7041V14.9811H3.08002V23.6052ZM14.8526 23.6052H23.4766V14.9811H14.8526V23.6052ZM26.5566 23.6052H35.1807V14.9811H26.5566V23.6052ZM38.3292 23.6052H47.0217V14.9811H38.3292V23.6052ZM3.08002 35.3092H11.7041V26.6852H3.08002V35.3092ZM14.8526 35.3092H23.4766V26.6852H14.8526V35.3092ZM26.5566 35.3092H35.1807V26.6852H26.5566V35.3092ZM38.3292 35.3092H47.0217V26.6852H38.3292V35.3092ZM3.08002 47.1502H11.7041V38.3893H3.08002V47.1502ZM14.8526 47.1502H23.4766V38.3893H14.8526V47.1502ZM26.5566 47.1502H35.1807V38.3893H26.5566V47.1502ZM38.3292 47.1502H47.0217V38.3893H38.3292V47.1502Z\"\n      fill=\"currentColor\"\n    />\n  </svg>\n)\n"
  },
  {
    "path": "packages/openauth/src/ui/code.tsx",
    "content": "/**\n * Configure the UI that's used by the Code provider.\n *\n * ```ts {1,7-12}\n * import { CodeUI } from \"@openauthjs/openauth/ui/code\"\n * import { CodeProvider } from \"@openauthjs/openauth/provider/code\"\n *\n * export default issuer({\n *   providers: {\n *     code: CodeAdapter(\n *       CodeUI({\n *         copy: {\n *           code_info: \"We'll send a pin code to your email\"\n *         },\n *         sendCode: (claims, code) => console.log(claims.email, code)\n *       })\n *     )\n *   },\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\n/** @jsxImportSource hono/jsx */\n\nimport { CodeProviderOptions } from \"../provider/code.js\"\nimport { UnknownStateError } from \"../error.js\"\nimport { Layout } from \"./base.js\"\nimport { FormAlert } from \"./form.js\"\n\nconst DEFAULT_COPY = {\n  /**\n   * Copy for the email input.\n   */\n  email_placeholder: \"Email\",\n  /**\n   * Error message when the email is invalid.\n   */\n  email_invalid: \"Email address is not valid\",\n  /**\n   * Copy for the continue button.\n   */\n  button_continue: \"Continue\",\n  /**\n   * Copy informing that the pin code will be emailed.\n   */\n  code_info: \"We'll send a pin code to your email.\",\n  /**\n   * Copy for the pin code input.\n   */\n  code_placeholder: \"Code\",\n  /**\n   * Error message when the code is invalid.\n   */\n  code_invalid: \"Invalid code\",\n  /**\n   * Copy for when the code was sent.\n   */\n  code_sent: \"Code sent to \",\n  /**\n   * Copy for when the code was resent.\n   */\n  code_resent: \"Code resent to \",\n  /**\n   * Copy for the link to resend the code.\n   */\n  code_didnt_get: \"Didn't get code?\",\n  /**\n   * Copy for the resend button.\n   */\n  code_resend: \"Resend\",\n}\n\nexport type CodeUICopy = typeof DEFAULT_COPY\n\n/**\n * Configure the password UI.\n */\nexport interface CodeUIOptions {\n  /**\n   * Callback to send the pin code to the user.\n   *\n   * The `claims` object contains the email or phone number of the user. You can send the code\n   * using this.\n   *\n   * @example\n   * ```ts\n   * async (claims, code) => {\n   *   // Send the code via the claim\n   * }\n   * ```\n   */\n  sendCode: (claims: Record<string, string>, code: string) => Promise<void>\n  /**\n   * Custom copy for the UI.\n   */\n  copy?: Partial<CodeUICopy>\n  /**\n   * The mode to use for the input.\n   * @default \"email\"\n   */\n  mode?: \"email\" | \"phone\"\n}\n\n/**\n * Creates a UI for the Code provider flow.\n * @param props - Configure the UI.\n */\nexport function CodeUI(props: CodeUIOptions): CodeProviderOptions {\n  const copy = {\n    ...DEFAULT_COPY,\n    ...props.copy,\n  }\n\n  const mode = props.mode ?? \"email\"\n\n  return {\n    sendCode: props.sendCode,\n    length: 6,\n    request: async (_req, state, _form, error): Promise<Response> => {\n      if (state.type === \"start\") {\n        const jsx = (\n          <Layout>\n            <form data-component=\"form\" method=\"post\">\n              {error?.type === \"invalid_claim\" && (\n                <FormAlert message={copy.email_invalid} />\n              )}\n              <input type=\"hidden\" name=\"action\" value=\"request\" />\n              <input\n                data-component=\"input\"\n                autofocus\n                type={mode === \"email\" ? \"email\" : \"tel\"}\n                name={mode === \"email\" ? \"email\" : \"phone\"}\n                inputmode={mode === \"email\" ? \"email\" : \"numeric\"}\n                required\n                placeholder={copy.email_placeholder}\n              />\n              <button data-component=\"button\">{copy.button_continue}</button>\n            </form>\n            <p data-component=\"form-footer\">{copy.code_info}</p>\n          </Layout>\n        )\n        return new Response(jsx.toString(), {\n          headers: {\n            \"Content-Type\": \"text/html\",\n          },\n        })\n      }\n\n      if (state.type === \"code\") {\n        const jsx = (\n          <Layout>\n            <form data-component=\"form\" class=\"form\" method=\"post\">\n              {error?.type === \"invalid_code\" && (\n                <FormAlert message={copy.code_invalid} />\n              )}\n              {state.type === \"code\" && (\n                <FormAlert\n                  message={\n                    (state.resend ? copy.code_resent : copy.code_sent) +\n                    state.claims.email\n                  }\n                  color=\"success\"\n                />\n              )}\n              <input type=\"hidden\" name=\"action\" value=\"verify\" />\n              <input\n                data-component=\"input\"\n                autofocus\n                minLength={6}\n                maxLength={6}\n                type=\"text\"\n                name=\"code\"\n                required\n                inputmode=\"numeric\"\n                autocomplete=\"one-time-code\"\n                placeholder={copy.code_placeholder}\n              />\n              <button data-component=\"button\">{copy.button_continue}</button>\n            </form>\n            <form method=\"post\">\n              {Object.entries(state.claims).map(([key, value]) => (\n                <input\n                  key={key}\n                  type=\"hidden\"\n                  name={key}\n                  value={value}\n                  className=\"hidden\"\n                />\n              ))}\n              <input type=\"hidden\" name=\"action\" value=\"request\" />\n              <div data-component=\"form-footer\">\n                <span>\n                  {copy.code_didnt_get}{\" \"}\n                  <button data-component=\"link\">{copy.code_resend}</button>\n                </span>\n              </div>\n            </form>\n          </Layout>\n        )\n        return new Response(jsx.toString(), {\n          headers: {\n            \"Content-Type\": \"text/html\",\n          },\n        })\n      }\n\n      throw new UnknownStateError()\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/form.tsx",
    "content": "/** @jsxImportSource hono/jsx */\n\nexport function FormAlert(props: {\n  message?: string\n  color?: \"danger\" | \"success\"\n}) {\n  return (\n    <div data-component=\"form-alert\" data-color={props.color}>\n      <svg\n        data-slot=\"icon-success\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        fill=\"none\"\n        viewBox=\"0 0 24 24\"\n        stroke-width=\"1.5\"\n        stroke=\"currentColor\"\n      >\n        <path\n          stroke-linecap=\"round\"\n          stroke-linejoin=\"round\"\n          d=\"M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\"\n        />\n      </svg>\n      <svg\n        data-slot=\"icon-danger\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        fill=\"none\"\n        viewBox=\"0 0 24 24\"\n        stroke-width=\"1.5\"\n        stroke=\"currentColor\"\n      >\n        <path\n          stroke-linecap=\"round\"\n          stroke-linejoin=\"round\"\n          d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n        />\n      </svg>\n      <span data-slot=\"message\">{props.message}</span>\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/icon.tsx",
    "content": "/** @jsxImportSource hono/jsx */\n\nexport const ICON_GITHUB = (\n  <svg\n    viewBox=\"0 0 256 250\"\n    width=\"256\"\n    height=\"250\"\n    fill=\"currentColor\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    preserveAspectRatio=\"xMidYMid\"\n  >\n    <path d=\"M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z\" />\n  </svg>\n)\n\nexport const ICON_GOOGLE = (\n  <svg\n    width=\"256\"\n    height=\"262\"\n    viewBox=\"0 0 256 262\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    preserveAspectRatio=\"xMidYMid\"\n  >\n    <path\n      d=\"M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027\"\n      fill=\"#4285F4\"\n    />\n    <path\n      d=\"M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1\"\n      fill=\"#34A853\"\n    />\n    <path\n      d=\"M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782\"\n      fill=\"#FBBC05\"\n    />\n    <path\n      d=\"M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251\"\n      fill=\"#EB4335\"\n    />\n  </svg>\n)\n\nexport const ICON_EMAIL = (\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke-width=\"1.5\"\n    stroke=\"currentColor\"\n    class=\"size-6\"\n  >\n    <path\n      stroke-linecap=\"round\"\n      stroke-linejoin=\"round\"\n      d=\"M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75\"\n    />\n  </svg>\n)\n\nexport const ICON_SLACK = (\n  <svg\n    width=\"24\"\n    height=\"24\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n  >\n    <g>\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M8.79948 0C7.47279 0.000978593 6.39909 1.07547 6.40007 2.39951C6.39909 3.72355 7.47377 4.79804 8.80046 4.79902H11.2009V2.40049C11.2018 1.07645 10.1271 0.00195719 8.79948 0ZM8.79948 6.4H2.40039C1.07371 6.40098 -0.000977873 7.47547 2.67973e-06 8.79951C-0.00195842 10.1235 1.07273 11.198 2.39941 11.2H8.79948C10.1262 11.199 11.2009 10.1245 11.1999 8.80049C11.2009 7.47547 10.1262 6.40098 8.79948 6.4Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M24.0007 8.79951C24.0016 7.47547 22.9269 6.40098 21.6003 6.4C20.2736 6.40098 19.1989 7.47547 19.1999 8.79951V11.2H21.6003C22.9269 11.199 24.0016 10.1245 24.0007 8.79951ZM17.6006 8.79951V2.39951C17.6016 1.07645 16.5279 0.00195719 15.2012 0C13.8745 0.000978593 12.7998 1.07547 12.8008 2.39951V8.79951C12.7988 10.1235 13.8735 11.198 15.2002 11.2C16.5269 11.199 17.6016 10.1245 17.6006 8.79951Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M15.1992 23.9998C16.5259 23.9988 17.6006 22.9243 17.5996 21.6003C17.6006 20.2763 16.5259 19.2018 15.1992 19.2008H12.7988V21.6003C12.7978 22.9234 13.8725 23.9978 15.1992 23.9998ZM15.1992 17.5988H21.5993C22.926 17.5978 24.0007 16.5234 23.9997 15.1993C24.0016 13.8753 22.927 12.8008 21.6003 12.7988H15.2002C13.8735 12.7998 12.7988 13.8743 12.7998 15.1983C12.7988 16.5234 13.8725 17.5978 15.1992 17.5988Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M0 15.1993C-0.000979882 16.5234 1.07371 17.5978 2.40039 17.5988C3.72708 17.5978 4.80177 16.5234 4.80079 15.1993V12.7998H2.40039C1.07371 12.8008 -0.000979882 13.8753 0 15.1993ZM6.40007 15.1993V21.5993C6.3981 22.9234 7.47279 23.9978 8.79948 23.9998C10.1262 23.9988 11.2009 22.9243 11.1999 21.6003V15.2013C11.2018 13.8772 10.1271 12.8027 8.80046 12.8008C7.47279 12.8008 6.39909 13.8753 6.40007 15.1993Z\"\n        fill=\"currentColor\"\n      />\n    </g>\n  </svg>\n)\n"
  },
  {
    "path": "packages/openauth/src/ui/password.tsx",
    "content": "/**\n * Configure the UI that's used by the Password provider.\n *\n * ```ts {1,7-12}\n * import { PasswordUI } from \"@openauthjs/openauth/ui/password\"\n * import { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n *\n * export default issuer({\n *   providers: {\n *     password: PasswordAdapter(\n *       PasswordUI({\n *         copy: {\n *           error_email_taken: \"This email is already taken.\"\n *         },\n *         sendCode: (email, code) => console.log(email, code)\n *       })\n *     )\n *   },\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\n/** @jsxImportSource hono/jsx */\n\nimport {\n  PasswordChangeError,\n  PasswordConfig,\n  PasswordLoginError,\n  PasswordRegisterError,\n} from \"../provider/password.js\"\nimport { Layout } from \"./base.js\"\nimport \"./form.js\"\nimport { FormAlert } from \"./form.js\"\n\nconst DEFAULT_COPY = {\n  /**\n   * Error message when email is already taken.\n   */\n  error_email_taken: \"There is already an account with this email.\",\n  /**\n   * Error message when the confirmation code is incorrect.\n   */\n  error_invalid_code: \"Code is incorrect.\",\n  /**\n   * Error message when the email is invalid.\n   */\n  error_invalid_email: \"Email is not valid.\",\n  /**\n   * Error message when the password is incorrect.\n   */\n  error_invalid_password: \"Password is incorrect.\",\n  /**\n   * Error message when the passwords do not match.\n   */\n  error_password_mismatch: \"Passwords do not match.\",\n  /**\n   * Error message when the user enters a password that fails validation.\n   */\n  error_validation_error: \"Password does not meet requirements.\",\n  /**\n   * Title of the register page.\n   */\n  register_title: \"Welcome to the app\",\n  /**\n   * Description of the register page.\n   */\n  register_description: \"Sign in with your email\",\n  /**\n   * Title of the login page.\n   */\n  login_title: \"Welcome to the app\",\n  /**\n   * Description of the login page.\n   */\n  login_description: \"Sign in with your email\",\n  /**\n   * Copy for the register button.\n   */\n  register: \"Register\",\n  /**\n   * Copy for the register link.\n   */\n  register_prompt: \"Don't have an account?\",\n  /**\n   * Copy for the login link.\n   */\n  login_prompt: \"Already have an account?\",\n  /**\n   * Copy for the login button.\n   */\n  login: \"Login\",\n  /**\n   * Copy for the forgot password link.\n   */\n  change_prompt: \"Forgot password?\",\n  /**\n   * Copy for the resend code button.\n   */\n  code_resend: \"Resend code\",\n  /**\n   * Copy for the \"Back to\" link.\n   */\n  code_return: \"Back to\",\n  /**\n   * Copy for the logo.\n   * @internal\n   */\n  logo: \"A\",\n  /**\n   * Copy for the email input.\n   */\n  input_email: \"Email\",\n  /**\n   * Copy for the password input.\n   */\n  input_password: \"Password\",\n  /**\n   * Copy for the code input.\n   */\n  input_code: \"Code\",\n  /**\n   * Copy for the repeat password input.\n   */\n  input_repeat: \"Repeat password\",\n  /**\n   * Copy for the continue button.\n   */\n  button_continue: \"Continue\",\n} satisfies {\n  [key in `error_${\n    | PasswordLoginError[\"type\"]\n    | PasswordRegisterError[\"type\"]\n    | PasswordChangeError[\"type\"]}`]: string\n} & Record<string, string>\n\ntype PasswordUICopy = typeof DEFAULT_COPY\n\n/**\n * Configure the password UI.\n */\nexport interface PasswordUIOptions\n  extends Pick<PasswordConfig, \"sendCode\" | \"validatePassword\"> {\n  /**\n   * Custom copy for the UI.\n   */\n  copy?: Partial<PasswordUICopy>\n}\n\n/**\n * Creates a UI for the Password provider flow.\n * @param input - Configure the UI.\n */\nexport function PasswordUI(input: PasswordUIOptions): PasswordConfig {\n  const copy = {\n    ...DEFAULT_COPY,\n    ...input.copy,\n  }\n  return {\n    validatePassword: input.validatePassword,\n    sendCode: input.sendCode,\n    login: async (_req, form, error): Promise<Response> => {\n      const jsx = (\n        <Layout>\n          <form data-component=\"form\" method=\"post\">\n            <FormAlert message={error?.type && copy?.[`error_${error.type}`]} />\n            <input\n              data-component=\"input\"\n              type=\"email\"\n              name=\"email\"\n              required\n              placeholder={copy.input_email}\n              autofocus={!error}\n              value={form?.get(\"email\")?.toString()}\n            />\n            <input\n              data-component=\"input\"\n              autofocus={error?.type === \"invalid_password\"}\n              required\n              type=\"password\"\n              name=\"password\"\n              placeholder={copy.input_password}\n              autoComplete=\"current-password\"\n            />\n            <button data-component=\"button\">{copy.button_continue}</button>\n            <div data-component=\"form-footer\">\n              <span>\n                {copy.register_prompt}{\" \"}\n                <a data-component=\"link\" href=\"register\">\n                  {copy.register}\n                </a>\n              </span>\n              <a data-component=\"link\" href=\"change\">\n                {copy.change_prompt}\n              </a>\n            </div>\n          </form>\n        </Layout>\n      )\n      return new Response(jsx.toString(), {\n        status: error ? 401 : 200,\n        headers: {\n          \"Content-Type\": \"text/html\",\n        },\n      })\n    },\n    register: async (_req, state, form, error): Promise<Response> => {\n      const emailError = [\"invalid_email\", \"email_taken\"].includes(\n        error?.type || \"\",\n      )\n      const passwordError = [\n        \"invalid_password\",\n        \"password_mismatch\",\n        \"validation_error\",\n      ].includes(error?.type || \"\")\n      const jsx = (\n        <Layout>\n          <form data-component=\"form\" method=\"post\">\n            <FormAlert\n              message={\n                error?.type\n                  ? error.type === \"validation_error\"\n                    ? (error.message ?? copy?.[`error_${error.type}`])\n                    : copy?.[`error_${error.type}`]\n                  : undefined\n              }\n            />\n            {state.type === \"start\" && (\n              <>\n                <input type=\"hidden\" name=\"action\" value=\"register\" />\n                <input\n                  data-component=\"input\"\n                  autofocus={!error || emailError}\n                  type=\"email\"\n                  name=\"email\"\n                  value={!emailError ? form?.get(\"email\")?.toString() : \"\"}\n                  required\n                  placeholder={copy.input_email}\n                />\n                <input\n                  data-component=\"input\"\n                  autofocus={passwordError}\n                  type=\"password\"\n                  name=\"password\"\n                  placeholder={copy.input_password}\n                  required\n                  value={\n                    !passwordError ? form?.get(\"password\")?.toString() : \"\"\n                  }\n                  autoComplete=\"new-password\"\n                />\n                <input\n                  data-component=\"input\"\n                  type=\"password\"\n                  name=\"repeat\"\n                  required\n                  autofocus={passwordError}\n                  placeholder={copy.input_repeat}\n                  autoComplete=\"new-password\"\n                />\n                <button data-component=\"button\">{copy.button_continue}</button>\n                <div data-component=\"form-footer\">\n                  <span>\n                    {copy.login_prompt}{\" \"}\n                    <a data-component=\"link\" href=\"authorize\">\n                      {copy.login}\n                    </a>\n                  </span>\n                </div>\n              </>\n            )}\n\n            {state.type === \"code\" && (\n              <>\n                <input type=\"hidden\" name=\"action\" value=\"verify\" />\n                <input\n                  data-component=\"input\"\n                  autofocus\n                  name=\"code\"\n                  minLength={6}\n                  maxLength={6}\n                  required\n                  placeholder={copy.input_code}\n                  autoComplete=\"one-time-code\"\n                />\n                <button data-component=\"button\">{copy.button_continue}</button>\n              </>\n            )}\n          </form>\n        </Layout>\n      ) as string\n      return new Response(jsx.toString(), {\n        headers: {\n          \"Content-Type\": \"text/html\",\n        },\n      })\n    },\n    change: async (_req, state, form, error): Promise<Response> => {\n      const passwordError = [\n        \"invalid_password\",\n        \"password_mismatch\",\n        \"validation_error\",\n      ].includes(error?.type || \"\")\n      const jsx = (\n        <Layout>\n          <form data-component=\"form\" method=\"post\" replace>\n            <FormAlert\n              message={\n                error?.type\n                  ? error.type === \"validation_error\"\n                    ? (error.message ?? copy?.[`error_${error.type}`])\n                    : copy?.[`error_${error.type}`]\n                  : undefined\n              }\n            />\n            {state.type === \"start\" && (\n              <>\n                <input type=\"hidden\" name=\"action\" value=\"code\" />\n                <input\n                  data-component=\"input\"\n                  autofocus\n                  type=\"email\"\n                  name=\"email\"\n                  required\n                  value={form?.get(\"email\")?.toString()}\n                  placeholder={copy.input_email}\n                />\n              </>\n            )}\n            {state.type === \"code\" && (\n              <>\n                <input type=\"hidden\" name=\"action\" value=\"verify\" />\n                <input\n                  data-component=\"input\"\n                  autofocus\n                  name=\"code\"\n                  minLength={6}\n                  maxLength={6}\n                  required\n                  placeholder={copy.input_code}\n                  autoComplete=\"one-time-code\"\n                />\n              </>\n            )}\n            {state.type === \"update\" && (\n              <>\n                <input type=\"hidden\" name=\"action\" value=\"update\" />\n                <input\n                  data-component=\"input\"\n                  autofocus\n                  type=\"password\"\n                  name=\"password\"\n                  placeholder={copy.input_password}\n                  required\n                  value={\n                    !passwordError ? form?.get(\"password\")?.toString() : \"\"\n                  }\n                  autoComplete=\"new-password\"\n                />\n                <input\n                  data-component=\"input\"\n                  type=\"password\"\n                  name=\"repeat\"\n                  required\n                  value={\n                    !passwordError ? form?.get(\"password\")?.toString() : \"\"\n                  }\n                  placeholder={copy.input_repeat}\n                  autoComplete=\"new-password\"\n                />\n              </>\n            )}\n            <button data-component=\"button\">{copy.button_continue}</button>\n          </form>\n          {state.type === \"code\" && (\n            <form method=\"post\">\n              <input type=\"hidden\" name=\"action\" value=\"code\" />\n              <input type=\"hidden\" name=\"email\" value={state.email} />\n              {state.type === \"code\" && (\n                <div data-component=\"form-footer\">\n                  <span>\n                    {copy.code_return}{\" \"}\n                    <a data-component=\"link\" href=\"authorize\">\n                      {copy.login.toLowerCase()}\n                    </a>\n                  </span>\n                  <button data-component=\"link\">{copy.code_resend}</button>\n                </div>\n              )}\n            </form>\n          )}\n        </Layout>\n      )\n      return new Response(jsx.toString(), {\n        status: error ? 400 : 200,\n        headers: {\n          \"Content-Type\": \"text/html\",\n        },\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/select.tsx",
    "content": "/**\n * The UI that's displayed when loading the root page of the OpenAuth server. You can configure\n * which providers should be displayed in the select UI.\n *\n * ```ts\n * import { Select } from \"@openauthjs/openauth/ui/select\"\n *\n * export default issuer({\n *   select: Select({\n *     providers: {\n *       github: {\n *         hide: true\n *       },\n *       google: {\n *         display: \"Google\"\n *       }\n *     }\n *   })\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\n/** @jsxImportSource hono/jsx */\n\nimport { Layout } from \"./base.js\"\nimport { ICON_GITHUB, ICON_GOOGLE } from \"./icon.js\"\n\nexport interface SelectProps {\n  /**\n   * An object with all the providers and their config; where the key is the provider name.\n   *\n   * @example\n   * ```ts\n   * {\n   *   github: {\n   *     hide: true\n   *   },\n   *   google: {\n   *     display: \"Google\"\n   *   }\n   * }\n   * ```\n   */\n  providers?: Record<\n    string,\n    {\n      /**\n       * Whether to hide the provider from the select UI.\n       * @default false\n       */\n      hide?: boolean\n      /**\n       * The display name of the provider.\n       */\n      display?: string\n    }\n  >\n}\n\nexport function Select(props?: SelectProps) {\n  return async (\n    providers: Record<string, string>,\n    _req: Request,\n  ): Promise<Response> => {\n    const jsx = (\n      <Layout>\n        <div data-component=\"form\">\n          {Object.entries(providers).map(([key, type]) => {\n            const match = props?.providers?.[key]\n            if (match?.hide) return\n            const icon = ICON[key]\n            return (\n              <a\n                href={`/${key}/authorize`}\n                data-component=\"button\"\n                data-color=\"ghost\"\n              >\n                {icon && <i data-slot=\"icon\">{icon}</i>}\n                Continue with {match?.display || DISPLAY[type] || type}\n              </a>\n            )\n          })}\n        </div>\n      </Layout>\n    )\n\n    return new Response(jsx.toString(), {\n      headers: {\n        \"Content-Type\": \"text/html\",\n      },\n    })\n  }\n}\n\nconst DISPLAY: Record<string, string> = {\n  twitch: \"Twitch\",\n  google: \"Google\",\n  github: \"GitHub\",\n  apple: \"Apple\",\n  x: \"X\",\n  facebook: \"Facebook\",\n  microsoft: \"Microsoft\",\n  slack: \"Slack\",\n}\n\nconst ICON: Record<string, any> = {\n  code: (\n    <svg\n      fill=\"currentColor\"\n      viewBox=\"0 0 52 52\"\n      data-name=\"Layer 1\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M8.55,36.91A6.55,6.55,0,1,1,2,43.45,6.54,6.54,0,0,1,8.55,36.91Zm17.45,0a6.55,6.55,0,1,1-6.55,6.54A6.55,6.55,0,0,1,26,36.91Zm17.45,0a6.55,6.55,0,1,1-6.54,6.54A6.54,6.54,0,0,1,43.45,36.91ZM8.55,19.45A6.55,6.55,0,1,1,2,26,6.55,6.55,0,0,1,8.55,19.45Zm17.45,0A6.55,6.55,0,1,1,19.45,26,6.56,6.56,0,0,1,26,19.45Zm17.45,0A6.55,6.55,0,1,1,36.91,26,6.55,6.55,0,0,1,43.45,19.45ZM8.55,2A6.55,6.55,0,1,1,2,8.55,6.54,6.54,0,0,1,8.55,2ZM26,2a6.55,6.55,0,1,1-6.55,6.55A6.55,6.55,0,0,1,26,2ZM43.45,2a6.55,6.55,0,1,1-6.54,6.55A6.55,6.55,0,0,1,43.45,2Z\"\n        fill-rule=\"evenodd\"\n      />\n    </svg>\n  ),\n  password: (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n    >\n      <path\n        fill-rule=\"evenodd\"\n        d=\"M12 1.5a5.25 5.25 0 0 0-5.25 5.25v3a3 3 0 0 0-3 3v6.75a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3v-6.75a3 3 0 0 0-3-3v-3c0-2.9-2.35-5.25-5.25-5.25Zm3.75 8.25v-3a3.75 3.75 0 1 0-7.5 0v3h7.5Z\"\n        clip-rule=\"evenodd\"\n      />\n    </svg>\n  ),\n  twitch: (\n    <svg role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\">\n      <path\n        fill=\"currentColor\"\n        d=\"M40.1 32L10 108.9v314.3h107V480h60.2l56.8-56.8h87l117-117V32H40.1zm357.8 254.1L331 353H224l-56.8 56.8V353H76.9V72.1h321v214zM331 149v116.9h-40.1V149H331zm-107 0v116.9h-40.1V149H224z\"\n      ></path>\n    </svg>\n  ),\n  google: ICON_GOOGLE,\n  github: ICON_GITHUB,\n  apple: (\n    <svg role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 814 1000\">\n      <path\n        fill=\"currentColor\"\n        d=\"M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z \"\n      />\n    </svg>\n  ),\n  x: (\n    <svg role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1200 1227\">\n      <path\n        fill=\"currentColor\"\n        d=\"M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z\"\n      />\n    </svg>\n  ),\n  microsoft: (\n    <svg\n      role=\"img\"\n      viewBox=\"0 0 256 256\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      preserveAspectRatio=\"xMidYMid\"\n    >\n      <path fill=\"#F1511B\" d=\"M121.666 121.666H0V0h121.666z\" />\n      <path fill=\"#80CC28\" d=\"M256 121.666H134.335V0H256z\" />\n      <path fill=\"#00ADEF\" d=\"M121.663 256.002H0V134.336h121.663z\" />\n      <path fill=\"#FBBC09\" d=\"M256 256.002H134.335V134.336H256z\" />\n    </svg>\n  ),\n  facebook: (\n    <svg\n      role=\"img\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 36 36\"\n      fill=\"url(#a)\"\n    >\n      <defs>\n        <linearGradient x1=\"50%\" x2=\"50%\" y1=\"97.078%\" y2=\"0%\" id=\"a\">\n          <stop offset=\"0%\" stop-color=\"#0062E0\" />\n          <stop offset=\"100%\" stop-color=\"#19AFFF\" />\n        </linearGradient>\n      </defs>\n      <path d=\"M15 35.8C6.5 34.3 0 26.9 0 18 0 8.1 8.1 0 18 0s18 8.1 18 18c0 8.9-6.5 16.3-15 17.8l-1-.8h-4l-1 .8z\" />\n      <path\n        fill=\"#FFF\"\n        d=\"m25 23 .8-5H21v-3.5c0-1.4.5-2.5 2.7-2.5H26V7.4c-1.3-.2-2.7-.4-4-.4-4.1 0-7 2.5-7 7v4h-4.5v5H15v12.7c1 .2 2 .3 3 .3s2-.1 3-.3V23h4z\"\n      />\n    </svg>\n  ),\n  slack: (\n    <svg\n      role=\"img\"\n      enable-background=\"new 0 0 2447.6 2452.5\"\n      viewBox=\"0 0 2447.6 2452.5\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g clip-rule=\"evenodd\" fill-rule=\"evenodd\">\n        <path\n          d=\"m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z\"\n          fill=\"#36c5f0\"\n        />\n        <path\n          d=\"m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z\"\n          fill=\"#2eb67d\"\n        />\n        <path\n          d=\"m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z\"\n          fill=\"#ecb22e\"\n        />\n        <path\n          d=\"m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0\"\n          fill=\"#e01e5a\"\n        />\n      </g>\n    </svg>\n  ),\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/theme.ts",
    "content": "/**\n * Use one of the built-in themes.\n *\n * @example\n *\n * ```ts\n * import { THEME_SST } from \"@openauthjs/openauth/ui/theme\"\n *\n * export default issuer({\n *   theme: THEME_SST,\n *   // ...\n * })\n * ```\n *\n * Or define your own.\n *\n * ```ts\n * import type { Theme } from \"@openauthjs/openauth/ui/theme\"\n *\n * const MY_THEME: Theme = {\n *   title: \"Acne\",\n *   radius: \"none\",\n *   favicon: \"https://www.example.com/favicon.svg\",\n *   // ...\n * }\n *\n * export default issuer({\n *   theme: MY_THEME,\n *   // ...\n * })\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * A type to define values for light and dark mode.\n *\n * @example\n * ```ts\n * {\n *   light: \"#FFF\",\n *   dark: \"#000\"\n * }\n * ```\n */\nexport interface ColorScheme {\n  /**\n   * The value for dark mode.\n   */\n  dark: string\n  /**\n   * The value for light mode.\n   */\n  light: string\n}\n\n/**\n * A type to define your custom theme.\n */\nexport interface Theme {\n  /**\n   * The name of your app. Also used as the title of the page.\n   *\n   * @example\n   * ```ts\n   * {\n   *   title: \"Acne\"\n   * }\n   * ```\n   */\n  title?: string\n  /**\n   * A URL to the favicon of your app.\n   *\n   * @example\n   * ```ts\n   * {\n   *   favicon: \"https://www.example.com/favicon.svg\"\n   * }\n   * ```\n   */\n  favicon?: string\n  /**\n   * The border radius of the UI elements.\n   *\n   * @example\n   * ```ts\n   * {\n   *   radius: \"none\"\n   * }\n   * ```\n   */\n  radius?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"full\"\n  /**\n   * The primary color of the theme.\n   *\n   * Takes a color or both light and dark colors.\n   *\n   * @example\n   * ```ts\n   * {\n   *   primary: \"#FF5E00\"\n   * }\n   * ```\n   */\n  primary: string | ColorScheme\n  /**\n   * The background color of the theme.\n   *\n   * Takes a color or both light and dark colors.\n   *\n   * @example\n   * ```ts\n   * {\n   *   background: \"#FFF\"\n   * }\n   * ```\n   */\n  background?: string | ColorScheme\n  /**\n   * A URL to the logo of your app.\n   *\n   * Takes a single image or both light and dark mode versions.\n   *\n   * @example\n   * ```ts\n   * {\n   *   logo: \"https://www.example.com/logo.svg\"\n   * }\n   * ```\n   */\n  logo?: string | ColorScheme\n  /**\n   * The font family and scale of the theme.\n   */\n  font?: {\n    /**\n     * The font family of the theme.\n     *\n     * @example\n     * ```ts\n     * {\n     *   font: {\n     *     family: \"Geist Mono, monospace\"\n     *   }\n     * }\n     * ```\n     */\n    family?: string\n    /**\n     * The font scale of the theme. Can be used to increase or decrease the font sizes across\n     * the UI.\n     *\n     * @default \"1\"\n     * @example\n     * ```ts\n     * {\n     *   font: {\n     *     scale: \"1.25\"\n     *   }\n     * }\n     * ```\n     */\n    scale?: string\n  }\n  /**\n   * Custom CSS that's added to the page in a `<style>` tag.\n   *\n   * This can be used to import custom fonts.\n   *\n   * @example\n   * ```ts\n   * {\n   *   css: `@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@100;200;300;400;500;600;700;800;900&display=swap');`\n   * }\n   * ```\n   */\n  css?: string\n}\n\n/**\n * Built-in default OpenAuth theme.\n */\nexport const THEME_OPENAUTH: Theme = {\n  title: \"OpenAuth\",\n  radius: \"none\",\n  background: {\n    dark: \"black\",\n    light: \"white\",\n  },\n  primary: {\n    dark: \"white\",\n    light: \"black\",\n  },\n  font: {\n    family: \"IBM Plex Sans, sans-serif\",\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@100;200;300;400;500;600;700&display=swap');\n  `,\n}\n\n/**\n * Built-in theme based on [Terminal](https://terminal.shop).\n */\nexport const THEME_TERMINAL: Theme = {\n  title: \"terminal\",\n  radius: \"none\",\n  favicon: \"https://www.terminal.shop/favicon.svg\",\n  logo: {\n    dark: \"https://www.terminal.shop/images/logo-white.svg\",\n    light: \"https://www.terminal.shop/images/logo-black.svg\",\n  },\n  primary: \"#ff5e00\",\n  background: {\n    dark: \"rgb(0, 0, 0)\",\n    light: \"rgb(255, 255, 255)\",\n  },\n  font: {\n    family: \"Geist Mono, monospace\",\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100;200;300;400;500;600;700;800;900&display=swap');\n  `,\n}\n\n/**\n * Built-in theme based on [SST](https://sst.dev).\n */\nexport const THEME_SST: Theme = {\n  title: \"SST\",\n  favicon: \"https://sst.dev/favicon.svg\",\n  logo: {\n    dark: \"https://sst.dev/favicon.svg\",\n    light: \"https://sst.dev/favicon.svg\",\n  },\n  background: {\n    dark: \"#1a1a2d\",\n    light: \"rgb(255, 255, 255)\",\n  },\n  primary: \"#f3663f\",\n  font: {\n    family: \"Rubik, sans-serif\",\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Rubik:wght@100;200;300;400;500;600;700;800;900&display=swap');\n  `,\n}\n\n/**\n * Built-in theme based on [Supabase](https://supabase.com).\n */\nexport const THEME_SUPABASE: Theme = {\n  title: \"Supabase\",\n  logo: {\n    dark: \"https://supabase.com/dashboard/_next/image?url=%2Fdashboard%2Fimg%2Fsupabase-dark.svg&w=128&q=75\",\n    light:\n      \"https://supabase.com/dashboard/_next/image?url=%2Fdashboard%2Fimg%2Fsupabase-light.svg&w=128&q=75\",\n  },\n  background: {\n    dark: \"#171717\",\n    light: \"#f8f8f8\",\n  },\n  primary: {\n    dark: \"#006239\",\n    light: \"#72e3ad\",\n  },\n  font: {\n    family: \"Varela Round, sans-serif\",\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Varela+Round:wght@100;200;300;400;500;600;700;800;900&display=swap');\n  `,\n}\n\n/**\n * Built-in theme based on [Vercel](https://vercel.com).\n */\nexport const THEME_VERCEL: Theme = {\n  title: \"Vercel\",\n  logo: {\n    dark: \"https://vercel.com/mktng/_next/static/media/vercel-logotype-dark.e8c0a742.svg\",\n    light:\n      \"https://vercel.com/mktng/_next/static/media/vercel-logotype-light.700a8d26.svg\",\n  },\n  background: {\n    dark: \"black\",\n    light: \"white\",\n  },\n  primary: {\n    dark: \"white\",\n    light: \"black\",\n  },\n  font: {\n    family: \"Geist, sans-serif\",\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Geist:wght@100;200;300;400;500;600;700;800;900&display=swap');\n  `,\n}\n\n// i really don't wanna use async local storage for this so get over it\n\n/**\n * @internal\n */\nexport function setTheme(value: Theme) {\n  // @ts-ignore\n  globalThis.OPENAUTH_THEME = value\n}\n\n/**\n * @internal\n */\nexport function getTheme() {\n  // @ts-ignore\n  return globalThis.OPENAUTH_THEME || THEME_OPENAUTH\n}\n"
  },
  {
    "path": "packages/openauth/src/ui/ui.css",
    "content": "@import url(\"https://unpkg.com/tailwindcss@3.4.15/src/css/preflight.css\");\n\n:root {\n  --color-background-dark: #0e0e11;\n  --color-background-light: #ffffff;\n  --color-primary-dark: #6772e5;\n  --color-primary-light: #6772e5;\n\n  --color-background-success-dark: oklch(0.3 0.04 172);\n  --color-background-success-light: oklch(from var(--color-background-success-dark) 0.83 c h);\n  --color-success-dark: oklch(from var(--color-background-success-dark) 0.92 c h);\n  --color-success-light: oklch(from var(--color-background-success-dark) 0.25 c h);\n\n  --color-background-error-dark: oklch(0.32 0.07 15);\n  --color-background-error-light: oklch(from var(--color-background-error-dark) 0.92 c h);\n  --color-error-dark: oklch(from var(--color-background-error-dark) 0.92 c h);\n  --color-error-light: oklch(from var(--color-background-error-dark) 0.25 c h);\n\n  --border-radius: 0;\n\n  --color-background: var(--color-background-dark);\n  --color-primary: var(--color-primary-dark);\n\n  --color-background-success: var(--color-background-success-dark);\n  --color-success: var(--color-success-dark);\n  --color-background-error: var(--color-background-error-dark);\n  --color-error: var(--color-error-dark);\n\n  @media (prefers-color-scheme: light) {\n    --color-background: var(--color-background-light);\n    --color-primary: var(--color-primary-light);\n\n    --color-background-success: var(--color-background-success-light);\n    --color-success: var(--color-success-light);\n    --color-background-error: var(--color-background-error-light);\n    --color-error: var(--color-error-light);\n  }\n\n  --color-high: oklch(\n    from var(--color-background) clamp(0, calc((l - 0.714) * -1000), 1) 0 0\n  );\n  --color-low: oklch(from var(--color-background) clamp(0, calc((l - 0.714) * 1000), 1) 0 0);\n  --lightness-high: color-mix(\n    in oklch,\n    var(--color-high) 0%,\n    oklch(var(--color-high) 0 0)\n  );\n  --lightness-low: color-mix(\n    in oklch,\n    var(--color-low) 0%,\n    oklch(var(--color-low) 0 0)\n  );\n  --font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\",\n    \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --font-scale: 1;\n\n  --font-size-xs: calc(0.75rem * var(--font-scale));\n  --font-size-sm: calc(0.875rem * var(--font-scale));\n  --font-size-md: calc(1rem * var(--font-scale));\n  --font-size-lg: calc(1.125rem * var(--font-scale));\n  --font-size-xl: calc(1.25rem * var(--font-scale));\n  --font-size-2xl: calc(1.5rem * var(--font-scale));\n}\n\n[data-component=\"root\"] {\n  font-family: var(--font-family);\n  background-color: var(--color-background);\n  padding: 1rem;\n  color: white;\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n  user-select: none;\n  color: var(--color-high);\n}\n\n[data-component=\"center\"] {\n  width: 380px;\n  display: flex;\n  flex-direction: column;\n  gap: 1.5rem;\n\n  &[data-size=\"small\"] {\n    width: 300px;\n  }\n}\n\n[data-component=\"link\"] {\n  text-decoration: underline;\n  text-underline-offset: 0.125rem;\n  font-weight: 600;\n}\n\n[data-component=\"label\"] {\n  display: flex;\n  gap: 0.75rem;\n  flex-direction: column;\n  font-size: var(--font-size-xs);\n}\n\n[data-component=\"logo\"] {\n  margin: 0 auto;\n  height: 2.5rem;\n  width: auto;\n  display: none;\n\n  @media (prefers-color-scheme: light) {\n    &[data-mode=\"light\"] {\n      display: block;\n    }\n  }\n\n  @media (prefers-color-scheme: dark) {\n    &[data-mode=\"dark\"] {\n      display: block;\n    }\n  }\n}\n\n[data-component=\"logo-default\"] {\n  margin: 0 auto;\n  height: 2.5rem;\n  width: auto;\n\n  @media (prefers-color-scheme: light) {\n    color: var(--color-high);\n  }\n\n  @media (prefers-color-scheme: dark) {\n    color: var(--color-high);\n  }\n}\n\n[data-component=\"input\"] {\n  width: 100%;\n  height: 2.5rem;\n  padding: 0 1rem;\n  border: 1px solid transparent;\n  --background: oklch(\n    from var(--color-background) calc(l + (-0.06 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.03)) c h\n\n  );\n  background: var(--background);\n  border-color: oklch(\n    from var(--color-background)\n      calc(clamp(0.22, l + (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06), 0.88)) c h\n  );\n  border-radius: calc(var(--border-radius) * 0.25rem);\n  font-size: var(--font-size-sm);\n  outline: none;\n\n  &:focus {\n    border-color: oklch(\n      from var(--color-background)\n        calc(clamp(0.3, l + (-0.2 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.1), 0.7)) c h\n    );\n  }\n\n  &:user-invalid:not(:focus) {\n    border-color: oklch(0.4 0.09 7.91);\n  }\n}\n\n[data-component=\"button\"] {\n  height: 2.5rem;\n  cursor: pointer;\n  border: 0;\n  font-weight: 500;\n  font-size: var(--font-size-sm);\n  border-radius: calc(var(--border-radius) * 0.25rem);\n  display: flex;\n  gap: 0.75rem;\n  align-items: center;\n  justify-content: center;\n  background: var(--color-primary);\n  color: oklch(from var(--color-primary) clamp(0, calc((l - 0.714) * -1000), 1) 0 0);\n\n  &[data-color=\"ghost\"] {\n    background: transparent;\n    color: var(--color-high);\n    border: 1px solid\n      oklch(\n        from var(--color-background)\n          calc(clamp(0.22, l + (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06), 0.88)) c h\n      );\n  }\n\n  [data-slot=\"icon\"] {\n    width: 16px;\n    height: 16px;\n\n    svg {\n      width: 100%;\n      height: 100%;\n    }\n  }\n}\n\n[data-component=\"form\"] {\n  max-width: 100%;\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  margin: 0;\n}\n\n[data-component=\"form-alert\"] {\n  height: 2.5rem;\n  display: flex;\n  align-items: center;\n  padding: 0 1rem;\n  border-radius: calc(var(--border-radius) * 0.25rem);\n  background: var(--color-background-error);\n  color: var(--color-error);\n  text-align: left;\n  font-size: 0.75rem;\n  gap: 0.5rem;\n\n  &[data-color=\"success\"] {\n    background: var(--color-background-success);\n    color: var(--color-success);\n\n    [data-slot=\"icon-success\"] { display: block; }\n    [data-slot=\"icon-danger\"] { display: none; }\n  }\n\n  &:has([data-slot=\"message\"]:empty) {\n    display: none;\n  }\n\n  [data-slot=\"icon-success\"],\n  [data-slot=\"icon-danger\"] {\n    width: 1rem;\n    height: 1rem;\n  }\n  [data-slot=\"icon-success\"] { display: none; }\n}\n\n[data-component=\"form-footer\"] {\n  display: flex;\n  gap: 1rem;\n  font-size: 0.75rem;\n  align-items: center;\n  justify-content: center;\n\n  &:has(> :nth-child(2)) {\n    justify-content: space-between;\n  }\n}\n"
  },
  {
    "path": "packages/openauth/src/util.ts",
    "content": "import type { Context } from \"hono\"\n\nexport type Prettify<T> = {\n  [K in keyof T]: T[K]\n}\n\nexport function getRelativeUrl(ctx: Context, path: string) {\n  const result = new URL(path, ctx.req.url)\n  result.host = ctx.req.header(\"x-forwarded-host\") || result.host\n  result.protocol = ctx.req.header(\"x-forwarded-proto\") || result.protocol\n  result.port = ctx.req.header(\"x-forwarded-port\") || result.port\n  return result.toString()\n}\n\nconst twoPartTlds = [\n  \"co.uk\",\n  \"co.jp\",\n  \"co.kr\",\n  \"co.nz\",\n  \"co.za\",\n  \"co.in\",\n  \"com.au\",\n  \"com.br\",\n  \"com.cn\",\n  \"com.mx\",\n  \"com.tw\",\n  \"net.au\",\n  \"org.uk\",\n  \"ne.jp\",\n  \"ac.uk\",\n  \"gov.uk\",\n  \"edu.au\",\n  \"gov.au\",\n]\n\nexport function isDomainMatch(a: string, b: string): boolean {\n  if (a === b) return true\n  const partsA = a.split(\".\")\n  const partsB = b.split(\".\")\n  const hasTwoPartTld = twoPartTlds.some(\n    (tld) => a.endsWith(\".\" + tld) || b.endsWith(\".\" + tld),\n  )\n  const numParts = hasTwoPartTld ? -3 : -2\n  const min = Math.min(partsA.length, partsB.length, numParts)\n  const tailA = partsA.slice(min).join(\".\")\n  const tailB = partsB.slice(min).join(\".\")\n  return tailA === tailB\n}\n\nexport function lazy<T>(fn: () => T): () => T {\n  let value: T | undefined\n  return () => {\n    if (value === undefined) {\n      value = fn()\n    }\n    return value\n  }\n}\n"
  },
  {
    "path": "packages/openauth/test/client.test.ts",
    "content": "import {\n  expect,\n  test,\n  setSystemTime,\n  describe,\n  beforeEach,\n  afterEach,\n  spyOn,\n  afterAll,\n  mock,\n} from \"bun:test\"\nimport { object, string } from \"valibot\"\nimport { issuer } from \"../src/issuer.js\"\nimport { createClient } from \"../src/client.js\"\nimport {\n  InvalidAccessTokenError,\n  InvalidRefreshTokenError,\n} from \"../src/error.js\"\nimport { MemoryStorage } from \"../src/storage/memory.js\"\nimport { createSubjects } from \"../src/subject.js\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string(),\n  }),\n})\n\nlet storage = MemoryStorage()\nconst auth = issuer({\n  storage,\n  subjects,\n  allow: async () => true,\n  success: async (ctx) => {\n    return ctx.subject(\"user\", {\n      userID: \"123\",\n    })\n  },\n  ttl: {\n    access: 60,\n  },\n  providers: {\n    dummy: {\n      type: \"dummy\",\n      init(route, ctx) {\n        route.get(\"/authorize\", async (c) => {\n          return ctx.success(c, {\n            email: \"foo@bar.com\",\n          })\n        })\n      },\n    },\n  },\n})\n\nconst expectNonEmptyString = expect.stringMatching(/.+/)\n\nbeforeEach(async () => {\n  setSystemTime(new Date(\"1/1/2024\"))\n})\n\nafterEach(() => {\n  setSystemTime()\n})\n\nconst consoleSpy = spyOn(console, \"error\").mockImplementation(mock())\nafterAll(() => {\n  consoleSpy.mockRestore()\n})\n\ndescribe(\"verify\", () => {\n  let tokens: { access: string; refresh: string }\n  let client: ReturnType<typeof createClient>\n\n  beforeEach(async () => {\n    client = createClient({\n      // use different issuer per test file to avoid JWKS cache issues\n      issuer: \"https://auth1.example.com\",\n      clientID: \"123\",\n      fetch: (a, b) => Promise.resolve(auth.request(a, b)),\n    })\n    const [verifier, authorization] = await client.pkce(\n      \"https://client.example.com/callback\",\n    )\n    let response = await auth.request(authorization)\n    response = await auth.request(response.headers.get(\"location\")!, {\n      headers: {\n        cookie: response.headers.get(\"set-cookie\")!,\n      },\n    })\n    const location = new URL(response.headers.get(\"location\")!)\n    const code = location.searchParams.get(\"code\")\n    const exchanged = await client.exchange(\n      code!,\n      \"https://client.example.com/callback\",\n      verifier,\n    )\n    if (exchanged.err) throw exchanged.err\n    tokens = exchanged.tokens\n  })\n\n  test(\"success\", async () => {\n    const refreshSpy = spyOn(client, \"refresh\")\n    const verified = await client.verify(subjects, tokens.access)\n    expect(verified).toStrictEqual({\n      aud: \"123\",\n      subject: {\n        type: \"user\",\n        properties: {\n          userID: \"123\",\n        },\n      },\n    })\n    expect(refreshSpy).not.toBeCalled()\n  })\n\n  test(\"success after refresh\", async () => {\n    const refreshSpy = spyOn(client, \"refresh\")\n    setSystemTime(Date.now() + 1000 * 6000 + 1000)\n    const verified = await client.verify(subjects, tokens.access, {\n      refresh: tokens.refresh,\n    })\n    expect(verified).toStrictEqual({\n      aud: \"123\",\n      tokens: {\n        expiresIn: 60,\n        access: expectNonEmptyString,\n        refresh: expectNonEmptyString,\n      },\n      subject: {\n        type: \"user\",\n        properties: {\n          userID: \"123\",\n        },\n      },\n    })\n    expect(refreshSpy).toBeCalled()\n  })\n\n  test(\"failure with expired access token\", async () => {\n    setSystemTime(Date.now() + 1000 * 6000 + 1000)\n    const verified = await client.verify(subjects, tokens.access)\n    expect(verified).toStrictEqual({\n      err: expect.any(InvalidAccessTokenError),\n    })\n  })\n\n  test(\"failure with invalid refresh token\", async () => {\n    setSystemTime(Date.now() + 1000 * 6000 + 1000)\n    const verified = await client.verify(subjects, tokens.access, {\n      refresh: \"foo\",\n    })\n    expect(verified).toStrictEqual({\n      err: expect.any(InvalidRefreshTokenError),\n    })\n  })\n})\n"
  },
  {
    "path": "packages/openauth/test/issuer.test.ts",
    "content": "import {\n  expect,\n  test,\n  setSystemTime,\n  describe,\n  beforeEach,\n  afterEach,\n} from \"bun:test\"\nimport { object, string } from \"valibot\"\nimport { issuer } from \"../src/issuer.js\"\nimport { createClient } from \"../src/client.js\"\nimport { createSubjects } from \"../src/subject.js\"\nimport { MemoryStorage } from \"../src/storage/memory.js\"\nimport { Provider } from \"../src/provider/provider.js\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string(),\n  }),\n})\n\nlet storage = MemoryStorage()\nconst issuerConfig = {\n  storage,\n  subjects,\n  allow: async () => true,\n  ttl: {\n    access: 60,\n    refresh: 6000,\n    refreshReuse: 60,\n    refreshRetention: 6000,\n  },\n  providers: {\n    dummy: {\n      type: \"dummy\",\n      init(route, ctx) {\n        route.get(\"/authorize\", async (c) => {\n          return ctx.success(c, {\n            email: \"foo@bar.com\",\n          })\n        })\n      },\n      client: async ({ clientID, clientSecret }) => {\n        if (clientID !== \"myuser\" && clientSecret !== \"mypass\") {\n          throw new Error(\"Wrong credentials\")\n        }\n        return {\n          email: \"foo@bar.com\",\n        }\n      },\n    } satisfies Provider<{ email: string }>,\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"dummy\") {\n      return ctx.subject(\"user\", {\n        userID: \"123\",\n      })\n    }\n    throw new Error(\"Invalid provider: \" + value.provider)\n  },\n}\nconst auth = issuer(issuerConfig)\n\nconst expectNonEmptyString = expect.stringMatching(/.+/)\n\nbeforeEach(async () => {\n  setSystemTime(new Date(\"1/1/2024\"))\n})\n\nafterEach(() => {\n  setSystemTime()\n})\n\ndescribe(\"code flow\", () => {\n  test(\"success\", async () => {\n    const client = createClient({\n      issuer: \"https://auth.example.com\",\n      clientID: \"123\",\n      fetch: (a, b) => Promise.resolve(auth.request(a, b)),\n    })\n    const { challenge, url } = await client.authorize(\n      \"https://client.example.com/callback\",\n      \"code\",\n      {\n        pkce: true,\n      },\n    )\n    let response = await auth.request(url)\n    expect(response.status).toBe(302)\n    response = await auth.request(response.headers.get(\"location\")!, {\n      headers: {\n        cookie: response.headers.get(\"set-cookie\")!,\n      },\n    })\n    expect(response.status).toBe(302)\n    const location = new URL(response.headers.get(\"location\")!)\n    const code = location.searchParams.get(\"code\")\n    expect(code).not.toBeNull()\n    const exchanged = await client.exchange(\n      code!,\n      \"https://client.example.com/callback\",\n      challenge.verifier,\n    )\n    if (exchanged.err) throw exchanged.err\n    const tokens = exchanged.tokens\n    expect(tokens).toStrictEqual({\n      access: expectNonEmptyString,\n      refresh: expectNonEmptyString,\n      expiresIn: 60,\n    })\n    const verified = await client.verify(subjects, tokens.access)\n    if (verified.err) throw verified.err\n    expect(verified.subject).toStrictEqual({\n      type: \"user\",\n      properties: {\n        userID: \"123\",\n      },\n    })\n  })\n})\n\ndescribe(\"client credentials flow\", () => {\n  test(\"success\", async () => {\n    const client = createClient({\n      issuer: \"https://auth.example.com\",\n      clientID: \"123\",\n      fetch: (a, b) => Promise.resolve(auth.request(a, b)),\n    })\n    const response = await auth.request(\"https://auth.example.com/token\", {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n      },\n      body: new URLSearchParams({\n        grant_type: \"client_credentials\",\n        provider: \"dummy\",\n        client_id: \"myuser\",\n        client_secret: \"mypass\",\n      }).toString(),\n    })\n    expect(response.status).toBe(200)\n    const tokens = await response.json()\n    expect(tokens).toStrictEqual({\n      access_token: expectNonEmptyString,\n      refresh_token: expectNonEmptyString,\n    })\n    const verified = await client.verify(subjects, tokens.access_token)\n    expect(verified).toStrictEqual({\n      aud: \"myuser\",\n      subject: {\n        type: \"user\",\n        properties: {\n          userID: \"123\",\n        },\n      },\n    })\n  })\n})\n\ndescribe(\"refresh token\", () => {\n  let tokens: { access: string; refresh: string }\n  let client: ReturnType<typeof createClient>\n\n  const generateTokens = async (issuer: typeof auth) => {\n    const { challenge, url } = await client.authorize(\n      \"https://client.example.com/callback\",\n      \"code\",\n      {\n        pkce: true,\n      },\n    )\n    let response = await issuer.request(url)\n    response = await issuer.request(response.headers.get(\"location\")!, {\n      headers: {\n        cookie: response.headers.get(\"set-cookie\")!,\n      },\n    })\n    const location = new URL(response.headers.get(\"location\")!)\n    const code = location.searchParams.get(\"code\")\n    const exchanged = await client.exchange(\n      code!,\n      \"https://client.example.com/callback\",\n      challenge.verifier,\n    )\n    if (exchanged.err) throw exchanged.err\n    return exchanged.tokens\n  }\n\n  const createClientAndTokens = async (issuer: typeof auth) => {\n    client = createClient({\n      issuer: \"https://auth.example.com\",\n      clientID: \"123\",\n      fetch: (a, b) => Promise.resolve(issuer.request(a, b)),\n    })\n    tokens = await generateTokens(issuer)\n  }\n\n  const requestRefreshToken = async (\n    refresh_token: string,\n    issuer?: typeof auth,\n  ) =>\n    (issuer ?? auth).request(\"https://auth.example.com/token\", {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n      },\n      body: new URLSearchParams({\n        grant_type: \"refresh_token\",\n        ...(refresh_token ? { refresh_token } : {}),\n      }).toString(),\n    })\n\n  beforeEach(async () => {\n    await createClientAndTokens(auth)\n  })\n\n  test(\"success\", async () => {\n    setSystemTime(Date.now() + 1000 * 60 + 1000)\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n    const refreshed = await response.json()\n    expect(refreshed).toStrictEqual({\n      access_token: expectNonEmptyString,\n      refresh_token: expectNonEmptyString,\n      expires_in: expect.any(Number),\n    })\n    expect(refreshed.access_token).not.toEqual(tokens.access)\n    expect(refreshed.refresh_token).not.toEqual(tokens.refresh)\n\n    const verified = await client.verify(subjects, refreshed.access_token)\n    expect(verified).toStrictEqual({\n      aud: \"123\",\n      subject: {\n        type: \"user\",\n        properties: {\n          userID: \"123\",\n        },\n      },\n    })\n  })\n\n  test(\"success with valid access token\", async () => {\n    // have to increment the time so new access token claims are different (i.e. exp)\n    setSystemTime(Date.now() + 1000)\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n    const refreshed = await response.json()\n    expect(refreshed).toStrictEqual({\n      access_token: expectNonEmptyString,\n      refresh_token: expectNonEmptyString,\n      expires_in: expect.any(Number),\n    })\n\n    expect(refreshed.access_token).not.toEqual(tokens.access)\n    expect(refreshed.refresh_token).not.toEqual(tokens.refresh)\n\n    const verified = await client.verify(subjects, refreshed.access_token)\n    expect(verified).toStrictEqual({\n      aud: \"123\",\n      subject: {\n        type: \"user\",\n        properties: {\n          userID: \"123\",\n        },\n      },\n    })\n  })\n\n  test(\"multiple active tokens\", async () => {\n    const tokens2 = await generateTokens(auth)\n\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n\n    response = await requestRefreshToken(tokens2.refresh)\n    expect(response.status).toBe(200)\n  })\n\n  test(\"failure with reuse interval disabled\", async () => {\n    const issuerWithoutReuse = issuer({\n      ...issuerConfig,\n      ttl: {\n        ...issuerConfig.ttl,\n        reuse: 0,\n        retention: 0,\n      },\n    })\n    await createClientAndTokens(issuerWithoutReuse)\n    let response = await requestRefreshToken(tokens.refresh, issuerWithoutReuse)\n    expect(response.status).toBe(200)\n\n    response = await requestRefreshToken(tokens.refresh, issuerWithoutReuse)\n    expect(response.status).toBe(400)\n    const reused = await response.json()\n    expect(reused.error).toBe(\"invalid_grant\")\n  })\n\n  test(\"success with reuse interval enabled\", async () => {\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n    const refreshed = await response.json()\n    const [, refreshedAccessPayload] = refreshed.access_token.split(\".\")\n\n    setSystemTime(Date.now() + 1000 * 30)\n\n    response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n    const reused = await response.json()\n    const [, reusedAccessPayload] = reused.access_token.split(\".\")\n    expect(refreshed.refresh_token).toEqual(reused.refresh_token)\n    /**\n     * Access token signature is different every time for ES256 alg,\n     * but the payload should be the same.\n     */\n    expect(refreshedAccessPayload).toEqual(reusedAccessPayload)\n  })\n\n  test(\"invalidated with reuse detection\", async () => {\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(200)\n\n    setSystemTime(Date.now() + 1000 * 60 + 1000)\n\n    response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(400)\n  })\n\n  test(\"expired failure\", async () => {\n    setSystemTime(Date.now() + 1000 * 6000 + 1000)\n    let response = await requestRefreshToken(tokens.refresh)\n    expect(response.status).toBe(400)\n    const reused = await response.json()\n    expect(reused.error).toBe(\"invalid_grant\")\n  })\n\n  test(\"missing failure\", async () => {\n    let response = await requestRefreshToken(\"\")\n    expect(response.status).toBe(400)\n    const reused = await response.json()\n    expect(reused.error).toBe(\"invalid_request\")\n  })\n})\n\ndescribe(\"user info\", () => {\n  let tokens: { access: string; refresh: string }\n  let client: ReturnType<typeof createClient>\n\n  const generateTokens = async (issuer: typeof auth) => {\n    const { challenge, url } = await client.authorize(\n      \"https://client.example.com/callback\",\n      \"code\",\n      { pkce: true },\n    )\n    let response = await issuer.request(url)\n    response = await issuer.request(response.headers.get(\"location\")!, {\n      headers: {\n        cookie: response.headers.get(\"set-cookie\")!,\n      },\n    })\n    const location = new URL(response.headers.get(\"location\")!)\n    const code = location.searchParams.get(\"code\")\n    const exchanged = await client.exchange(\n      code!,\n      \"https://client.example.com/callback\",\n      challenge.verifier,\n    )\n    if (exchanged.err) throw exchanged.err\n    return exchanged.tokens\n  }\n\n  const createClientAndTokens = async (issuer: typeof auth) => {\n    client = createClient({\n      issuer: \"https://auth.example.com\",\n      clientID: \"123\",\n      fetch: (a, b) => Promise.resolve(issuer.request(a, b)),\n    })\n    tokens = await generateTokens(issuer)\n  }\n\n  beforeEach(async () => {\n    await createClientAndTokens(auth)\n  })\n\n  test(\"success\", async () => {\n    const response = await auth.request(\"https://auth.example.com/userinfo\", {\n      headers: { Authorization: `Bearer ${tokens.access}` },\n    })\n\n    const userinfo = await response.json()\n\n    expect(userinfo).toStrictEqual({ userID: \"123\" })\n  })\n})\n"
  },
  {
    "path": "packages/openauth/test/scrap.test.ts",
    "content": "import { expect, test } from \"bun:test\"\nimport { issuer } from \"../src/issuer.js\"\nimport { MemoryStorage } from \"../src/storage/memory.js\"\nimport { object, string } from \"valibot\"\nimport { createSubjects } from \"../src/subject.js\"\nimport { createClient } from \"../src/client.js\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string(),\n  }),\n})\n\nconst auth = issuer({\n  storage: MemoryStorage(),\n  subjects,\n  allow: async () => true,\n  success: async (ctx) => {\n    return ctx.subject(\"user\", {\n      userID: \"123\",\n    })\n  },\n  ttl: {\n    access: 1,\n  },\n  providers: {\n    dummy: {\n      type: \"dummy\",\n      init(route, ctx) {\n        route.get(\"/authorize\", async (c) => {\n          return ctx.success(c, {\n            email: \"foo@bar.com\",\n          })\n        })\n      },\n    },\n  },\n})\n\ntest(\"code flow\", async () => {\n  const client = createClient({\n    issuer: \"https://auth.example.com\",\n    clientID: \"123\",\n    fetch: (a, b) => Promise.resolve(auth.request(a, b)),\n  })\n  const [verifier, authorization] = await client.pkce(\n    \"https://client.example.com/callback\",\n  )\n  let response = await auth.request(authorization)\n  expect(response.status).toBe(302)\n  response = await auth.request(response.headers.get(\"location\")!, {\n    headers: {\n      cookie: response.headers.get(\"set-cookie\")!,\n    },\n  })\n  expect(response.status).toBe(302)\n  const location = new URL(response.headers.get(\"location\")!)\n  const code = location.searchParams.get(\"code\")\n  expect(code).not.toBeNull()\n  const exchanged = await client.exchange(\n    code!,\n    \"https://client.example.com/callback\",\n    verifier,\n  )\n  if (exchanged.err) throw exchanged.err\n  expect(exchanged.tokens.access).toBeTruthy()\n  expect(exchanged.tokens.refresh).toBeTruthy()\n  const verified = await client.verify(subjects, exchanged.tokens.access)\n  if (verified.err) throw verified.err\n  expect(verified.subject.type).toBe(\"user\")\n  if (verified.subject.type !== \"user\") throw new Error(\"Invalid subject\")\n  expect(verified.subject.properties.userID).toBe(\"123\")\n  await new Promise((resolve) => setTimeout(resolve, 2000))\n  const failed = await client.verify(subjects, exchanged.tokens.access)\n  expect(failed.err).toBeInstanceOf(Error)\n  const next = await client.verify(subjects, exchanged.tokens.access, {\n    refresh: exchanged.tokens.refresh,\n  })\n  if (next.err) throw next.err\n  expect(next.tokens?.access).toBeDefined()\n  expect(next.tokens?.refresh).toBeDefined()\n  expect(next.tokens?.access).not.toEqual(exchanged.tokens.access)\n  expect(next.tokens?.refresh).not.toEqual(exchanged.tokens.refresh)\n  await client.verify(subjects, next.tokens!.access!)\n})\n"
  },
  {
    "path": "packages/openauth/test/storage.test.ts",
    "content": "import { afterEach, setSystemTime } from \"bun:test\"\nimport { beforeEach, describe, expect, test } from \"bun:test\"\nimport { MemoryStorage } from \"../src/storage/memory.js\"\n\nlet storage = MemoryStorage()\n\nbeforeEach(async () => {\n  storage = MemoryStorage()\n  setSystemTime(new Date(\"1/1/2024\"))\n})\n\nafterEach(() => {\n  setSystemTime()\n})\n\ndescribe(\"set\", () => {\n  test(\"basic\", async () => {\n    await storage.set([\"users\", \"123\"], { name: \"Test User\" })\n    const result = await storage.get([\"users\", \"123\"])\n    expect(result).toEqual({ name: \"Test User\" })\n  })\n\n  test(\"ttl\", async () => {\n    await storage.set(\n      [\"temp\", \"key\"],\n      { value: \"value\" },\n      new Date(Date.now() + 100),\n    ) // 100ms TTL\n    let result = await storage.get([\"temp\", \"key\"])\n    expect(result?.value).toBe(\"value\")\n\n    setSystemTime(Date.now() + 150)\n    result = await storage.get([\"temp\", \"key\"])\n    expect(result).toBeUndefined()\n  })\n\n  test(\"nested\", async () => {\n    const complexObj = {\n      id: 1,\n      nested: { a: 1, b: { c: 2 } },\n      array: [1, 2, 3],\n    }\n    await storage.set([\"complex\"], complexObj)\n    const result = await storage.get([\"complex\"])\n    expect(result).toEqual(complexObj)\n  })\n})\n\ndescribe(\"get\", () => {\n  test(\"missing\", async () => {\n    const result = await storage.get([\"nonexistent\"])\n    expect(result).toBeUndefined()\n  })\n\n  test(\"key\", async () => {\n    await storage.set([\"a\", \"b\", \"c\"], { value: \"nested\" })\n    const result = await storage.get([\"a\", \"b\", \"c\"])\n    expect(result?.value).toBe(\"nested\")\n  })\n})\n\ndescribe(\"remove\", () => {\n  test(\"existing\", async () => {\n    await storage.set([\"test\"], \"value\")\n    await storage.remove([\"test\"])\n    const result = await storage.get([\"test\"])\n    expect(result).toBeUndefined()\n  })\n\n  test(\"missing\", async () => {\n    expect(storage.remove([\"nonexistent\"])).resolves.toBeUndefined()\n  })\n})\n\ndescribe(\"scan\", () => {\n  test(\"all\", async () => {\n    await storage.set([\"users\", \"1\"], { id: 1 })\n    await storage.set([\"users\", \"2\"], { id: 2 })\n    await storage.set([\"other\"], { id: 3 })\n    const results = await Array.fromAsync(storage.scan([\"users\"]))\n    expect(results).toHaveLength(2)\n    expect(results).toContainEqual([[\"users\", \"1\"], { id: 1 }])\n    expect(results).toContainEqual([[\"users\", \"2\"], { id: 2 }])\n  })\n\n  test(\"ttl\", async () => {\n    await storage.set([\"temp\", \"1\"], \"a\", new Date(Date.now() + 100))\n    await storage.set([\"temp\", \"2\"], \"b\", new Date(Date.now() + 100))\n    await storage.set([\"temp\", \"3\"], \"c\")\n    expect(await Array.fromAsync(storage.scan([\"temp\"]))).toHaveLength(3)\n    setSystemTime(Date.now() + 150)\n    expect(await Array.fromAsync(storage.scan([\"temp\"]))).toHaveLength(1)\n  })\n})\n"
  },
  {
    "path": "packages/openauth/test/util.test.ts",
    "content": "import { expect, test } from \"bun:test\"\nimport { Context } from \"hono\"\nimport { getRelativeUrl, isDomainMatch } from \"../src/util.js\"\n\ntest(\"isDomainMatch\", () => {\n  // Basic matches\n  expect(isDomainMatch(\"example.com\", \"example.com\")).toBe(true)\n  expect(isDomainMatch(\"sub.example.com\", \"example.com\")).toBe(true)\n  expect(isDomainMatch(\"a.example.com\", \"b.example.com\")).toBe(true)\n\n  // Local hostnames\n  expect(isDomainMatch(\"romulus\", \"romulus\")).toBe(true)\n  expect(isDomainMatch(\"romulus\", \"remus\")).toBe(false)\n  expect(isDomainMatch(\"localhost\", \"localhost\")).toBe(true)\n  expect(isDomainMatch(\"server\", \"server.local\")).toBe(false)\n\n  // Two-part TLDs\n  expect(isDomainMatch(\"example.co.uk\", \"example.co.uk\")).toBe(true)\n  expect(isDomainMatch(\"sub.example.co.uk\", \"example.co.uk\")).toBe(true)\n  expect(isDomainMatch(\"evil.co.uk\", \"bank.co.uk\")).toBe(false)\n  expect(isDomainMatch(\"example.com.au\", \"example.com.au\")).toBe(true)\n\n  // Attack vectors\n  // Attempt to match on TLD only\n  expect(isDomainMatch(\"evil.com\", \"bank.com\")).toBe(false)\n  expect(isDomainMatch(\"evil.co.uk\", \"bank.co.uk\")).toBe(false)\n\n  // Subdomain attacks\n  expect(isDomainMatch(\"evil.com.attacker.com\", \"evil.com\")).toBe(false)\n  expect(isDomainMatch(\"bank.co.uk.attacker.com\", \"bank.co.uk\")).toBe(false)\n  expect(isDomainMatch(\"example.com.evil.com\", \"example.com\")).toBe(false)\n\n  // Prefix attacks\n  expect(isDomainMatch(\"myexample.com\", \"example.com\")).toBe(false)\n  expect(isDomainMatch(\"exampleevilsite.com\", \"example.com\")).toBe(false)\n\n  // Double-dot attacks\n  expect(isDomainMatch(\"example..com\", \"example.com\")).toBe(false)\n  expect(isDomainMatch(\"evil..co..uk\", \"bank.co.uk\")).toBe(false)\n\n  // Empty parts attacks\n  expect(isDomainMatch(\"example.com.\", \"example.com\")).toBe(false)\n\n  // Mixed case attacks\n  expect(isDomainMatch(\"EXAMPLE.COM\", \"example.com\")).toBe(false)\n  expect(isDomainMatch(\"Example.Co.Uk\", \"example.co.uk\")).toBe(false)\n\n  // IP address attempts\n  expect(isDomainMatch(\"127.0.0.1\", \"localhost\")).toBe(false)\n  expect(isDomainMatch(\"192.168.1.1\", \"192.168.1.1\")).toBe(true)\n\n  // Special character attacks\n  expect(isDomainMatch(\"exam%70le.com\", \"example.com\")).toBe(false)\n  expect(isDomainMatch(\"exam\\u0000ple.com\", \"example.com\")).toBe(false)\n\n  // Unicode/punycode attacks\n  expect(isDomainMatch(\"xn--e1awd7f.com\", \"example.com\")).toBe(false)\n  expect(isDomainMatch(\"еxample.com\", \"example.com\")).toBe(false) // cyrillic 'е'\n\n  // Edge cases\n  expect(isDomainMatch(\"\", \"\")).toBe(true) // empty strings\n  expect(isDomainMatch(\" \", \" \")).toBe(true) // spaces\n  expect(isDomainMatch(\"example.com\", \"\")).toBe(false) // empty vs non-empty\n  expect(isDomainMatch(\"com\", \"com\")).toBe(true) // single part\n  expect(isDomainMatch(\".com\", \"com\")).toBe(false) // dot prefix\n\n  // Mixed TLD tests\n  expect(isDomainMatch(\"example.co.uk.com\", \"example.co.uk\")).toBe(false)\n  expect(isDomainMatch(\"example.com.co.uk\", \"example.co.uk\")).toBe(false)\n})\n\ntest(\"getRelativeUrl\", () => {\n  // Helper to create a mock Context\n  const createMockContext = (\n    url: string,\n    headers: Record<string, string> = {},\n  ) => {\n    return {\n      req: {\n        url,\n        header: (name: string) => headers[name.toLowerCase()] || \"\",\n      },\n    } as Context\n  }\n\n  // Test basic URL construction\n  const ctx1 = createMockContext(\"http://example.com\")\n  expect(getRelativeUrl(ctx1, \"/path\")).toBe(\"http://example.com/path\")\n\n  // Test with x-forwarded headers\n  const ctx2 = createMockContext(\"http://original.com\", {\n    \"x-forwarded-host\": \"forwarded.com\",\n    \"x-forwarded-proto\": \"https\",\n    \"x-forwarded-port\": \"443\",\n  })\n  expect(getRelativeUrl(ctx2, \"/path\")).toBe(\"https://forwarded.com/path\")\n\n  // Test with absolute URLs\n  const ctx4 = createMockContext(\"http://example.com\")\n  expect(getRelativeUrl(ctx4, \"http://other.com/path\")).toBe(\n    \"http://other.com/path\",\n  )\n})\n"
  },
  {
    "path": "packages/openauth/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/node22/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"dist\",\n    \"declaration\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"hono/jsx\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "scripts/format",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nbun x prettier --write \"**/*.{js,jsx,ts,tsx,json,md,yaml,yml}\"\n\n# Check for changes\nif ! git diff --quiet HEAD; then\n    git add -A\n    git commit -m \"auto: format code\"\n    git push origin HEAD\n    echo \"pushed changes\"\nelse\n    echo \"no changes\"\nfi\n"
  },
  {
    "path": "www/.gitignore",
    "content": "# build output\ndist/\n# generated types\n.astro/\n# doc output\noutput/\n\n# dependencies\nnode_modules/\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# environment variables\n.env\n.env.production\n\n# macOS-specific files\n.DS_Store\n"
  },
  {
    "path": "www/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"astro-build.astro-vscode\"],\n  \"unwantedRecommendations\": []\n}\n"
  },
  {
    "path": "www/.vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"command\": \"./node_modules/.bin/astro dev\",\n      \"name\": \"Development server\",\n      \"request\": \"launch\",\n      \"type\": \"node-terminal\"\n    }\n  ]\n}\n"
  },
  {
    "path": "www/README.md",
    "content": "# Starlight Starter Kit: Basics\n\n[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build)\n\n```\nnpm create astro@latest -- --template starlight\n```\n\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)\n[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)\n[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics)\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)\n\n> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!\n\n## 🚀 Project Structure\n\nInside of your Astro + Starlight project, you'll see the following folders and files:\n\n```\n.\n├── public/\n├── src/\n│   ├── assets/\n│   ├── content/\n│   │   ├── docs/\n│   │   └── config.ts\n│   └── env.d.ts\n├── astro.config.mjs\n├── package.json\n└── tsconfig.json\n```\n\nStarlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.\n\nImages can be added to `src/assets/` and embedded in Markdown with a relative link.\n\nStatic assets, like favicons, can be placed in the `public/` directory.\n\n## 🧞 Commands\n\nAll commands are run from the root of the project, from a terminal:\n\n| Command                   | Action                                           |\n| :------------------------ | :----------------------------------------------- |\n| `npm install`             | Installs dependencies                            |\n| `npm run dev`             | Starts local dev server at `localhost:4321`      |\n| `npm run build`           | Build your production site to `./dist/`          |\n| `npm run preview`         | Preview your build locally, before deploying     |\n| `npm run astro ...`       | Run CLI commands like `astro add`, `astro check` |\n| `npm run astro -- --help` | Get help using the Astro CLI                     |\n\n## 👀 Want to learn more?\n\nCheck out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).\n"
  },
  {
    "path": "www/astro.config.mjs",
    "content": "import theme from \"toolbeam-docs-theme\"\nimport starlight from \"@astrojs/starlight\"\nimport { defineConfig } from \"astro/config\"\nimport { rehypeHeadingIds } from \"@astrojs/markdown-remark\"\nimport rehypeAutolinkHeadings from \"rehype-autolink-headings\"\nimport config from \"./config\"\n\nconst url = \"https://openauth.js.org\"\n\n// https://astro.build/config\nexport default defineConfig({\n  site: url,\n  trailingSlash: 'always',\n  devToolbar: {\n    enabled: false,\n  },\n  integrations: [\n    starlight({\n      plugins: [theme()],\n      title: \"OpenAuth\",\n      description: \"Universal, standards-based auth provider.\",\n      head: [\n        {\n          tag: \"link\",\n          attrs: {\n            rel: \"icon\",\n            href: \"/favicon.ico\",\n            sizes: \"48x48\",\n          },\n        },\n        // Add light/dark mode favicon\n        {\n          tag: \"link\",\n          attrs: {\n            rel: \"icon\",\n            href: \"/favicon.svg\",\n            media: \"(prefers-color-scheme: light)\",\n          },\n        },\n        {\n          tag: \"link\",\n          attrs: {\n            rel: \"icon\",\n            href: \"/favicon-dark.svg\",\n            media: \"(prefers-color-scheme: dark)\",\n          },\n        },\n        {\n          tag: \"meta\",\n          attrs: {\n            property: \"og:image\",\n            content: `${url}/social-share.png`,\n          },\n        },\n        {\n          tag: \"meta\",\n          attrs: {\n            property: \"twitter:image\",\n            content: `${url}/social-share.png`,\n          },\n        },\n      ],\n      logo: {\n        light: \"./src/assets/logo-light.svg\",\n        dark: \"./src/assets/logo-dark.svg\",\n        replacesTitle: true,\n      },\n      social: {\n        github: config.github,\n        discord: config.discord,\n      },\n      lastUpdated: true,\n      editLink: {\n        baseUrl: `${config.github}/edit/master/www/`,\n      },\n      components: {\n        Hero: \"./src/components/Hero.astro\",\n      },\n      customCss: [\n        \"./src/custom.css\",\n        \"./src/styles/lander.css\",\n      ],\n      sidebar: [\n        { label: \"Intro\", slug: \"docs\" },\n        {\n          label: \"Quick Start\",\n          items: [\n            { label: \"Standalone\", slug: \"docs/start/standalone\" },\n            { label: \"SST\", slug: \"docs/start/sst\" },\n          ],\n        },\n        {\n          label: \"Core\",\n          items: [\"docs/client\", \"docs/issuer\", \"docs/subject\"],\n        },\n        {\n          label: \"Providers\",\n          items: [\n            { label: \"Code\", slug: \"docs/provider/code\" },\n            { label: \"OIDC\", slug: \"docs/provider/oidc\" },\n            { label: \"OAuth\", slug: \"docs/provider/oauth2\" },\n            { label: \"Apple\", slug: \"docs/provider/apple\" },\n            { label: \"X.com\", slug: \"docs/provider/x\" },\n            { label: \"Slack\", slug: \"docs/provider/slack\" },\n            { label: \"Yahoo\", slug: \"docs/provider/yahoo\" },\n            { label: \"Google\", slug: \"docs/provider/google\" },\n            { label: \"Github\", slug: \"docs/provider/github\" },\n            { label: \"Twitch\", slug: \"docs/provider/twitch\" },\n            { label: \"Spotify\", slug: \"docs/provider/spotify\" },\n            { label: \"Cognito\", slug: \"docs/provider/cognito\" },\n            { label: \"Discord\", slug: \"docs/provider/discord\" },\n            { label: \"Facebook\", slug: \"docs/provider/facebook\" },\n            { label: \"Keycloak\", slug: \"docs/provider/keycloak\" },\n            { label: \"Password\", slug: \"docs/provider/password\" },\n            { label: \"Microsoft\", slug: \"docs/provider/microsoft\" },\n            { label: \"JumpCloud\", slug: \"docs/provider/jumpcloud\" },\n          ],\n        },\n        {\n          label: \"UI\",\n          items: [\"docs/ui/theme\", \"docs/ui/select\", \"docs/ui/code\", \"docs/ui/password\"],\n        },\n        {\n          label: \"Storage\",\n          items: [\"docs/storage/memory\", \"docs/storage/dynamo\", \"docs/storage/cloudflare\"],\n        },\n      ],\n    }),\n  ],\n  markdown: {\n    rehypePlugins: [\n      rehypeHeadingIds,\n      [rehypeAutolinkHeadings, { behavior: \"wrap\" }],\n    ],\n  },\n})\n"
  },
  {
    "path": "www/config.ts",
    "content": "export default {\n  github: \"https://github.com/toolbeam/openauth\",\n  discord: \"https://sst.dev/discord\",\n}\n"
  },
  {
    "path": "www/generate.ts",
    "content": "import * as path from \"path\"\nimport * as fs from \"fs\"\nimport * as TypeDoc from \"typedoc\"\nimport config from \"./config\"\n\nlet warnings = 0\nconst OUTPUT_DIR = \"src/content/docs/docs\"\ntype Text = string | Text[]\n\nconfigureLogger()\nconst project = await build()\nconst modules = project.getChildrenByKind(TypeDoc.ReflectionKind.Module)\nconst subject = modules.find((m) => m.name === \"subject\")!\nconst issuer = modules.find((m) => m.name === \"issuer\")!\nconst error = modules.find((m) => m.name === \"error\")!\nconst client = modules.find((m) => m.name === \"client\")!\nconst uis = modules.filter((m) => m.name.startsWith(\"ui/\"))\nconst providers = modules.filter((m) => m.name.startsWith(\"provider/\"))\nconst storages = modules.filter((m) => m.name.startsWith(\"storage/\"))\n\nconst FRONTMATTER: Record<\n  string,\n  { title: string; editUrl: string; description: string }\n> = {\n  themeUI: {\n    title: \"Themes\",\n    description: \"Reference docs for themes.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/ui/theme.ts`,\n  },\n  passwordUI: {\n    title: \"PasswordUI\",\n    description: \"Reference doc for the `PasswordUI`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/ui/password.tsx`,\n  },\n  codeUI: {\n    title: \"CodeUI\",\n    description: \"Reference doc for the `CodeUI`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/ui/code.tsx`,\n  },\n  selectUI: {\n    title: \"Select\",\n    description: \"Reference doc for the `Select` UI.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/ui/select.tsx`,\n  },\n  dynamo: {\n    title: \"DynamoDB\",\n    description: \"Reference doc for the DynamoDB storage adapter.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/storage/dynamo.ts`,\n  },\n  memory: {\n    title: \"Memory\",\n    description: \"Reference doc for the Memory storage adapter.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/storage/memory.ts`,\n  },\n  cloudflare: {\n    title: \"Cloudflare KV\",\n    description: \"Reference doc for the Cloudflare KV storage adapter.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/storage/cloudflare.ts`,\n  },\n  issuer: {\n    title: \"Issuer\",\n    description: \"Reference doc for the OpenAuth server.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/issuer.ts`,\n  },\n  subject: {\n    title: \"Subject\",\n    description: \"Reference doc for creating subjects.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/subject.ts`,\n  },\n  client: {\n    title: \"Client\",\n    description: \"Reference doc for the OpenAuth client.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/client.ts`,\n  },\n  apple: {\n    title: \"AppleProvider\",\n    description: \"Reference doc for the `AppleProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/apple.ts`,\n  },\n  google: {\n    title: \"GoogleProvider\",\n    description: \"Reference doc for the `GoogleProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/google.ts`,\n  },\n  x: {\n    title: \"XProvider\",\n    description: \"Reference doc for the `XProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/x.ts`,\n  },\n  yahoo: {\n    title: \"YahooProvider\",\n    description: \"Reference doc for the `YahooProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/yahoo.ts`,\n  },\n  github: {\n    title: \"GithubProvider\",\n    description: \"Reference doc for the `GithubProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/github.ts`,\n  },\n  microsoft: {\n    title: \"MicrosoftProvider\",\n    description: \"Reference doc for the `MicrosoftProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/microsoft.ts`,\n  },\n  password: {\n    title: \"PasswordProvider\",\n    description: \"Reference doc for the `PasswordProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/password.ts`,\n  },\n  keycloak: {\n    title: \"KeycloakProvider\",\n    description: \"Reference doc for the `KeycloakProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/keycloak.ts`,\n  },\n  slack: {\n    title: \"SlackProvider\",\n    description: \"Reference doc for the `SlackProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/slack.ts`,\n  },\n  jumpcloud: {\n    title: \"JumpCloudProvider\",\n    description: \"Reference doc for the `JumpCloudProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/jumpcloud.ts`,\n  },\n  spotify: {\n    title: \"SpotifyProvider\",\n    description: \"Reference doc for the `SpotifyProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/spotify.ts`,\n  },\n  discord: {\n    title: \"DiscordProvider\",\n    description: \"Reference doc for the `DiscordProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/discord.ts`,\n  },\n  cognito: {\n    title: \"CognitoProvider\",\n    description: \"Reference doc for the `CognitoProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/cognito.ts`,\n  },\n  facebook: {\n    title: \"FacebookProvider\",\n    description: \"Reference doc for the `FacebookProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/facebook.ts`,\n  },\n  twitch: {\n    title: \"TwitchProvider\",\n    description: \"Reference doc for the `TwitchProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/twitch.ts`,\n  },\n  oidc: {\n    title: \"OidcProvider\",\n    description: \"Reference doc for the `OidcProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/oidc.ts`,\n  },\n  oauth2: {\n    title: \"Oauth2Provider\",\n    description: \"Reference doc for the `Oauth2Provider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/oauth2.ts`,\n  },\n  code: {\n    title: \"CodeProvider\",\n    description: \"Reference doc for the `CodeProvider`.\",\n    editUrl: `${config.github}/blob/master/packages/openauth/src/provider/code.ts`,\n  },\n}\n\nrenderSubject()\nrenderClient()\nrenderIssuer()\nproviders.map(renderProvider)\nstorages.map(renderStorage)\nuis.map(renderUI)\nprintWarnings()\n\nfunction renderProvider(module: TypeDoc.DeclarationReflection) {\n  console.debug(`renderProvider: ${module.name}`)\n\n  const name = module.name.replace(\"provider/\", \"\")\n  const { title, editUrl, description } = FRONTMATTER[name] || {}\n  saveFile(module.name, [\n    renderHeader({\n      title: title || name,\n      moduleName: module.name,\n      editUrl: editUrl || false,\n      description: description || `A page for the ${name} provider.`,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(module)),\n    renderFunctions(module),\n    renderInterfaces(module),\n    `</div>`,\n  ])\n}\n\nfunction renderStorage(module: TypeDoc.DeclarationReflection) {\n  console.debug(`renderStorage: ${module.name}`)\n\n  const name = module.name.replace(\"storage/\", \"\")\n  const { title, editUrl, description } = FRONTMATTER[name] || {}\n  saveFile(module.name, [\n    renderHeader({\n      title: title || name,\n      moduleName: module.name,\n      editUrl: editUrl || false,\n      description: description || `A page for the ${name} storage.`,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(module)),\n    renderFunctions(module),\n    renderInterfaces(module),\n    `</div>`,\n  ])\n}\n\nfunction renderUI(module: TypeDoc.DeclarationReflection) {\n  console.debug(`renderUI: ${module.name}`)\n\n  const name = module.name.replace(\"ui/\", \"\")\n  const { title, editUrl, description } = FRONTMATTER[`${name}UI`] || {}\n  saveFile(module.name, [\n    renderHeader({\n      title: title || name,\n      moduleName: module.name,\n      editUrl: editUrl || false,\n      description: description || `A page for the ${name} UI.`,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(module)),\n    renderVariables(module, { title: \"Themes\" }),\n    renderFunctions(module),\n    renderInterfaces(module),\n    `</div>`,\n  ])\n}\n\nfunction renderSubject() {\n  console.debug(`renderSubject`)\n\n  const module = subject\n  const name = module.name\n  const { title, editUrl, description } = FRONTMATTER[name] || {}\n  saveFile(name, [\n    renderHeader({\n      title,\n      editUrl,\n      description,\n      moduleName: module.name,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(module)),\n    renderFunctions(module),\n    renderInterfaces(module),\n    `</div>`,\n  ])\n}\n\nfunction renderClient() {\n  console.debug(`renderClient`)\n\n  const module = client\n  const name = module.name\n  const { title, editUrl, description } = FRONTMATTER[name] || {}\n  saveFile(name, [\n    renderHeader({\n      title: title || name,\n      moduleName: module.name,\n      editUrl: editUrl || false,\n      description: description || `A page for the client.`,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(module)),\n    renderFunctions(module),\n    renderInterfaces(module, {\n      filter: (i) => i.name === \"Client\",\n      depth: \"h3\",\n    }),\n    renderInterfaces(module, {\n      filter: (i) => i.name !== \"Client\",\n    }),\n    `</div>`,\n  ])\n}\n\nfunction renderIssuer() {\n  console.debug(`renderIssuer`)\n\n  const errors = error.getChildrenByKind(TypeDoc.ReflectionKind.Class)\n  const name = issuer.name\n  const { title, editUrl, description } = FRONTMATTER[name] || {}\n  saveFile(name, [\n    renderHeader({\n      title,\n      editUrl,\n      description,\n      moduleName: issuer.name,\n    }),\n    `<div class=\"tsdoc\">`,\n    renderAbout(renderComment(issuer)),\n    renderFunctions(issuer),\n    renderInterfaces(issuer),\n    \"## Errors\",\n    renderAbout(renderComment(error)),\n    errors.map(renderClass),\n    `</div>`,\n  ])\n}\n\nfunction renderHeader(input: {\n  moduleName: string\n  title: string\n  editUrl: string | boolean\n  description: string\n}) {\n  return [\n    `---`,\n    `title: ${input.title}`,\n    `editUrl: ${input.editUrl}`,\n    `description: ${input.description}`,\n    `---`,\n    \"\",\n    \"import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\",\n    \"import { Tabs, TabItem } from '@astrojs/starlight/components'\",\n    \"\",\n  ]\n}\n\nfunction renderAbout(content: Text) {\n  console.debug(` ∟renderAbout`)\n  return [`<Section type=\"about\">`, content, `</Section>`, \"---\"]\n}\n\nfunction renderClass(c: TypeDoc.DeclarationReflection) {\n  console.debug(`   ∟class: ${c.name}`)\n  return [`### ${c.name}`, `<Segment>`, renderComment(c), `</Segment>`]\n}\n\nfunction renderVariables(\n  module: TypeDoc.DeclarationReflection,\n  options: {\n    title: string\n  },\n) {\n  console.debug(` ∟renderVariables`)\n  const variables = module.getChildrenByKind(TypeDoc.ReflectionKind.Variable)\n  return render(variables.length, [\n    `## ${options.title}`,\n    variables.map((v) => {\n      console.debug(`   ∟variable: ${v.name}`)\n      return [`### ${v.name}`, `<Segment>`, renderComment(v), `</Segment>`]\n    }),\n  ])\n}\n\nfunction renderFunctions(module: TypeDoc.DeclarationReflection) {\n  console.debug(` ∟renderFunctions`)\n  const functions = module.getChildrenByKind(TypeDoc.ReflectionKind.Function)\n  return render(functions.length, [\n    \"## Methods\",\n    ...functions.map((f) => {\n      console.debug(`   ∟function: ${f.name}`)\n      return [\n        `### ${f.name}`,\n        `<Segment>`,\n        `<Section type=\"signature\">`,\n        renderSignatureAsCode(f.signatures![0]),\n        `</Section>`,\n        render(f.signatures![0].parameters?.length, [\n          `<Section type=\"parameters\">`,\n          `#### Parameters`,\n          f.signatures![0].parameters!.map((p) => {\n            return [\n              `- <p><code class=\"key\">${renderParameter(p)}</code> ${renderType(p.type!)}</p>`,\n              renderComment(p),\n            ]\n          }),\n          `</Section>`,\n        ]),\n        `<InlineSection>`,\n        `**Returns** ${renderType(f.signatures![0].type!)}`,\n        `</InlineSection>`,\n        renderComment(f.signatures![0]),\n        `</Segment>`,\n      ]\n    }),\n  ])\n}\n\nfunction renderInterfaces(\n  module: TypeDoc.DeclarationReflection,\n  options?: {\n    filter?: (i: TypeDoc.DeclarationReflection) => boolean\n    depth?: \"h2\" | \"h3\"\n  },\n) {\n  console.debug(` ∟renderInterfaces`)\n  const depth = options?.depth ?? \"h2\"\n  const interfaces = [\n    ...module.getChildrenByKind(TypeDoc.ReflectionKind.Interface),\n    ...module.getChildrenByKind(TypeDoc.ReflectionKind.TypeAlias),\n  ]\n    .filter(options?.filter ?? (() => true))\n    .sort((a, b) => a.name.localeCompare(b.name))\n\n  return interfaces.map((i) => {\n    // render type alias as a type\n    if (i.kindOf(TypeDoc.ReflectionKind.TypeAlias)) {\n      return [\n        `## ${i.name}`,\n        `<Segment>`,\n        `<Section type=\"parameters\">`,\n        `<InlineSection>`,\n        `**Type** ${renderType(i.type!)}`,\n        `</InlineSection>`,\n        `</Section>`,\n        renderComment(i),\n        `</Segment>`,\n      ]\n    }\n    // render interface as a type\n    else {\n      console.debug(`   ∟interface: ${i.name}`)\n      const properties = i.getChildrenByKind(TypeDoc.ReflectionKind.Property)\n      const methods = i.getChildrenByKind(TypeDoc.ReflectionKind.Method)\n      return [\n        `## ${i.name}`,\n        `<Segment>`,\n        render(depth === \"h2\", [\n          `<Section type=\"parameters\">`,\n          properties.map((p) => [\n            `- <p>[<code class=\"key\">${renderProperty(p)}</code>](#${buildLinkHash(i.name, p.name)}) ${renderType(p.type!)}</p>`,\n            flattenNestedTypes(p.type!, p.name).map(\n              ({ depth, prefix, subType }) =>\n                `${\" \".repeat(depth * 2)}- <p>[<code class=\"key\">${renderProperty(\n                  subType,\n                )}</code>](#${buildLinkHash(prefix, subType.name)}) ${renderType(subType.type!)}</p>`,\n            ),\n          ]),\n          methods.map((m) => {\n            return `- <p>[<code class=\"key\">${renderProperty(m)}</code>](#${buildLinkHash(i.name, m.name)}) ${renderSignatureAsType(m.signatures![0])}</p>`\n          }),\n          `</Section>`,\n        ]),\n        renderComment(i),\n        `</Segment>`,\n        // Render nested types\n        properties.flatMap((p) => [\n          `<NestedTitle id=\"${buildLinkHash(i.name, p.name)}\" Tag=\"h4\" parent=\"${i.name}.\">${renderProperty(p)}</NestedTitle>`,\n          `<Segment>`,\n          `<Section type=\"parameters\">`,\n          `<InlineSection>`,\n          `**Type** ${renderType(p.type!)}`,\n          `</InlineSection>`,\n          `</Section>`,\n          renderComment(p),\n          `</Segment>`,\n          flattenNestedTypes(p.type!, p.name).map(\n            ({ depth, prefix, subType }) => [\n              `<NestedTitle id=\"${buildLinkHash(prefix, subType.name)}\" Tag=\"h5\" parent=\"${i.name}.${prefix}.\">${renderProperty(subType)}</NestedTitle>`,\n              `<Segment>`,\n              `<Section type=\"parameters\">`,\n              `<InlineSection>`,\n              `**Type** ${renderType(subType.type!)}`,\n              `</InlineSection>`,\n              `</Section>`,\n              renderComment(subType),\n              `</Segment>`,\n            ],\n          ),\n        ]),\n        methods.flatMap((m) => [\n          depth === \"h2\"\n            ? `<NestedTitle id=\"${buildLinkHash(i.name, m.name)}\" Tag=\"h4\" parent=\"${i.name}.\">${renderProperty(m)}</NestedTitle>`\n            : `### ${m.name}`,\n          `<Segment>`,\n          `<Section type=\"parameters\">`,\n          `<InlineSection>`,\n          `**Type** ${renderSignatureAsType(m.signatures![0])}`,\n          `</InlineSection>`,\n          `</Section>`,\n          renderComment(m.signatures![0]),\n          `</Segment>`,\n        ]),\n      ]\n    }\n  })\n}\n\nfunction renderComment(declaration: TypeDoc.Reflection) {\n  if (!declaration.comment) return []\n\n  return [\n    declaration instanceof TypeDoc.DeclarationReflection &&\n    declaration.defaultValue\n      ? [\n          ``,\n          `<InlineSection>`,\n          `**Default** ${renderType({\n            type: \"literal\",\n            value: declaration.defaultValue.replace(/\"/g, \"\"),\n          } as TypeDoc.LiteralType)}`,\n          `</InlineSection>`,\n        ]\n      : [],\n    declaration.comment.blockTags\n      .filter((tag) => tag.tag === \"@default\")\n      .map((tag) => {\n        return [\n          ``,\n          `<InlineSection>`,\n          // If default tag is just a value, render it as a type ie. false\n          // Otherwise render it as a comment ie. No domains configured\n          tag.content.length === 1 && tag.content[0].kind === \"code\"\n            ? `**Default** ${renderType(\n                new TypeDoc.IntrinsicType(\n                  tag.content[0].text\n                    .replace(/`/g, \"\")\n                    .replace(/{/g, \"&lcub;\")\n                    .replace(/}/g, \"&rcub;\"),\n                ),\n              )}`\n            : `**Default** ${tag.content.map((c) => c.text)}`,\n          `</InlineSection>`,\n        ]\n      }),\n    declaration.comment.summary.map((s) => s.text).join(\"\"),\n    declaration.comment.blockTags\n      .filter((tag) => tag.tag === \"@example\")\n      .map((tag) => tag.content.map((c) => c.text)),\n  ]\n}\n\nfunction renderSignatureAsCode(signature: TypeDoc.SignatureReflection) {\n  const parameters = (signature.parameters ?? []).map(renderParameter)\n  return [\"```ts\", `${signature.name}(${parameters})`, \"```\"]\n}\n\nfunction renderSignatureAsType(signature: TypeDoc.SignatureReflection) {\n  const parameters = (signature.parameters ?? [])\n    .map(\n      (parameter) =>\n        `${renderParameter(parameter)}: ${\n          // If the type is an object, render it inline\n          parameter.type?.type === \"reflection\" &&\n          parameter.type.declaration.kind === TypeDoc.ReflectionKind.TypeLiteral\n            ? renderObjectTypeInline(parameter.type)\n            : renderType(parameter.type!)\n        }`,\n    )\n    .join(\", \")\n  return `<code class=\"primitive\">(${parameters}) => ${renderType(\n    signature.type!,\n  )}</code>`\n}\n\nfunction renderParameter(parameter: TypeDoc.ParameterReflection) {\n  if (parameter.defaultValue && parameter.defaultValue !== \"{}\") {\n    throw new Error(\n      [\n        `Unsupported default value \"${parameter.defaultValue}\" for name \"${parameter.name}\".`,\n        ``,\n        `Function signature parameters can be defined as optional in one of two ways:`,\n        ` - flag.isOptional is set, ie. \"(args?: FooArgs)\"`,\n        ` - defaultValue is set, ie. \"(args: FooArgs = {})`,\n        ``,\n        `But in this case, the default value is not \"{}\". Hence not supported.`,\n      ].join(\"\\n\"),\n    )\n  }\n\n  return [\n    parameter.type?.type === \"tuple\" ? \"...\" : \"\",\n    parameter.name,\n    parameter.flags.isOptional || parameter.defaultValue ? \"?\" : \"\",\n  ].join(\"\")\n}\n\nfunction renderProperty(property: TypeDoc.DeclarationReflection) {\n  return `${property.name}${property.flags.isOptional ? \"?\" : \"\"}`\n}\n\nfunction renderType(type: TypeDoc.SomeType): Text {\n  // Special handle hard-to-document types\n  if (\n    type.type === \"reference\" &&\n    type.package === \"@openauthjs/openauth\" &&\n    type.qualifiedName === \"IssuerInput.Result\"\n  )\n    return `<code class=\"type\">${type.name}</code>`\n\n  // Special handle hard-to-document types\n  if (type.type === \"indexedAccess\") {\n    if (\n      type.indexType.type === \"typeOperator\" &&\n      type.indexType.target.type === \"reference\" &&\n      type.indexType.target.qualifiedName === \"VerifyResult.T\"\n    )\n      return `<code class=\"type\">Subject</code>`\n  }\n\n  if (type.type === \"intrinsic\") return renderIntrisicType(type)\n  if (type.type === \"literal\") return renderLiteralType(type)\n  if (type.type === \"templateLiteral\") return renderTemplateLiteralType(type)\n  if (type.type === \"union\") return renderUnionType(type)\n  if (type.type === \"array\") return renderArrayType(type)\n  if (type.type === \"reference\") {\n    if (type.package === \"typescript\") return renderTypescriptType(type)\n    if (type.package === \"@openauthjs/openauth\") return renderOpenAuthType(type)\n    if (type.package === \"@standard-schema/spec\")\n      return renderStandardSchemaType(type)\n    return `<code class=\"type\">${type.name}</code>`\n  }\n  if (\n    type.type === \"reflection\" &&\n    type.declaration.kind === TypeDoc.ReflectionKind.TypeLiteral\n  ) {\n    if (type.declaration.signatures) return renderCallbackType(type)\n    return `<code class=\"primitive\">Object</code>`\n  }\n  console.warn(`🟡️ WARNING: rendering \"${type.type}\" type as any`)\n  return `<code class=\"primitive\">any</code>`\n}\nfunction renderIntrisicType(type: TypeDoc.IntrinsicType) {\n  return `<code class=\"primitive\">${type.name}</code>`\n}\nfunction renderLiteralType(type: TypeDoc.LiteralType) {\n  // Intrisic values: don't print in quotes\n  // ie.\n  // {\n  //   \"type\": \"literal\",\n  //   \"value\": false\n  // }\n  if (type.value === true || type.value === false) {\n    return `<code class=\"primitive\">${type.value}</code>`\n  }\n  // String value\n  // ie.\n  // {\n  //   \"type\": \"literal\",\n  //   \"value\": \"arm64\"\n  // }\n  const sanitized =\n    typeof type.value === \"string\"\n      ? type.value!.replace(/([*:])/g, \"\\\\$1\")\n      : type.value\n  return `<code class=\"symbol\">&ldquo;</code><code class=\"primitive\">${sanitized}</code><code class=\"symbol\">&rdquo;</code>`\n}\nfunction renderTemplateLiteralType(type: TypeDoc.TemplateLiteralType) {\n  // ie. memory: `${number} MB`\n  // {\n  //   \"type\": \"templateLiteral\",\n  //   \"head\": \"\",\n  //   \"tail\": [\n  //     [\n  //       {\n  //         \"type\": \"intrinsic\",\n  //         \"name\": \"number\"\n  //       },\n  //       \" MB\"\n  //     ]\n  //   ]\n  // },\n  if (\n    typeof type.head !== \"string\" ||\n    type.tail.length !== 1 ||\n    type.tail[0].length !== 2 ||\n    type.tail[0][0].type !== \"intrinsic\" ||\n    typeof type.tail[0][1] !== \"string\"\n  ) {\n    console.error(type)\n    throw new Error(`Unsupported templateLiteral type`)\n  }\n  return `<code class=\"symbol\">&ldquo;</code><code class=\"primitive\">${type.head}$\\\\{${type.tail[0][0].name}\\\\}${type.tail[0][1]}</code><code class=\"symbol\">&rdquo;</code>`\n}\nfunction renderUnionType(type: TypeDoc.UnionType) {\n  return type.types\n    .map((t) => {\n      // Handle discriminated unions\n      if (\n        t.type === \"reflection\" &&\n        t.declaration.kind === TypeDoc.ReflectionKind.TypeLiteral &&\n        t.declaration.children\n      ) {\n        return renderObjectTypeInline(t)\n      }\n      return renderType(t)\n    })\n    .join(`<code class=\"symbol\"> | </code>`)\n}\nfunction renderArrayType(type: TypeDoc.ArrayType) {\n  return type.elementType.type === \"union\"\n    ? `<code class=\"symbol\">(</code>${renderType(\n        type.elementType,\n      )}<code class=\"symbol\">)[]</code>`\n    : `${renderType(type.elementType)}<code class=\"symbol\">[]</code>`\n}\nfunction renderCallbackType(type: TypeDoc.ReflectionType) {\n  return renderSignatureAsType(type.declaration.signatures![0])\n}\nfunction renderObjectTypeInline(type: TypeDoc.ReflectionType): Text {\n  return [\n    `<code class=\"symbol\">&lcub; </code>`,\n    type.declaration\n      .children!.map((c) =>\n        [\n          `<code class=\"key\">${c.name}</code>`,\n          `<code class=\"symbol\">&colon; </code>`,\n          // If rendering inline, also render children inline\n          c.type?.type === \"reflection\" &&\n          c.type.declaration.kind === TypeDoc.ReflectionKind.TypeLiteral\n            ? renderObjectTypeInline(c.type)\n            : renderType(c.type!),\n        ].join(\"\"),\n      )\n      .join(`<code class=\"symbol\">, </code>`),\n    `<code class=\"symbol\"> &rcub;</code>`,\n  ].join(\"\")\n}\nfunction renderTypescriptType(type: TypeDoc.ReferenceType) {\n  // ie. Partial<Foo> => just render Foo\n  if (type.name === \"Partial\") return renderType(type.typeArguments![0])\n\n  // ie. Record<string, string>\n  return [\n    `<code class=\"primitive\">${type.name}</code>`,\n    `<code class=\"symbol\">&lt;</code>`,\n    type.typeArguments?.map((t) => renderType(t)).join(\", \"),\n    `<code class=\"symbol\">&gt;</code>`,\n  ].join(\"\")\n}\nfunction renderOpenAuthType(type: TypeDoc.ReferenceType) {\n  // Reference to a non-documented type, ie. FetchLike\n  if (!type.reflection) return `<code class=\"type\">${type.name}</code>`\n\n  // Reference to a generic type\n  // ie.\n  // export function createSubjects<Schema extends SubjectSchema>(types: Schema): Schema {\n  //   return { ...types }\n  // }\n  if (type.reflection.kind === TypeDoc.ReflectionKind.TypeParameter) {\n    const t = (type.reflection as TypeDoc.TypeParameterReflection).type\n    if (t) return renderType(t)\n  }\n\n  if (\n    type.reflection.kind === TypeDoc.ReflectionKind.TypeAlias ||\n    type.reflection.kind === TypeDoc.ReflectionKind.Interface ||\n    type.reflection.kind === TypeDoc.ReflectionKind.Class\n  ) {\n    const t = type.reflection as TypeDoc.DeclarationReflection\n    const fileName = t.sources?.[0]?.fileName\n    if (fileName?.startsWith(\"packages/openauth/src/subject.ts\"))\n      return `[<code class=\"type\">${t.name}</code>](/docs/subject#${t.name.toLowerCase()})`\n    if (fileName?.startsWith(\"packages/openauth/src/error.ts\"))\n      return `[<code class=\"type\">${t.name}</code>](/docs/issuer#${t.name.toLowerCase()})`\n    if (fileName?.startsWith(\"packages/openauth/src/provider/\")) {\n      const provider = fileName.split(\"/\").pop()?.split(\".\")[0]\n      return `[<code class=\"type\">${t.name}</code>](/docs/provider/${provider}#${t.name.toLowerCase()})`\n    }\n  }\n\n  return `[<code class=\"type\">${type.name}</code>](#${type.name.toLowerCase()})`\n}\nfunction renderStandardSchemaType(type: TypeDoc.ReferenceType) {\n  return `[<code class=\"type\">${type.name}</code>](https://github.com/standard-schema/standard-schema)`\n}\n\nfunction render(condition: any, content: Text) {\n  return condition ? content : []\n}\n\nfunction buildLinkHash(namespace: string, name: string) {\n  return `${namespace}.${name}`.toLowerCase()\n}\n\nfunction flattenNestedTypes(\n  type: TypeDoc.SomeType,\n  prefix: string,\n  depth: number = 1,\n): {\n  subType: TypeDoc.DeclarationReflection\n  prefix: string\n  depth: number\n}[] {\n  if (type.type === \"union\") {\n    return type.types.flatMap((t) => flattenNestedTypes(t, prefix, depth))\n  }\n  if (type.type === \"array\") {\n    return flattenNestedTypes(type.elementType, `${prefix}[]`, depth)\n  }\n  if (type.type === \"reference\") {\n    return (type.typeArguments ?? []).flatMap((t) =>\n      type.package === \"typescript\" && type.name === \"Record\"\n        ? flattenNestedTypes(t, `${prefix}[]`, depth)\n        : flattenNestedTypes(t, prefix, depth),\n    )\n  }\n  if (type.type === \"reflection\" && type.declaration.children?.length) {\n    return type.declaration\n      .children!.filter((c) => !c.comment?.modifierTags.has(\"@internal\"))\n      .filter((c) => !c.comment?.blockTags.find((t) => t.tag === \"@deprecated\"))\n      .flatMap((subType) => [\n        { prefix, subType, depth },\n        ...(subType.kind === TypeDoc.ReflectionKind.Property\n          ? flattenNestedTypes(\n              subType.type!,\n              `${prefix}.${subType.name}`,\n              depth + 1,\n            )\n          : []),\n        ...(subType.kind === TypeDoc.ReflectionKind.Accessor\n          ? flattenNestedTypes(\n              subType.getSignature?.type!,\n              `${prefix}.${subType.name}`,\n              depth + 1,\n            )\n          : []),\n      ])\n  }\n\n  return []\n}\n\n/********************/\n/** Other functions */\n/********************/\n\nfunction saveFile(moduleName: string, content: any[]) {\n  const filePath = path.join(OUTPUT_DIR, `${moduleName}.mdx`)\n  if (!fs.existsSync(path.dirname(filePath)))\n    fs.mkdirSync(path.dirname(filePath), { recursive: true })\n  fs.writeFileSync(filePath, content.flat(Infinity).join(\"\\n\"))\n}\n\nfunction configureLogger() {\n  if (process.env.DEBUG) {\n    console.warn = (...args) => {\n      warnings++\n      console.log(...args)\n    }\n    return\n  }\n\n  console.debug = () => {}\n  console.warn = () => {}\n}\n\nfunction printWarnings() {\n  if (process.env.DEBUG) {\n    console.log(`- - -\\n${warnings} warnings`)\n  }\n}\n\nasync function build() {\n  // Generate project reflection\n  const app = await TypeDoc.Application.bootstrap({\n    excludeInternal: true,\n    // Ignore type errors caused by patching `Input<>`.\n    skipErrorChecking: true,\n    // Disable parsing @default tags as ```ts block code.\n    jsDocCompatibility: {\n      defaultTag: false,\n    },\n\n    entryPoints: [\n      \"../packages/openauth/src/provider/code.ts\",\n      \"../packages/openauth/src/provider/apple.ts\",\n      \"../packages/openauth/src/provider/google.ts\",\n      \"../packages/openauth/src/provider/github.ts\",\n      \"../packages/openauth/src/provider/password.ts\",\n      \"../packages/openauth/src/provider/oauth2.ts\",\n      \"../packages/openauth/src/provider/oidc.ts\",\n      \"../packages/openauth/src/provider/slack.ts\",\n      \"../packages/openauth/src/provider/spotify.ts\",\n      \"../packages/openauth/src/provider/twitch.ts\",\n      \"../packages/openauth/src/provider/yahoo.ts\",\n      \"../packages/openauth/src/provider/microsoft.ts\",\n      \"../packages/openauth/src/provider/keycloak.ts\",\n      \"../packages/openauth/src/provider/jumpcloud.ts\",\n      \"../packages/openauth/src/provider/facebook.ts\",\n      \"../packages/openauth/src/provider/discord.ts\",\n      \"../packages/openauth/src/provider/cognito.ts\",\n      \"../packages/openauth/src/provider/x.ts\",\n      \"../packages/openauth/src/subject.ts\",\n      \"../packages/openauth/src/ui/theme.ts\",\n      \"../packages/openauth/src/ui/code.tsx\",\n      \"../packages/openauth/src/ui/select.tsx\",\n      \"../packages/openauth/src/ui/password.tsx\",\n      \"../packages/openauth/src/client.ts\",\n      \"../packages/openauth/src/issuer.ts\",\n      \"../packages/openauth/src/storage/memory.ts\",\n      \"../packages/openauth/src/storage/dynamo.ts\",\n      \"../packages/openauth/src/storage/cloudflare.ts\",\n      \"../packages/openauth/src/error.ts\",\n    ],\n    tsconfig: \"../packages/openauth/tsconfig.json\",\n  })\n\n  const project = await app.convert()\n  if (!project) throw new Error(\"Failed to convert project\")\n\n  // Generate JSON (generated for debugging purposes)\n  await app.generateJson(project, \"output/doc.json\")\n\n  return project\n}\n\nfunction print(type: TypeDoc.SomeType) {\n  // @ts-ignore\n  delete type._project\n  console.log(type)\n}\n"
  },
  {
    "path": "www/package.json",
    "content": "{\n  \"name\": \"www\",\n  \"type\": \"module\",\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"start\": \"astro dev\",\n    \"build\": \"bun generate.ts && astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\"\n  },\n  \"dependencies\": {\n    \"@astrojs/markdown-remark\": \"^6.0.1\",\n    \"@astrojs/starlight\": \"^0.32.0\",\n    \"@fontsource/ibm-plex-mono\": \"^5.1.0\",\n    \"astro\": \"^5.1.5\",\n    \"rehype-autolink-headings\": \"^7.1.0\",\n    \"sharp\": \"^0.33.4\",\n    \"toolbeam-docs-theme\": \"^0.0.3\"\n  },\n  \"devDependencies\": {\n    \"typedoc\": \"^0.27.5\"\n  }\n}\n"
  },
  {
    "path": "www/src/components/Hero.astro",
    "content": "---\nimport Default from '@astrojs/starlight/components/Hero.astro';\nimport Lander from './Lander.astro';\n\nconst { slug } = Astro.locals.starlightRoute.entry;\n---\n\n{ slug === \"\"\n  ? <Lander {...Astro.props} />\n  : <Default {...Astro.props}><slot /></Default>\n}\n"
  },
  {
    "path": "www/src/components/Lander.astro",
    "content": "---\nimport { Image } from 'astro:assets';\nimport config from \"virtual:starlight/user-config\";\nimport type { Props } from '@astrojs/starlight/props';\n\nconst { data } = Astro.locals.starlightRoute.entry;\nconst { title = data.title, tagline, image, actions = [] } = data.hero || {};\n\nconst imageAttrs = {\n    loading: 'eager' as const,\n    decoding: 'async' as const,\n    width: 400,\n    alt: image?.alt || '',\n};\n\nlet darkImage: ImageMetadata | undefined;\nlet lightImage: ImageMetadata | undefined;\nlet rawHtml: string | undefined;\nif (image) {\n    if ('file' in image) {\n        darkImage = image.file;\n    } else if ('dark' in image) {\n        darkImage = image.dark;\n        lightImage = image.light;\n    } else {\n        rawHtml = image.html;\n    }\n}\n---\n<div class=\"hero\">\n    <section class=\"top\">\n      <div class=\"logo\">\n        <Image\n          src={darkImage}\n          {...imageAttrs}\n          class:list={{ 'light:sl-hidden': Boolean(lightImage) }}\n        />\n        <Image src={lightImage} {...imageAttrs} class=\"dark:sl-hidden\" />\n      </div>\n      <h1>Universal, standards-based auth provider.</h1>\n    </section>\n\n    <section class=\"cta\">\n      <div class=\"col1\">\n        <a href=\"https://www.youtube.com/watch?v=SSjNUuQ06tk\" target=\"_blank\">Learn more</a>\n      </div>\n      <div class=\"col2\">\n      <a href=\"/docs/\">View the docs</a>\n      </div>\n      <div class=\"col3\">\n        <a href={config.social.github.url}>Star on GitHub</a>\n      </div>\n    </section>\n\n    <section class=\"content\">\n      <ul>\n        <li><b>Universal</b>: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform.</li>\n        <li><b>Self-hosted</b>: It runs entirely on your infrastructure and can be deployed on Node.js, Bun, AWS Lambda, or Cloudflare Workers.</li>\n        <li><b>Standards-based</b>: It implements the OAuth 2.0 spec and is based on web standards. So any OAuth client can use it.</li>\n        <li><b>Customizable</b>: It comes with prebuilt themeable UI that you can customize or opt out of.</li>\n      </ul>\n    </section>\n\n    <section class=\"footer\">\n      <div class=\"col1\">\n        <span>Version: Beta</span>\n      </div>\n      <div class=\"col2\">\n        <span>Author: <a href=\"https://sst.dev\">SST</a></span>\n      </div>\n    </section>\n</div>\n\n<style>\n.hero {\n  --padding: 3rem;\n  --vertical-padding: 2rem;\n  --heading-font-size: var(--sl-text-3xl);\n\n  margin: 1rem;\n  border: 2px solid var(--sl-color-white);\n}\n@media (max-width: 30rem) {\n  .hero {\n    --padding: 1rem;\n    --vertical-padding: 1rem;\n    --heading-font-size: var(--sl-text-2xl);\n\n    margin: 0.5rem;\n  }\n}\n\nsection.top {\n  padding: var(--padding);\n\n  h1 {\n    margin-top: calc(var(--vertical-padding) / 8);\n    font-size: var(--heading-font-size);\n    line-height: 1.25;\n    text-transform: uppercase;\n  }\n\n  img {\n    height: auto;\n    width: clamp(200px, 70vw, 400px);\n  }\n}\n\nsection.cta {\n  display: flex;\n  flex-direction: row;\n  justify-content: space-between;\n  border-top: 2px solid var(--sl-color-white);\n\n  & > div {\n    flex: 1;\n    line-height: 1.4;\n    text-align: center;\n    text-transform: uppercase;\n    padding: calc(var(--padding) / 2) 0.5rem;\n  }\n\n  @media (max-width: 30rem) {\n    & > div {\n      padding-bottom: calc(var(--padding) / 2 + 4px);\n    }\n  }\n\n  & > div + div {\n    border-left: 2px solid var(--sl-color-white);\n  }\n}\n\nsection.content {\n  border-top: 2px solid var(--sl-color-white);\n  padding: var(--padding);\n\n  ul {\n    padding-left: 1rem;\n\n    li + li {\n      margin-top: calc(var(--vertical-padding) / 2);\n    }\n\n    li b {\n      text-transform: uppercase;\n    }\n  }\n}\n\nsection.approach {\n  border-top: 2px solid var(--sl-color-white);\n  padding: var(--padding);\n\n  p + p {\n    margin-top: var(--vertical-padding);\n  }\n}\n\nsection.footer {\n  border-top: 2px solid var(--sl-color-white);\n  display: flex;\n  flex-direction: row;\n\n  & > div {\n    flex: 1;\n    text-align: center;\n    text-transform: uppercase;\n    padding: calc(var(--padding) / 2) 0.5rem;\n  }\n\n  & > div + div {\n    border-left: 2px solid var(--sl-color-white);\n  }\n}\n</style>\n"
  },
  {
    "path": "www/src/content/config.ts",
    "content": "import { defineCollection } from \"astro:content\"\nimport { docsSchema } from \"@astrojs/starlight/schema\"\n\nexport const collections = {\n  docs: defineCollection({ schema: docsSchema() }),\n}\n"
  },
  {
    "path": "www/src/content/docs/docs/client.mdx",
    "content": "---\ntitle: Client\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/client.ts\ndescription: Reference doc for the OpenAuth client.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse the OpenAuth client kick off your OAuth flows, exchange tokens, refresh tokens,\nand verify tokens.\n\nFirst, create a client.\n\n```ts title=\"client.ts\"\nimport { createClient } from \"@openauthjs/openauth/client\"\n\nconst client = createClient({\n  clientID: \"my-client\",\n  issuer: \"https://auth.myserver.com\"\n})\n```\n\nKick off the OAuth flow by calling `authorize`.\n\n```ts\nconst redirect_uri = \"https://myserver.com/callback\"\n\nconst { url } = await client.authorize(\n  redirect_uri,\n  \"code\"\n)\n```\n\nWhen the user completes the flow, `exchange` the code for tokens.\n\n```ts\nconst tokens = await client.exchange(query.get(\"code\"), redirect_uri)\n```\n\nAnd `verify` the tokens.\n\n```ts\nconst verified = await client.verify(subjects, tokens.access)\n```\n</Section>\n---\n## Methods\n### createClient\n<Segment>\n<Section type=\"signature\">\n```ts\ncreateClient(input)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">input</code> [<code class=\"type\">ClientInput</code>](#clientinput)</p>\nConfigure the client.\n</Section>\n<InlineSection>\n**Returns** [<code class=\"type\">Client</code>](#client)\n</InlineSection>\nCreate an OpenAuth client.\n</Segment>\n## Client\n<Segment>\nAn instance of the OpenAuth client contains the following methods.\n</Segment>\n### authorize\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(redirectURI: <code class=\"primitive\">string</code>, response: <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">token</code><code class=\"symbol\">&rdquo;</code>, opts?: [<code class=\"type\">AuthorizeOptions</code>](#authorizeoptions)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code>[<code class=\"type\">AuthorizeResult</code>](#authorizeresult)<code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nStart the autorization flow. For example, in SSR sites.\n\n```ts\nconst { url } = await client.authorize(<redirect_uri>, \"code\")\n```\n\nThis takes a redirect URI and the type of flow you want to use. The redirect URI is the\nlocation where the user will be redirected to after the flow is complete.\n\nSupports both the _code_ and _token_ flows. We recommend using the _code_ flow as it's more\nsecure.\n\n:::tip\nThis returns a URL to redirect the user to. This starts the OAuth flow.\n:::\n\nThis returns a URL to the auth server. You can redirect the user to the URL to start the\nOAuth flow.\n\nFor SPA apps, we recommend using the PKCE flow.\n\n```ts {4}\nconst { challenge, url } = await client.authorize(\n  <redirect_uri>,\n  \"code\",\n  { pkce: true }\n)\n```\n\nThis returns a redirect URL and a challenge that you need to use later to verify the code.\n</Segment>\n### exchange\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(code: <code class=\"primitive\">string</code>, redirectURI: <code class=\"primitive\">string</code>, verifier?: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code>[<code class=\"type\">ExchangeSuccess</code>](#exchangesuccess)<code class=\"symbol\"> | </code>[<code class=\"type\">ExchangeError</code>](#exchangeerror)<code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nExchange the code for access and refresh tokens.\n\n```ts\nconst exchanged = await client.exchange(<code>, <redirect_uri>)\n```\n\nYou call this after the user has been redirected back to your app after the OAuth flow.\n\n:::tip\nFor SSR sites, the code is returned in the query parameter.\n:::\n\nSo the code comes from the query parameter in the redirect URI. The redirect URI here is\nthe one that you passed in to the `authorize` call when starting the flow.\n\n:::tip\nFor SPA sites, the code is returned through the URL hash.\n:::\n\nIf you used the PKCE flow for an SPA app, the code is returned as a part of the redirect URL\nhash.\n\n```ts {4}\nconst exchanged = await client.exchange(\n  <code>,\n  <redirect_uri>,\n  <challenge.verifier>\n)\n```\n\nYou also need to pass in the previously stored challenge verifier.\n\nThis method returns the access and refresh tokens. Or if it fails, it returns an error that\nyou can handle depending on the error.\n\n```ts\nimport { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n\nif (exchanged.err) {\n  if (exchanged.err instanceof InvalidAuthorizationCodeError) {\n    // handle invalid code error\n  }\n  else {\n    // handle other errors\n  }\n}\n\nconst { access, refresh } = exchanged.tokens\n```\n</Segment>\n### refresh\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(refresh: <code class=\"primitive\">string</code>, opts?: [<code class=\"type\">RefreshOptions</code>](#refreshoptions)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code>[<code class=\"type\">RefreshSuccess</code>](#refreshsuccess)<code class=\"symbol\"> | </code>[<code class=\"type\">RefreshError</code>](#refresherror)<code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nRefreshes the tokens if they have expired. This is used in an SPA app to maintain the\nsession, without logging the user out.\n\n```ts\nconst next = await client.refresh(<refresh_token>)\n```\n\nCan optionally take the access token as well. If passed in, this will skip the refresh\nif the access token is still valid.\n\n```ts\nconst next = await client.refresh(<refresh_token>, { access: <access_token> })\n```\n\nThis returns the refreshed tokens only if they've been refreshed.\n\n```ts\nif (!next.err) {\n  // tokens are still valid\n}\nif (next.tokens) {\n  const { access, refresh } = next.tokens\n}\n```\n\nOr if it fails, it returns an error that you can handle depending on the error.\n\n```ts\nimport { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n\nif (next.err) {\n  if (next.err instanceof InvalidRefreshTokenError) {\n    // handle invalid refresh token error\n  }\n  else {\n    // handle other errors\n  }\n}\n```\n</Segment>\n### verify\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(subjects: [<code class=\"type\">SubjectSchema</code>](/docs/subject#subjectschema), token: <code class=\"primitive\">string</code>, options?: [<code class=\"type\">VerifyOptions</code>](#verifyoptions)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code>[<code class=\"type\">VerifyError</code>](#verifyerror)<code class=\"symbol\"> | </code>[<code class=\"type\">VerifyResult</code>](#verifyresult)<code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nVerify the token in the incoming request.\n\nThis is typically used for SSR sites where the token is stored in an HTTP only cookie. And\nis passed to the server on every request.\n\n```ts\nconst verified = await client.verify(<subjects>, <token>)\n```\n\nThis takes the subjects that you had previously defined when creating the issuer.\n\n:::tip\nIf the refresh token is passed in, it'll automatically refresh the access token.\n:::\n\nThis can optionally take the refresh token as well. If passed in, it'll automatically\nrefresh the access token if it has expired.\n\n```ts\nconst verified = await client.verify(<subjects>, <token>, { refresh: <refresh_token> })\n```\n\nThis returns the decoded subjects from the access token. And the tokens if they've been\nrefreshed.\n\n```ts\n// based on the subjects you defined earlier\nconsole.log(verified.subject.properties.userID)\n\nif (verified.tokens) {\n  const { access, refresh } = verified.tokens\n}\n```\n\nOr if it fails, it returns an error that you can handle depending on the error.\n\n```ts\nimport { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n\nif (verified.err) {\n  if (verified.err instanceof InvalidRefreshTokenError) {\n    // handle invalid refresh token error\n  }\n  else {\n    // handle other errors\n  }\n}\n```\n</Segment>\n## AuthorizeOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">pkce?</code>](#authorizeoptions.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">provider?</code>](#authorizeoptions.provider) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"authorizeoptions.pkce\" Tag=\"h4\" parent=\"AuthorizeOptions.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nEnable the PKCE flow. This is for SPA apps.\n\n```ts\n{\n  pkce: true\n}\n```\n</Segment>\n<NestedTitle id=\"authorizeoptions.provider\" Tag=\"h4\" parent=\"AuthorizeOptions.\">provider?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe provider you want to use for the OAuth flow.\n\n```ts\n{\n  provider: \"google\"\n}\n```\n\nIf no provider is specified, the user is directed to a page where they can select from the\nlist of configured providers.\n\nIf there's only one provider configured, the user will be redirected to that.\n</Segment>\n## AuthorizeResult\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">challenge</code>](#authorizeresult.challenge) [<code class=\"type\">Challenge</code>](#challenge)</p>\n- <p>[<code class=\"key\">url</code>](#authorizeresult.url) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"authorizeresult.challenge\" Tag=\"h4\" parent=\"AuthorizeResult.\">challenge</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">Challenge</code>](#challenge)\n</InlineSection>\n</Section>\nThe challenge that you can use to verify the code. This is for the PKCE flow for SPA apps.\n\nThis is an object that you _stringify_ and store it in session storage.\n\n```ts\nsessionStorage.setItem(\"challenge\", JSON.stringify(challenge))\n```\n</Segment>\n<NestedTitle id=\"authorizeresult.url\" Tag=\"h4\" parent=\"AuthorizeResult.\">url</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe URL to redirect the user to. This starts the OAuth flow.\n\nFor example, for SPA apps.\n\n```ts\nlocation.href = url\n```\n</Segment>\n## Challenge\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nThe challenge that you can use to verify the code.\n</Segment>\n## ClientInput\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#clientinput.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">fetch?</code>](#clientinput.fetch) <code class=\"type\">FetchLike</code></p>\n- <p>[<code class=\"key\">issuer?</code>](#clientinput.issuer) <code class=\"primitive\">string</code></p>\n</Section>\nConfigure the client.\n</Segment>\n<NestedTitle id=\"clientinput.clientid\" Tag=\"h4\" parent=\"ClientInput.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID. This is just a string to identify your app.\n\nIf you have a web app and a mobile app, you want to use different client IDs both.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"clientinput.fetch\" Tag=\"h4\" parent=\"ClientInput.\">fetch?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"type\">FetchLike</code>\n</InlineSection>\n</Section>\nOptionally, override the internally used fetch function.\n\nThis is useful if you are using a polyfilled fetch function in your application and you\nwant the client to use it too.\n</Segment>\n<NestedTitle id=\"clientinput.issuer\" Tag=\"h4\" parent=\"ClientInput.\">issuer?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe URL of your OpenAuth server.\n```ts\n{\n  issuer: \"https://auth.myserver.com\"\n}\n```\n</Segment>\n## ExchangeError\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err</code>](#exchangeerror.err) [<code class=\"type\">InvalidAuthorizationCodeError</code>](/docs/issuer#invalidauthorizationcodeerror)</p>\n</Section>\nReturned when the exchange fails.\n</Segment>\n<NestedTitle id=\"exchangeerror.err\" Tag=\"h4\" parent=\"ExchangeError.\">err</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">InvalidAuthorizationCodeError</code>](/docs/issuer#invalidauthorizationcodeerror)\n</InlineSection>\n</Section>\nThe type of error that occurred. You can handle this by checking the type.\n```ts\nimport { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n\nconsole.log(err instanceof InvalidAuthorizationCodeError)\n```\n</Segment>\n## ExchangeSuccess\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err</code>](#exchangesuccess.err) <code class=\"primitive\">false</code></p>\n- <p>[<code class=\"key\">tokens</code>](#exchangesuccess.tokens) [<code class=\"type\">Tokens</code>](#tokens)</p>\n</Section>\nReturned when the exchange is successful.\n</Segment>\n<NestedTitle id=\"exchangesuccess.err\" Tag=\"h4\" parent=\"ExchangeSuccess.\">err</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">false</code>\n</InlineSection>\n</Section>\nThis is always `false` when the exchange is successful.\n</Segment>\n<NestedTitle id=\"exchangesuccess.tokens\" Tag=\"h4\" parent=\"ExchangeSuccess.\">tokens</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">Tokens</code>](#tokens)\n</InlineSection>\n</Section>\nThe access and refresh tokens.\n</Segment>\n## RefreshError\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err</code>](#refresherror.err) [<code class=\"type\">InvalidRefreshTokenError</code>](/docs/issuer#invalidrefreshtokenerror)<code class=\"symbol\"> | </code>[<code class=\"type\">InvalidAccessTokenError</code>](/docs/issuer#invalidaccesstokenerror)</p>\n</Section>\nReturned when the refresh fails.\n</Segment>\n<NestedTitle id=\"refresherror.err\" Tag=\"h4\" parent=\"RefreshError.\">err</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">InvalidRefreshTokenError</code>](/docs/issuer#invalidrefreshtokenerror)<code class=\"symbol\"> | </code>[<code class=\"type\">InvalidAccessTokenError</code>](/docs/issuer#invalidaccesstokenerror)\n</InlineSection>\n</Section>\nThe type of error that occurred. You can handle this by checking the type.\n```ts\nimport { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n\nconsole.log(err instanceof InvalidRefreshTokenError)\n```\n</Segment>\n## RefreshOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">access?</code>](#refreshoptions.access) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"refreshoptions.access\" Tag=\"h4\" parent=\"RefreshOptions.\">access?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nOptionally, pass in the access token.\n</Segment>\n## RefreshSuccess\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err</code>](#refreshsuccess.err) <code class=\"primitive\">false</code></p>\n- <p>[<code class=\"key\">tokens?</code>](#refreshsuccess.tokens) [<code class=\"type\">Tokens</code>](#tokens)</p>\n</Section>\nReturned when the refresh is successful.\n</Segment>\n<NestedTitle id=\"refreshsuccess.err\" Tag=\"h4\" parent=\"RefreshSuccess.\">err</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">false</code>\n</InlineSection>\n</Section>\nThis is always `false` when the refresh is successful.\n</Segment>\n<NestedTitle id=\"refreshsuccess.tokens\" Tag=\"h4\" parent=\"RefreshSuccess.\">tokens?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">Tokens</code>](#tokens)\n</InlineSection>\n</Section>\nReturns the refreshed tokens only if they've been refreshed.\n\nIf they are still valid, this will be `undefined`.\n</Segment>\n## Tokens\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">access</code>](#tokens.access) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">expiresIn</code>](#tokens.expiresin) <code class=\"primitive\">number</code></p>\n- <p>[<code class=\"key\">refresh</code>](#tokens.refresh) <code class=\"primitive\">string</code></p>\n</Section>\nThe tokens returned by the auth server.\n</Segment>\n<NestedTitle id=\"tokens.access\" Tag=\"h4\" parent=\"Tokens.\">access</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe access token.\n</Segment>\n<NestedTitle id=\"tokens.expiresin\" Tag=\"h4\" parent=\"Tokens.\">expiresIn</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\nThe number of seconds until the access token expires.\n</Segment>\n<NestedTitle id=\"tokens.refresh\" Tag=\"h4\" parent=\"Tokens.\">refresh</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe refresh token.\n</Segment>\n## VerifyError\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err</code>](#verifyerror.err) [<code class=\"type\">InvalidRefreshTokenError</code>](/docs/issuer#invalidrefreshtokenerror)<code class=\"symbol\"> | </code>[<code class=\"type\">InvalidAccessTokenError</code>](/docs/issuer#invalidaccesstokenerror)</p>\n</Section>\nReturned when the verify call fails.\n</Segment>\n<NestedTitle id=\"verifyerror.err\" Tag=\"h4\" parent=\"VerifyError.\">err</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">InvalidRefreshTokenError</code>](/docs/issuer#invalidrefreshtokenerror)<code class=\"symbol\"> | </code>[<code class=\"type\">InvalidAccessTokenError</code>](/docs/issuer#invalidaccesstokenerror)\n</InlineSection>\n</Section>\nThe type of error that occurred. You can handle this by checking the type.\n```ts\nimport { InvalidRefreshTokenError } from \"@openauthjs/openauth/error\"\n\nconsole.log(err instanceof InvalidRefreshTokenError)\n```\n</Segment>\n## VerifyOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">fetch?</code>](#verifyoptions.fetch) <code class=\"type\">FetchLike</code></p>\n- <p>[<code class=\"key\">refresh?</code>](#verifyoptions.refresh) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"verifyoptions.fetch\" Tag=\"h4\" parent=\"VerifyOptions.\">fetch?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"type\">FetchLike</code>\n</InlineSection>\n</Section>\nOptionally, override the internally used fetch function.\n\nThis is useful if you are using a polyfilled fetch function in your application and you\nwant the client to use it too.\n</Segment>\n<NestedTitle id=\"verifyoptions.refresh\" Tag=\"h4\" parent=\"VerifyOptions.\">refresh?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nOptionally, pass in the refresh token.\n\nIf passed in, this will automatically refresh the access token if it has expired.\n</Segment>\n## VerifyResult\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">err?</code>](#verifyresult.err) <code class=\"primitive\">undefined</code></p>\n- <p>[<code class=\"key\">subject</code>](#verifyresult.subject) <code class=\"type\">Subject</code></p>\n- <p>[<code class=\"key\">tokens?</code>](#verifyresult.tokens) [<code class=\"type\">Tokens</code>](#tokens)</p>\n</Section>\n</Segment>\n<NestedTitle id=\"verifyresult.err\" Tag=\"h4\" parent=\"VerifyResult.\">err?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">undefined</code>\n</InlineSection>\n</Section>\nThis is always `undefined` when the verify is successful.\n</Segment>\n<NestedTitle id=\"verifyresult.subject\" Tag=\"h4\" parent=\"VerifyResult.\">subject</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"type\">Subject</code>\n</InlineSection>\n</Section>\nThe decoded subjects from the access token.\n\nHas the same shape as the subjects you defined when creating the issuer.\n</Segment>\n<NestedTitle id=\"verifyresult.tokens\" Tag=\"h4\" parent=\"VerifyResult.\">tokens?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">Tokens</code>](#tokens)\n</InlineSection>\n</Section>\nReturns the refreshed tokens only if they’ve been refreshed.\n\nIf they are still valid, this will be undefined.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/index.mdx",
    "content": "---\ntitle: OpenAuth\ndescription: Introduction to OpenAuth.\n---\n\nimport { Image } from \"astro:assets\"\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\nimport themeDark from \"./themes-dark.png\"\nimport themeLight from \"./themes-light.png\"\n\n[OpenAuth](/) 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<picture>\n  <source srcset={themeDark.src} media=\"(prefers-color-scheme: dark)\" />\n  <source srcset={themeLight.src} media=\"(prefers-color-scheme: light)\" />\n  <Image src={themeLight} alt=\"OpenAuth themes\" />\n</picture>\n\n:::tip\nCheck out the [launch video](https://www.youtube.com/watch?v=SSjNUuQ06tk) to learn more.\n:::\n\n---\n\n## Get started\n\nIf you just want to get started as fast as possible you can jump straight into the [code examples](https://github.com/openauthjs/openauthjs/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.\n\n---\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/password 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/creation 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://sst.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---\n\n## Overview\n\nWe'll show how to deploy the OpenAuth server and then a sample app that uses it.\n\n---\n\n### Server\n\nStart by importing the `issuer` function from the `@openauthjs/openauth` package.\n\n```ts\nimport { issuer } from \"@openauthjs/openauth\"\n```\n\nOpenAuth 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.\n\nThe `issuer` function requires a few things:\n\n```ts title=\"issuer.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/password or pin code. You can also implement your own. Let's try the GitHub provider.\n\n```ts title=\"issuer.ts\"\nimport { GithubProvider } from \"@openauthjs/openauth/provider/github\"\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 title=\"issuer.ts\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nconst app = issuer({\n  providers: {\n    github: ...,\n    password: PasswordProvider(...),\n  },\n  // ...\n})\n```\n\nThe 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.\n\n```ts title=\"issuer.ts\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\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 title=\"subjects.ts\"\nimport { object, string } from \"valibot\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string(),\n    // may want to add workspaceID here if doing a multi-tenant app\n    workspaceID: string(),\n  }),\n})\n```\n\nWe are using Valibot here to define and validate the shape of the subject. You can use any validation library that follows the [standard-schema specification](https://github.com/standard-schema/standard-schema), including Zod `^3.24.0` and Valibot `^1.0.0`. See the full list of compatible libraries [here](https://standardschema.dev/#what-schema-libraries-implement-the-spec).\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 title=\"issuer.ts\"\nimport { subjects } from \"./subjects.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 title=\"issuer.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 = ... // lookup user or create them\n    }\n    if (value.provider === \"github\") {\n      console.log(value.tokenset.access)\n      userID = ... // lookup user or create them\n    }\n    return ctx.subject(\"user\", {\n      userID,\n      'a workspace id'\n    })\n  }\n})\n```\n\nAll 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. 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 title=\"issuer.ts\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\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\n<Tabs>\n  <TabItem label=\"Node\">\n  ```ts title=\"issuer.ts\"\n  import { serve } from \"@hono/node-server\"\n\n  serve(app)\n  ```\n  </TabItem>\n  <TabItem label=\"Lambda\">\n  ```ts title=\"issuer.ts\"\n  import { handle } from \"hono/aws-lambda\"\n\n  export const handler = handle(app)\n  ```\n  </TabItem>\n  <TabItem label=\"Bun\">\n  ```ts title=\"issuer.ts\"\n  export default app\n  ```\n  </TabItem>\n  <TabItem label=\"Workers\">\n  ```ts title=\"issuer.ts\"\n  export default app\n  ```\n  </TabItem>\n</Tabs>\n\nYou now have a centralized OpenAuth server. Test it out by visiting `/.well-known/oauth-authorization-server` - you can see a live example [here](https://auth.terminal.shop/.well-known/oauth-authorization-server).\n\n---\n\n### 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 title=\"client.ts\"\nimport { createClient } from \"@openauthjs/openauth/client\"\n\nconst client = createClient({\n  clientID: \"my-client\",\n  issuer: \"https://auth.myserver.com\" // 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  <redirect-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// 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(<redirect_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/refresh 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://auth.example.com/api/user\", {\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"
  },
  {
    "path": "www/src/content/docs/docs/issuer.mdx",
    "content": "---\ntitle: Issuer\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/issuer.ts\ndescription: Reference doc for the OpenAuth server.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nThe `issuer` create an OpentAuth server, a [Hono](https://hono.dev) app that's\ndesigned to run anywhere.\n\nThe `issuer` function requires a few things:\n\n```ts title=\"issuer.ts\"\nimport { issuer } from \"@openauthjs/openauth\"\n\nconst app = issuer({\n  providers: { ... },\n  storage,\n  subjects,\n  success: async (ctx, value) => { ... }\n})\n```\n\n#### Add providers\n\nYou start by specifying the auth providers you are going to use. Let's say you want your users\nto be able to authenticate with GitHub and with their email and password.\n\n```ts title=\"issuer.ts\"\nimport { GithubProvider } from \"@openauthjs/openauth/provider/github\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nconst app = issuer({\n  providers: {\n    github: GithubProvider({\n      // ...\n    }),\n    password: PasswordProvider({\n      // ...\n    }),\n  },\n})\n```\n\n#### Handle success\n\nThe `success` callback receives the payload when a user completes a provider's auth flow.\n\n```ts title=\"issuer.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 = ... // lookup user or create them\n    }\n    if (value.provider === \"github\") {\n      console.log(value.tokenset.access)\n      userID = ... // lookup user or create them\n    }\n    return ctx.subject(\"user\", {\n      userID\n    })\n  }\n})\n```\n\nOnce complete, the `issuer` issues the access tokens that a client can use. The `ctx.subject`\ncall is what is placed in the access token as a JWT.\n\n#### Define subjects\n\nYou define the shape of these in the `subjects` field.\n\n```ts title=\"subjects.ts\"\nimport { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string()\n  })\n})\n```\n\nIt's good to place this in a separate file since this'll be used in your client apps as well.\n\n```ts title=\"issuer.ts\"\nimport { subjects } from \"./subjects.js\"\n\nconst app = issuer({\n  providers: { ... },\n  subjects,\n  // ...\n})\n```\n\n#### Deploy\n\nSince `issuer` is a Hono app, you can deploy it anywhere Hono supports.\n\n<Tabs>\n  <TabItem label=\"Node\">\n  ```ts title=\"issuer.ts\"\n  import { serve } from \"@hono/node-server\"\n\n  serve(app)\n  ```\n  </TabItem>\n  <TabItem label=\"Lambda\">\n  ```ts title=\"issuer.ts\"\n  import { handle } from \"hono/aws-lambda\"\n\n  export const handler = handle(app)\n  ```\n  </TabItem>\n  <TabItem label=\"Bun\">\n  ```ts title=\"issuer.ts\"\n  export default app\n  ```\n  </TabItem>\n  <TabItem label=\"Workers\">\n  ```ts title=\"issuer.ts\"\n  export default app\n  ```\n  </TabItem>\n</Tabs>\n</Section>\n---\n## Methods\n### issuer\n<Segment>\n<Section type=\"signature\">\n```ts\nissuer(input)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">input</code> [<code class=\"type\">IssuerInput</code>](#issuerinput)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Hono</code>\n</InlineSection>\nCreate an OpenAuth server, a Hono app.\n</Segment>\n## IssuerInput\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">providers</code>](#issuerinput.providers) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"type\">Provider</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">storage?</code>](#issuerinput.storage) <code class=\"type\">StorageAdapter</code></p>\n- <p>[<code class=\"key\">subjects</code>](#issuerinput.subjects) [<code class=\"type\">SubjectSchema</code>](/docs/subject#subjectschema)</p>\n- <p>[<code class=\"key\">theme?</code>](#issuerinput.theme) [<code class=\"type\">Theme</code>](#theme)</p>\n- <p>[<code class=\"key\">ttl?</code>](#issuerinput.ttl) <code class=\"primitive\">Object</code></p>\n  - <p>[<code class=\"key\">access?</code>](#ttl.access) <code class=\"primitive\">number</code></p>\n  - <p>[<code class=\"key\">refresh?</code>](#ttl.refresh) <code class=\"primitive\">number</code></p>\n  - <p>[<code class=\"key\">retention?</code>](#ttl.retention) <code class=\"primitive\">number</code></p>\n  - <p>[<code class=\"key\">reuse?</code>](#ttl.reuse) <code class=\"primitive\">number</code></p>\n- <p>[<code class=\"key\">allow?</code>](#issuerinput.allow) <code class=\"primitive\">(input: <code class=\"symbol\">&lcub; </code><code class=\"key\">audience</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">clientID</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">redirectURI</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\"> &rcub;</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">boolean</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">select?</code>](#issuerinput.select) <code class=\"primitive\">(providers: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">success</code>](#issuerinput.success) <code class=\"primitive\">(response: [<code class=\"type\">OnSuccessResponder</code>](#onsuccessresponder), input: <code class=\"type\">Result</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"issuerinput.providers\" Tag=\"h4\" parent=\"IssuerInput.\">providers</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"type\">Provider</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nThe providers that you want your OpenAuth server to support.\n```ts title=\"issuer.ts\"\nimport { GithubProvider } from \"@openauthjs/openauth/provider/github\"\n\nissuer({\n  providers: {\n    github: GithubProvider()\n  }\n})\n```\n\n\nThe key is just a string that you can use to identify the provider. It's passed back to\nthe \n`success`\n callback.\n\nYou can also specify multiple providers.\n\n\n```ts\n{\n  providers: {\n    github: GithubProvider(),\n    google: GoogleProvider()\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"issuerinput.storage\" Tag=\"h4\" parent=\"IssuerInput.\">storage?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"type\">StorageAdapter</code>\n</InlineSection>\n</Section>\nThe storage adapter that you want to use.\n```ts title=\"issuer.ts\"\nimport { DynamoStorage } from \"@openauthjs/openauth/storage/dynamo\"\n\nissuer({\n  storage: DynamoStorage()\n  // ...\n})\n```\n</Segment>\n<NestedTitle id=\"issuerinput.subjects\" Tag=\"h4\" parent=\"IssuerInput.\">subjects</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">SubjectSchema</code>](/docs/subject#subjectschema)\n</InlineSection>\n</Section>\nThe shape of the subjects that you want to return.\n```ts title=\"issuer.ts\"\nimport { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nissuer({\n  subjects: createSubjects({\n    user: object({\n      userID: string()\n    })\n  })\n  // ...\n})\n```\n</Segment>\n<NestedTitle id=\"issuerinput.theme\" Tag=\"h4\" parent=\"IssuerInput.\">theme?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">Theme</code>](#theme)\n</InlineSection>\n</Section>\nThe theme you want to use for the UI.\n\nThis includes the UI the user sees when selecting a provider. And the `PasswordUI` and\n`CodeUI` that are used by the `PasswordProvider` and `CodeProvider`.\n```ts title=\"issuer.ts\"\nimport { THEME_SST } from \"@openauthjs/openauth/ui/theme\"\n\nissuer({\n  theme: THEME_SST\n  // ...\n})\n```\n\n\nOr define your own.\n\n\n```ts title=\"issuer.ts\"\nimport type { Theme } from \"@openauthjs/openauth/ui/theme\"\n\nconst MY_THEME: Theme = {\n  // ...\n}\n\nissuer({\n  theme: MY_THEME\n  // ...\n})\n```\n</Segment>\n<NestedTitle id=\"issuerinput.ttl\" Tag=\"h4\" parent=\"IssuerInput.\">ttl?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nSet the TTL, in seconds, for access and refresh tokens.\n```ts\n{\n  ttl: {\n    access: 60 * 60 * 24 * 30,\n    refresh: 60 * 60 * 24 * 365\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"ttl.access\" Tag=\"h5\" parent=\"IssuerInput.ttl.\">access?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** 30d\n</InlineSection>\nInterval in seconds where the access token is valid.\n</Segment>\n<NestedTitle id=\"ttl.refresh\" Tag=\"h5\" parent=\"IssuerInput.ttl.\">refresh?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** 1y\n</InlineSection>\nInterval in seconds where the refresh token is valid.\n</Segment>\n<NestedTitle id=\"ttl.retention\" Tag=\"h5\" parent=\"IssuerInput.ttl.\">retention?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** 0s\n</InlineSection>\nInterval in seconds to retain refresh tokens for reuse detection.\n</Segment>\n<NestedTitle id=\"ttl.reuse\" Tag=\"h5\" parent=\"IssuerInput.ttl.\">reuse?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** 60s\n</InlineSection>\nInterval in seconds where refresh token reuse is allowed. This helps mitigrate\nconcurrency issues.\n</Segment>\n<NestedTitle id=\"issuerinput.allow\" Tag=\"h4\" parent=\"IssuerInput.\">allow?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(input: <code class=\"symbol\">&lcub; </code><code class=\"key\">audience</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">clientID</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">redirectURI</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\"> &rcub;</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">boolean</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nOverride the logic for whether a client request is allowed to call the issuer.\n\nBy default, it uses the following:\n\n- Allow if the `redirectURI` is localhost.\n- Compare `redirectURI` to the request's hostname or the `x-forwarded-host` header. If they\n  are from the same sub-domain level, then allow.\n```ts\n{\n  allow: async (input, req) => {\n    // Allow all clients\n    return true\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"issuerinput.select\" Tag=\"h4\" parent=\"IssuerInput.\">select?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(providers: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** Select()\n</InlineSection>\nOptionally, configure the UI that's displayed when the user visits the root URL of the\nof the OpenAuth server.\n\n```ts title=\"issuer.ts\"\nimport { Select } from \"@openauthjs/openauth/ui/select\"\n\nissuer({\n  select: Select({\n    providers: {\n      github: { hide: true },\n      google: { display: \"Google\" }\n    }\n  })\n  // ...\n})\n```\n</Segment>\n<NestedTitle id=\"issuerinput.success\" Tag=\"h4\" parent=\"IssuerInput.\">success</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(response: [<code class=\"type\">OnSuccessResponder</code>](#onsuccessresponder), input: <code class=\"type\">Result</code>, req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe success callback that's called when the user completes the flow.\n\nThis is called after the user has been redirected back to your app after the OAuth flow.\n```ts\n{\n  success: async (ctx, value) => {\n    let userID\n    if (value.provider === \"password\") {\n      console.log(value.email)\n      userID = ... // lookup user or create them\n    }\n    if (value.provider === \"github\") {\n      console.log(value.tokenset.access)\n      userID = ... // lookup user or create them\n    }\n    return ctx.subject(\"user\", {\n      userID\n    })\n  },\n  // ...\n}\n```\n</Segment>\n## OnSuccessResponder\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">subject</code>](#onsuccessresponder.subject) <code class=\"primitive\">(type: <code class=\"primitive\">string</code>, properties: <code class=\"primitive\">any</code>, opts?: <code class=\"symbol\">&lcub; </code><code class=\"key\">subject</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">ttl</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&lcub; </code><code class=\"key\">access</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">number</code><code class=\"symbol\">, </code><code class=\"key\">refresh</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">number</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> &rcub;</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n</Section>\nSets the subject payload in the JWT token and returns the response.\n\n```ts\nctx.subject(\"user\", {\n  userID\n})\n```\n</Segment>\n<NestedTitle id=\"onsuccessresponder.subject\" Tag=\"h4\" parent=\"OnSuccessResponder.\">subject</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(type: <code class=\"primitive\">string</code>, properties: <code class=\"primitive\">any</code>, opts?: <code class=\"symbol\">&lcub; </code><code class=\"key\">subject</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">ttl</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&lcub; </code><code class=\"key\">access</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">number</code><code class=\"symbol\">, </code><code class=\"key\">refresh</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">number</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> &rcub;</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe `type` is the type of the subject, that was defined in the `subjects` field.\n\nThe `properties` are the properties of the subject. This is the shape of the subject that\nyou defined in the `subjects` field.\n</Segment>\n## Errors\n<Section type=\"about\">\nA list of errors that can be thrown by OpenAuth.\n\nYou can use these errors to check the type of error and handle it. For example.\n\n```ts\nimport { InvalidAuthorizationCodeError } from \"@openauthjs/openauth/error\"\n\nif (err instanceof InvalidAuthorizationCodeError) {\n  // handle invalid code error\n}\n```\n</Section>\n---\n### InvalidAccessTokenError\n<Segment>\nThe given access token is invalid.\n</Segment>\n### InvalidAuthorizationCodeError\n<Segment>\nThe given authorization code is invalid.\n</Segment>\n### InvalidRefreshTokenError\n<Segment>\nThe given refresh token is invalid.\n</Segment>\n### InvalidSubjectError\n<Segment>\nThe given subject is invalid.\n</Segment>\n### MissingParameterError\n<Segment>\nThe given parameter is missing.\n</Segment>\n### MissingProviderError\n<Segment>\nThe `provider` needs to be passed in.\n</Segment>\n### OauthError\n<Segment>\nThe OAuth server returned an error.\n</Segment>\n### UnauthorizedClientError\n<Segment>\nThe given client is not authorized to use the redirect URI that was passed in.\n</Segment>\n### UnknownStateError\n<Segment>\nThe browser was in an unknown state.\n\nThis can happen when certain cookies have expired. Or the browser was switched in the middle\nof the authentication flow.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/apple.mdx",
    "content": "---\ntitle: AppleProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/apple.ts\ndescription: Reference doc for the `AppleProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Apple. Supports both OAuth2 and OIDC.\n\n#### Using OAuth\n\n```ts {5-8}\nimport { AppleProvider } from \"@openauthjs/openauth/provider/apple\"\n\nexport default issuer({\n  providers: {\n    apple: AppleProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n\n#### Using OIDC\n\n```ts {5-7}\nimport { AppleOidcProvider } from \"@openauthjs/openauth/provider/apple\"\n\nexport default issuer({\n  providers: {\n    apple: AppleOidcProvider({\n      clientID: \"1234567890\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### AppleOidcProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nAppleOidcProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">AppleOidcConfig</code>](/docs/provider/apple#appleoidcconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate an Apple OIDC provider.\n\nThis is useful if you just want to verify the user's email address.\n```ts\nAppleOidcProvider({\n  clientID: \"1234567890\"\n})\n```\n</Segment>\n### AppleProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nAppleProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">AppleConfig</code>](/docs/provider/apple#appleconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate an Apple OAuth2 provider.\n```ts\nAppleProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## AppleConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#appleconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#appleconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#appleconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#appleconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#appleconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"appleconfig.clientid\" Tag=\"h4\" parent=\"AppleConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"appleconfig.clientsecret\" Tag=\"h4\" parent=\"AppleConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"appleconfig.pkce\" Tag=\"h4\" parent=\"AppleConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"appleconfig.query\" Tag=\"h4\" parent=\"AppleConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"appleconfig.scopes\" Tag=\"h4\" parent=\"AppleConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n## AppleOidcConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#appleoidcconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">query?</code>](#appleoidcconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes?</code>](#appleoidcconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"appleoidcconfig.clientid\" Tag=\"h4\" parent=\"AppleOidcConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"appleoidcconfig.query\" Tag=\"h4\" parent=\"AppleOidcConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"appleoidcconfig.scopes\" Tag=\"h4\" parent=\"AppleOidcConfig.\">scopes?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OIDC scopes that you want to request.\n```ts\n{\n  scopes: [\"openid\", \"profile\", \"email\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/code.mdx",
    "content": "---\ntitle: CodeProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/code.ts\ndescription: Reference doc for the `CodeProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigures a provider that supports pin code authentication. This is usually paired with the\n`CodeUI`.\n\n```ts\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\n\nexport default issuer({\n  providers: {\n    code: CodeProvider(\n      CodeUI({\n        copy: {\n          code_info: \"We'll send a pin code to your email\"\n        },\n        sendCode: (claims, code) => console.log(claims.email, code)\n      })\n    )\n  },\n  // ...\n})\n```\n\nYou can customize the provider using.\n\n```ts {7-9}\nconst ui = CodeUI({\n  // ...\n})\n\nexport default issuer({\n  providers: {\n    code: CodeProvider(\n      { ...ui, length: 4 }\n    )\n  },\n  // ...\n})\n```\n\nBehind the scenes, the `CodeProvider` expects callbacks that implements request handlers\nthat generate the UI for the following.\n\n```ts\nCodeProvider({\n  // ...\n  request: (req, state, form, error) => Promise<Response>\n})\n```\n\nThis allows you to create your own UI.\n</Section>\n---\n## Methods\n### CodeProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nCodeProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">CodeProviderConfig</code>](/docs/provider/code#codeproviderconfig)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\n</Segment>\n## CodeProviderConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">length?</code>](#codeproviderconfig.length) <code class=\"primitive\">number</code></p>\n- <p>[<code class=\"key\">request</code>](#codeproviderconfig.request) <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">CodeProviderState</code>](/docs/provider/code#codeproviderstate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">CodeProviderError</code>](/docs/provider/code#codeprovidererror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">sendCode</code>](#codeproviderconfig.sendcode) <code class=\"primitive\">(claims: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\"> | </code>[<code class=\"type\">CodeProviderError</code>](/docs/provider/code#codeprovidererror)<code class=\"symbol\">&gt;</code></code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"codeproviderconfig.length\" Tag=\"h4\" parent=\"CodeProviderConfig.\">length?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">number</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** 6\n</InlineSection>\nThe length of the pin code.\n</Segment>\n<NestedTitle id=\"codeproviderconfig.request\" Tag=\"h4\" parent=\"CodeProviderConfig.\">request</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">CodeProviderState</code>](/docs/provider/code#codeproviderstate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">CodeProviderError</code>](/docs/provider/code#codeprovidererror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe request handler to generate the UI for the code flow.\n\nTakes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\nand optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\nojects.\n\nAlso passes in the current `state` of the flow and any `error` that occurred.\n\nExpects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\nin return.\n</Segment>\n<NestedTitle id=\"codeproviderconfig.sendcode\" Tag=\"h4\" parent=\"CodeProviderConfig.\">sendCode</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(claims: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\"> | </code>[<code class=\"type\">CodeProviderError</code>](/docs/provider/code#codeprovidererror)<code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to send the pin code to the user.\n```ts\n{\n  sendCode: async (claims, code) => {\n    // Send the code through the email or phone number based on the claims\n  }\n}\n```\n</Segment>\n## CodeProviderError\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">key</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_claim</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\">, </code><code class=\"key\">value</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe errors that can happen on the code flow.\n\n| Error | Description |\n| ----- | ----------- |\n| `invalid_code` | The code is invalid. |\n| `invalid_claim` | The _claim_, email or phone number, is invalid. |\n</Segment>\n## CodeProviderState\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">start</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">claims</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code><code class=\"symbol\">, </code><code class=\"key\">code</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">resend</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">boolean</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe state of the code flow.\n\n| State | Description |\n| ----- | ----------- |\n| `start` | The user is asked to enter their email address or phone number to start the flow. |\n| `code` | The user needs to enter the pin code to verify their _claim_. |\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/cognito.mdx",
    "content": "---\ntitle: CognitoProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/cognito.ts\ndescription: Reference doc for the `CognitoProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with a Cognito OAuth endpoint.\n\n```ts {5-10}\nimport { CognitoProvider } from \"@openauthjs/openauth/provider/cognito\"\n\nexport default issuer({\n  providers: {\n    cognito: CognitoProvider({\n      domain: \"your-domain.auth.us-east-1.amazoncognito.com\",\n      region: \"us-east-1\",\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### CognitoProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nCognitoProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">CognitoConfig</code>](/docs/provider/cognito#cognitoconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Cognito OAuth2 provider.\n```ts\nCognitoProvider({\n  domain: \"your-domain.auth.us-east-1.amazoncognito.com\",\n  region: \"us-east-1\",\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## CognitoConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#cognitoconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#cognitoconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">domain</code>](#cognitoconfig.domain) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#cognitoconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#cognitoconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">region</code>](#cognitoconfig.region) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">scopes</code>](#cognitoconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"cognitoconfig.clientid\" Tag=\"h4\" parent=\"CognitoConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"cognitoconfig.clientsecret\" Tag=\"h4\" parent=\"CognitoConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"cognitoconfig.domain\" Tag=\"h4\" parent=\"CognitoConfig.\">domain</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe domain of the Cognito User Pool.\n```ts\n{\n  domain: \"your-domain.auth.us-east-1.amazoncognito.com\"\n}\n```\n</Segment>\n<NestedTitle id=\"cognitoconfig.pkce\" Tag=\"h4\" parent=\"CognitoConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"cognitoconfig.query\" Tag=\"h4\" parent=\"CognitoConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"cognitoconfig.region\" Tag=\"h4\" parent=\"CognitoConfig.\">region</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe region the Cognito User Pool is in.\n```ts\n{\n  region: \"us-east-1\"\n}\n```\n</Segment>\n<NestedTitle id=\"cognitoconfig.scopes\" Tag=\"h4\" parent=\"CognitoConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/discord.mdx",
    "content": "---\ntitle: DiscordProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/discord.ts\ndescription: Reference doc for the `DiscordProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Discord.\n\n```ts {5-8}\nimport { DiscordProvider } from \"@openauthjs/openauth/provider/discord\"\n\nexport default issuer({\n  providers: {\n    discord: DiscordProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### DiscordProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nDiscordProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">DiscordConfig</code>](/docs/provider/discord#discordconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Discord OAuth2 provider.\n```ts\nDiscordProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## DiscordConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#discordconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#discordconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#discordconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#discordconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#discordconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"discordconfig.clientid\" Tag=\"h4\" parent=\"DiscordConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"discordconfig.clientsecret\" Tag=\"h4\" parent=\"DiscordConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"discordconfig.pkce\" Tag=\"h4\" parent=\"DiscordConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"discordconfig.query\" Tag=\"h4\" parent=\"DiscordConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"discordconfig.scopes\" Tag=\"h4\" parent=\"DiscordConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/facebook.mdx",
    "content": "---\ntitle: FacebookProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/facebook.ts\ndescription: Reference doc for the `FacebookProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Facebook. Supports both OAuth2 and OIDC.\n\n#### Using OAuth\n\n```ts {5-8}\nimport { FacebookProvider } from \"@openauthjs/openauth/provider/facebook\"\n\nexport default issuer({\n  providers: {\n    facebook: FacebookProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n\n#### Using OIDC\n\n```ts {5-7}\nimport { FacebookOidcProvider } from \"@openauthjs/openauth/provider/facebook\"\n\nexport default issuer({\n  providers: {\n    facebook: FacebookOidcProvider({\n      clientID: \"1234567890\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### FacebookOidcProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nFacebookOidcProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">FacebookOidcConfig</code>](/docs/provider/facebook#facebookoidcconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Facebook OIDC provider.\n\nThis is useful if you just want to verify the user's email address.\n```ts\nFacebookOidcProvider({\n  clientID: \"1234567890\"\n})\n```\n</Segment>\n### FacebookProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nFacebookProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">FacebookConfig</code>](/docs/provider/facebook#facebookconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Facebook OAuth2 provider.\n```ts\nFacebookProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## FacebookConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#facebookconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#facebookconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#facebookconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#facebookconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#facebookconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"facebookconfig.clientid\" Tag=\"h4\" parent=\"FacebookConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"facebookconfig.clientsecret\" Tag=\"h4\" parent=\"FacebookConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"facebookconfig.pkce\" Tag=\"h4\" parent=\"FacebookConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"facebookconfig.query\" Tag=\"h4\" parent=\"FacebookConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"facebookconfig.scopes\" Tag=\"h4\" parent=\"FacebookConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n## FacebookOidcConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#facebookoidcconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">query?</code>](#facebookoidcconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes?</code>](#facebookoidcconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"facebookoidcconfig.clientid\" Tag=\"h4\" parent=\"FacebookOidcConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"facebookoidcconfig.query\" Tag=\"h4\" parent=\"FacebookOidcConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"facebookoidcconfig.scopes\" Tag=\"h4\" parent=\"FacebookOidcConfig.\">scopes?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OIDC scopes that you want to request.\n```ts\n{\n  scopes: [\"openid\", \"profile\", \"email\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/github.mdx",
    "content": "---\ntitle: GithubProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/github.ts\ndescription: Reference doc for the `GithubProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Github.\n\n```ts {5-8}\nimport { GithubProvider } from \"@openauthjs/openauth/provider/github\"\n\nexport default issuer({\n  providers: {\n    github: GithubProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### GithubProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nGithubProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">GithubConfig</code>](/docs/provider/github#githubconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Github OAuth2 provider.\n```ts\nGithubProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## GithubConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#githubconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#githubconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#githubconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#githubconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#githubconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"githubconfig.clientid\" Tag=\"h4\" parent=\"GithubConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"githubconfig.clientsecret\" Tag=\"h4\" parent=\"GithubConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"githubconfig.pkce\" Tag=\"h4\" parent=\"GithubConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"githubconfig.query\" Tag=\"h4\" parent=\"GithubConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"githubconfig.scopes\" Tag=\"h4\" parent=\"GithubConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/google.mdx",
    "content": "---\ntitle: GoogleProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/google.ts\ndescription: Reference doc for the `GoogleProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Google. Supports both OAuth2 and OIDC.\n\n#### Using OAuth\n\n```ts {5-8}\nimport { GoogleProvider } from \"@openauthjs/openauth/provider/google\"\n\nexport default issuer({\n  providers: {\n    google: GoogleProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n\n#### Using OIDC\n\n```ts {5-7}\nimport { GoogleOidcProvider } from \"@openauthjs/openauth/provider/google\"\n\nexport default issuer({\n  providers: {\n    google: GoogleOidcProvider({\n      clientID: \"1234567890\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### GoogleOidcProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nGoogleOidcProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">GoogleOidcConfig</code>](/docs/provider/google#googleoidcconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Google OIDC provider.\n\nThis is useful if you just want to verify the user's email address.\n```ts\nGoogleOidcProvider({\n  clientID: \"1234567890\"\n})\n```\n</Segment>\n### GoogleProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nGoogleProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">GoogleConfig</code>](/docs/provider/google#googleconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Google OAuth2 provider.\n```ts\nGoogleProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## GoogleConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#googleconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#googleconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#googleconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#googleconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#googleconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"googleconfig.clientid\" Tag=\"h4\" parent=\"GoogleConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"googleconfig.clientsecret\" Tag=\"h4\" parent=\"GoogleConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"googleconfig.pkce\" Tag=\"h4\" parent=\"GoogleConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"googleconfig.query\" Tag=\"h4\" parent=\"GoogleConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"googleconfig.scopes\" Tag=\"h4\" parent=\"GoogleConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n## GoogleOidcConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#googleoidcconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">query?</code>](#googleoidcconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes?</code>](#googleoidcconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"googleoidcconfig.clientid\" Tag=\"h4\" parent=\"GoogleOidcConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"googleoidcconfig.query\" Tag=\"h4\" parent=\"GoogleOidcConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"googleoidcconfig.scopes\" Tag=\"h4\" parent=\"GoogleOidcConfig.\">scopes?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OIDC scopes that you want to request.\n```ts\n{\n  scopes: [\"openid\", \"profile\", \"email\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/jumpcloud.mdx",
    "content": "---\ntitle: JumpCloudProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/jumpcloud.ts\ndescription: Reference doc for the `JumpCloudProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with JumpCloud.\n\n```ts {5-8}\nimport { JumpCloudProvider } from \"@openauthjs/openauth/provider/jumpcloud\"\n\nexport default issuer({\n  providers: {\n    jumpcloud: JumpCloudProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### JumpCloudProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nJumpCloudProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">JumpCloudConfig</code>](/docs/provider/jumpcloud#jumpcloudconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a JumpCloud OAuth2 provider.\n```ts\nJumpCloudProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## JumpCloudConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#jumpcloudconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#jumpcloudconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#jumpcloudconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#jumpcloudconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#jumpcloudconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"jumpcloudconfig.clientid\" Tag=\"h4\" parent=\"JumpCloudConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"jumpcloudconfig.clientsecret\" Tag=\"h4\" parent=\"JumpCloudConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"jumpcloudconfig.pkce\" Tag=\"h4\" parent=\"JumpCloudConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"jumpcloudconfig.query\" Tag=\"h4\" parent=\"JumpCloudConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"jumpcloudconfig.scopes\" Tag=\"h4\" parent=\"JumpCloudConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/keycloak.mdx",
    "content": "---\ntitle: KeycloakProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/keycloak.ts\ndescription: Reference doc for the `KeycloakProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with a Keycloak server.\n\n```ts {5-10}\nimport { KeycloakProvider } from \"@openauthjs/openauth/provider/keycloak\"\n\nexport default issuer({\n  providers: {\n    keycloak: KeycloakProvider({\n      baseUrl: \"https://your-keycloak-domain\",\n      realm: \"your-realm\",\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### KeycloakProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nKeycloakProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">KeycloakConfig</code>](/docs/provider/keycloak#keycloakconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Keycloak OAuth2 provider.\n```ts\nKeycloakProvider({\n  baseUrl: \"https://your-keycloak-domain\",\n  realm: \"your-realm\",\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## KeycloakConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">baseUrl</code>](#keycloakconfig.baseurl) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientID</code>](#keycloakconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#keycloakconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#keycloakconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#keycloakconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">realm</code>](#keycloakconfig.realm) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">scopes</code>](#keycloakconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"keycloakconfig.baseurl\" Tag=\"h4\" parent=\"KeycloakConfig.\">baseUrl</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe base URL of the Keycloak server.\n```ts\n{\n  baseUrl: \"https://your-keycloak-domain\"\n}\n```\n</Segment>\n<NestedTitle id=\"keycloakconfig.clientid\" Tag=\"h4\" parent=\"KeycloakConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"keycloakconfig.clientsecret\" Tag=\"h4\" parent=\"KeycloakConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"keycloakconfig.pkce\" Tag=\"h4\" parent=\"KeycloakConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"keycloakconfig.query\" Tag=\"h4\" parent=\"KeycloakConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"keycloakconfig.realm\" Tag=\"h4\" parent=\"KeycloakConfig.\">realm</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe realm in the Keycloak server to authenticate against.\n\nA realm in Keycloak is like a tenant or namespace that manages a set of\nusers, credentials, roles, and groups.\n```ts\n{\n  realm: \"your-realm\"\n}\n```\n</Segment>\n<NestedTitle id=\"keycloakconfig.scopes\" Tag=\"h4\" parent=\"KeycloakConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/microsoft.mdx",
    "content": "---\ntitle: MicrosoftProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/microsoft.ts\ndescription: Reference doc for the `MicrosoftProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Microsoft. Supports both OAuth2 and OIDC.\n\n#### Using OAuth\n\n```ts {5-9}\nimport { MicrosoftProvider } from \"@openauthjs/openauth/provider/microsoft\"\n\nexport default issuer({\n  providers: {\n    microsoft: MicrosoftProvider({\n      tenant: \"1234567890\",\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n\n#### Using OIDC\n\n```ts {5-7}\nimport { MicrosoftOidcProvider } from \"@openauthjs/openauth/provider/microsoft\"\n\nexport default issuer({\n  providers: {\n    microsoft: MicrosoftOidcProvider({\n      clientID: \"1234567890\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### MicrosoftOidcProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nMicrosoftOidcProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">MicrosoftOidcConfig</code>](/docs/provider/microsoft#microsoftoidcconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Microsoft OIDC provider.\n\nThis is useful if you just want to verify the user's email address.\n```ts\nMicrosoftOidcProvider({\n  clientID: \"1234567890\"\n})\n```\n</Segment>\n### MicrosoftProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nMicrosoftProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">MicrosoftConfig</code>](/docs/provider/microsoft#microsoftconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Microsoft OAuth2 provider.\n```ts\nMicrosoftProvider({\n  tenant: \"1234567890\",\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## MicrosoftConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#microsoftconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#microsoftconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#microsoftconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#microsoftconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#microsoftconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n- <p>[<code class=\"key\">tenant</code>](#microsoftconfig.tenant) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"microsoftconfig.clientid\" Tag=\"h4\" parent=\"MicrosoftConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftconfig.clientsecret\" Tag=\"h4\" parent=\"MicrosoftConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftconfig.pkce\" Tag=\"h4\" parent=\"MicrosoftConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"microsoftconfig.query\" Tag=\"h4\" parent=\"MicrosoftConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftconfig.scopes\" Tag=\"h4\" parent=\"MicrosoftConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftconfig.tenant\" Tag=\"h4\" parent=\"MicrosoftConfig.\">tenant</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe tenant ID of the Microsoft account.\n\nThis is usually the same as the client ID.\n```ts\n{\n  tenant: \"1234567890\"\n}\n```\n</Segment>\n## MicrosoftOidcConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#microsoftoidcconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">query?</code>](#microsoftoidcconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes?</code>](#microsoftoidcconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"microsoftoidcconfig.clientid\" Tag=\"h4\" parent=\"MicrosoftOidcConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftoidcconfig.query\" Tag=\"h4\" parent=\"MicrosoftOidcConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"microsoftoidcconfig.scopes\" Tag=\"h4\" parent=\"MicrosoftOidcConfig.\">scopes?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OIDC scopes that you want to request.\n```ts\n{\n  scopes: [\"openid\", \"profile\", \"email\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/oauth2.mdx",
    "content": "---\ntitle: Oauth2Provider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/oauth2.ts\ndescription: Reference doc for the `Oauth2Provider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this to connect authentication providers that support OAuth 2.0.\n\n```ts {5-12}\nimport { Oauth2Provider } from \"@openauthjs/openauth/provider/oauth2\"\n\nexport default issuer({\n  providers: {\n    oauth2: Oauth2Provider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\",\n      endpoint: {\n        authorization: \"https://auth.myserver.com/authorize\",\n        token: \"https://auth.myserver.com/token\"\n      }\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### Oauth2Provider\n<Segment>\n<Section type=\"signature\">\n```ts\nOauth2Provider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">Oauth2Config</code>](/docs/provider/oauth2#oauth2config)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\n</Segment>\n## Oauth2Config\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#oauth2config.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#oauth2config.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">endpoint</code>](#oauth2config.endpoint) <code class=\"primitive\">Object</code></p>\n  - <p>[<code class=\"key\">authorization</code>](#endpoint.authorization) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">token</code>](#endpoint.token) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#oauth2config.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#oauth2config.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#oauth2config.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"oauth2config.clientid\" Tag=\"h4\" parent=\"Oauth2Config.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"oauth2config.clientsecret\" Tag=\"h4\" parent=\"Oauth2Config.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"oauth2config.endpoint\" Tag=\"h4\" parent=\"Oauth2Config.\">endpoint</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nThe URLs of the authorization and token endpoints.\n```ts\n{\n  endpoint: {\n    authorization: \"https://auth.myserver.com/authorize\",\n    token: \"https://auth.myserver.com/token\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"endpoint.authorization\" Tag=\"h5\" parent=\"Oauth2Config.endpoint.\">authorization</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe URL of the authorization endpoint.\n</Segment>\n<NestedTitle id=\"endpoint.token\" Tag=\"h5\" parent=\"Oauth2Config.endpoint.\">token</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe URL of the token endpoint.\n</Segment>\n<NestedTitle id=\"oauth2config.pkce\" Tag=\"h4\" parent=\"Oauth2Config.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"oauth2config.query\" Tag=\"h4\" parent=\"Oauth2Config.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"oauth2config.scopes\" Tag=\"h4\" parent=\"Oauth2Config.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/oidc.mdx",
    "content": "---\ntitle: OidcProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/oidc.ts\ndescription: Reference doc for the `OidcProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this to connect authentication providers that support OIDC.\n\n```ts {5-8}\nimport { OidcProvider } from \"@openauthjs/openauth/provider/oidc\"\n\nexport default issuer({\n  providers: {\n    oauth2: OidcProvider({\n      clientId: \"1234567890\",\n      issuer: \"https://auth.myserver.com\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### OidcProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nOidcProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">OidcConfig</code>](/docs/provider/oidc#oidcconfig)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\n</Segment>\n## OidcConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#oidcconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">issuer</code>](#oidcconfig.issuer) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">query?</code>](#oidcconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes?</code>](#oidcconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"oidcconfig.clientid\" Tag=\"h4\" parent=\"OidcConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"oidcconfig.issuer\" Tag=\"h4\" parent=\"OidcConfig.\">issuer</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe URL of your authorization server.\n```ts\n{\n  issuer: \"https://auth.myserver.com\"\n}\n```\n</Segment>\n<NestedTitle id=\"oidcconfig.query\" Tag=\"h4\" parent=\"OidcConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"oidcconfig.scopes\" Tag=\"h4\" parent=\"OidcConfig.\">scopes?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OIDC scopes that you want to request.\n```ts\n{\n  scopes: [\"openid\", \"profile\", \"email\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/password.mdx",
    "content": "---\ntitle: PasswordProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/password.ts\ndescription: Reference doc for the `PasswordProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigures a provider that supports username and password authentication. This is usually\npaired with the `PasswordUI`.\n\n```ts\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nexport default issuer({\n  providers: {\n    password: PasswordProvider(\n      PasswordUI({\n        copy: {\n          error_email_taken: \"This email is already taken.\"\n        },\n        sendCode: (email, code) => console.log(email, code)\n      })\n    )\n  },\n  // ...\n})\n```\n\nBehind the scenes, the `PasswordProvider` expects callbacks that implements request handlers\nthat generate the UI for the following.\n\n```ts\nPasswordProvider({\n  // ...\n  login: (req, form, error) => Promise<Response>\n  register: (req, state, form, error) => Promise<Response>\n  change: (req, state, form, error) => Promise<Response>\n})\n```\n\nThis allows you to create your own UI for each of these screens.\n</Section>\n---\n## Methods\n### PasswordProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nPasswordProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">PasswordConfig</code>](/docs/provider/password#passwordconfig)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\n</Segment>\n## PasswordChangeError\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_password</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">password_mismatch</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">message</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">validation_error</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe errors that can happen on the change password screen.\n\n| Error | Description |\n| ----- | ----------- |\n| `invalid_email` | The email is invalid. |\n| `invalid_code` | The code is invalid. |\n| `invalid_password` | The password is invalid. |\n| `password_mismatch` | The passwords do not match. |\n</Segment>\n## PasswordChangeState\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">redirect</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">start</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">code</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">email</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">redirect</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">email</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">redirect</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">update</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe state of the password change flow.\n\n| State | Description |\n| ----- | ----------- |\n| `start` | The user is asked to enter their email address to start the flow. |\n| `code` | The user needs to enter the pin code to verify their email. |\n| `update` | The user is asked to enter their new password and confirm it. |\n</Segment>\n## PasswordConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">change</code>](#passwordconfig.change) <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">PasswordChangeState</code>](/docs/provider/password#passwordchangestate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordChangeError</code>](/docs/provider/password#passwordchangeerror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">login</code>](#passwordconfig.login) <code class=\"primitive\">(req: <code class=\"type\">Request</code>, form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordLoginError</code>](/docs/provider/password#passwordloginerror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">register</code>](#passwordconfig.register) <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">PasswordRegisterState</code>](/docs/provider/password#passwordregisterstate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordRegisterError</code>](/docs/provider/password#passwordregistererror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">sendCode</code>](#passwordconfig.sendcode) <code class=\"primitive\">(email: <code class=\"primitive\">string</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">validatePassword?</code>](#passwordconfig.validatepassword) [<code class=\"type\">StandardSchema</code>](https://github.com/standard-schema/standard-schema)<code class=\"symbol\"> | </code><code class=\"primitive\">(password: <code class=\"primitive\">string</code>) => <code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\"> | </code><code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"passwordconfig.change\" Tag=\"h4\" parent=\"PasswordConfig.\">change</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">PasswordChangeState</code>](/docs/provider/password#passwordchangestate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordChangeError</code>](/docs/provider/password#passwordchangeerror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe request handler to generate the UI for the change password screen.\n\nTakes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\nand optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\nojects.\n\nAlso passes in the current `state` of the flow and any `error` that occurred.\n\nExpects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\nin return.\n</Segment>\n<NestedTitle id=\"passwordconfig.login\" Tag=\"h4\" parent=\"PasswordConfig.\">login</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(req: <code class=\"type\">Request</code>, form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordLoginError</code>](/docs/provider/password#passwordloginerror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe request handler to generate the UI for the login screen.\n\nTakes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\nand optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\nojects.\n\nIn case of an error, this is called again with the `error`.\n\nExpects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\nin return.\n</Segment>\n<NestedTitle id=\"passwordconfig.register\" Tag=\"h4\" parent=\"PasswordConfig.\">register</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(req: <code class=\"type\">Request</code>, state: [<code class=\"type\">PasswordRegisterState</code>](/docs/provider/password#passwordregisterstate), form?: <code class=\"type\">FormData</code>, error?: [<code class=\"type\">PasswordRegisterError</code>](/docs/provider/password#passwordregistererror)) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nThe request handler to generate the UI for the register screen.\n\nTakes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\nand optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)\nojects.\n\nAlso passes in the current `state` of the flow and any `error` that occurred.\n\nExpects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object\nin return.\n</Segment>\n<NestedTitle id=\"passwordconfig.sendcode\" Tag=\"h4\" parent=\"PasswordConfig.\">sendCode</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(email: <code class=\"primitive\">string</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to send the confirmation pin code to the user.\n```ts\n{\n  sendCode: async (email, code) => {\n    // Send an email with the code\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"passwordconfig.validatepassword\" Tag=\"h4\" parent=\"PasswordConfig.\">validatePassword?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">StandardSchema</code>](https://github.com/standard-schema/standard-schema)<code class=\"symbol\"> | </code><code class=\"primitive\">(password: <code class=\"primitive\">string</code>) => <code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\"> | </code><code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to validate the password on sign up and password reset.\n```ts\n{\n  validatePassword: (password) => {\n     return password.length < 8 ? \"Password must be at least 8 characters\" : undefined\n  }\n}\n```\n</Segment>\n## PasswordLoginError\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_password</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe errors that can happen on the login screen.\n\n| Error | Description |\n| ----- | ----------- |\n| `invalid_email` | The email is invalid. |\n| `invalid_password` | The password is invalid. |\n</Segment>\n## PasswordRegisterError\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">email_taken</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">invalid_password</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">password_mismatch</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">message</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">validation_error</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe errors that can happen on the register screen.\n\n| Error | Description |\n| ----- | ----------- |\n| `email_taken` | The email is already taken. |\n| `invalid_email` | The email is invalid. |\n| `invalid_code` | The code is invalid. |\n| `invalid_password` | The password is invalid. |\n| `password_mismatch` | The passwords do not match. |\n</Segment>\n## PasswordRegisterState\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&lcub; </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">start</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&lcub; </code><code class=\"key\">code</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">email</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">password</code><code class=\"symbol\">&colon; </code><code class=\"primitive\">string</code><code class=\"symbol\">, </code><code class=\"key\">type</code><code class=\"symbol\">&colon; </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">code</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> &rcub;</code>\n</InlineSection>\n</Section>\nThe states that can happen on the register screen.\n\n| State | Description |\n| ----- | ----------- |\n| `start` | The user is asked to enter their email address and password to start the flow. |\n| `code` | The user needs to enter the pin code to verify their email. |\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/slack.mdx",
    "content": "---\ntitle: SlackProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/slack.ts\ndescription: Reference doc for the `SlackProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Slack.\n\n```ts {5-10}\nimport { SlackProvider } from \"@openauthjs/openauth/provider/slack\"\n\nexport default issuer({\n  providers: {\n    slack: SlackProvider({\n      team: \"T1234567890\",\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\",\n      scopes: [\"openid\", \"email\", \"profile\"]\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### SlackProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nSlackProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">SlackConfig</code>](/docs/provider/slack#slackconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreates a [Slack OAuth2 provider](https://api.slack.com/authentication/sign-in-with-slack).\n```ts\nSlackProvider({\n  team: \"T1234567890\",\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\",\n  scopes: [\"openid\", \"email\", \"profile\"]\n})\n```\n</Segment>\n## SlackConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#slackconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#slackconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#slackconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#slackconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#slackconfig.scopes) <code class=\"symbol\">(</code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">profile</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">openid</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\">)[]</code></p>\n- <p>[<code class=\"key\">team</code>](#slackconfig.team) <code class=\"primitive\">string</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"slackconfig.clientid\" Tag=\"h4\" parent=\"SlackConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"slackconfig.clientsecret\" Tag=\"h4\" parent=\"SlackConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"slackconfig.pkce\" Tag=\"h4\" parent=\"SlackConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"slackconfig.query\" Tag=\"h4\" parent=\"SlackConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"slackconfig.scopes\" Tag=\"h4\" parent=\"SlackConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">(</code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">profile</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">openid</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\">)[]</code>\n</InlineSection>\n</Section>\nThe scopes to request from the user.\n\n| Scope | Description |\n|-|-|\n| `email` | Grants permission to access the user's email address. |\n| `profile` | Grants permission to access the user's profile information. |\n| `openid` | Grants permission to use OpenID Connect to verify the user's identity. |\n</Segment>\n<NestedTitle id=\"slackconfig.team\" Tag=\"h4\" parent=\"SlackConfig.\">team</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe workspace the user is intending to authenticate.\n\nIf that workspace has been previously authenticated, the user will be signed in directly,\nbypassing the consent screen.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/spotify.mdx",
    "content": "---\ntitle: SpotifyProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/spotify.ts\ndescription: Reference doc for the `SpotifyProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Spotify.\n\n```ts {5-8}\nimport { SpotifyProvider } from \"@openauthjs/openauth/provider/spotify\"\n\nexport default issuer({\n  providers: {\n    spotify: SpotifyProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### SpotifyProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nSpotifyProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">SpotifyConfig</code>](/docs/provider/spotify#spotifyconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Spotify OAuth2 provider.\n```ts\nSpotifyProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## SpotifyConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#spotifyconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#spotifyconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#spotifyconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#spotifyconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#spotifyconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"spotifyconfig.clientid\" Tag=\"h4\" parent=\"SpotifyConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"spotifyconfig.clientsecret\" Tag=\"h4\" parent=\"SpotifyConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"spotifyconfig.pkce\" Tag=\"h4\" parent=\"SpotifyConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"spotifyconfig.query\" Tag=\"h4\" parent=\"SpotifyConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"spotifyconfig.scopes\" Tag=\"h4\" parent=\"SpotifyConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/twitch.mdx",
    "content": "---\ntitle: TwitchProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/twitch.ts\ndescription: Reference doc for the `TwitchProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Twitch.\n\n```ts {5-8}\nimport { TwitchProvider } from \"@openauthjs/openauth/provider/twitch\"\n\nexport default issuer({\n  providers: {\n    twitch: TwitchProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### TwitchProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nTwitchProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">TwitchConfig</code>](/docs/provider/twitch#twitchconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Twitch OAuth2 provider.\n```ts\nTwitchProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## TwitchConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#twitchconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#twitchconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#twitchconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#twitchconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#twitchconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"twitchconfig.clientid\" Tag=\"h4\" parent=\"TwitchConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"twitchconfig.clientsecret\" Tag=\"h4\" parent=\"TwitchConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"twitchconfig.pkce\" Tag=\"h4\" parent=\"TwitchConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"twitchconfig.query\" Tag=\"h4\" parent=\"TwitchConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"twitchconfig.scopes\" Tag=\"h4\" parent=\"TwitchConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/x.mdx",
    "content": "---\ntitle: XProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/x.ts\ndescription: Reference doc for the `XProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with X.com.\n\n```ts {5-8}\nimport { XProvider } from \"@openauthjs/openauth/provider/x\"\n\nexport default issuer({\n  providers: {\n    x: XProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### XProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nXProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">XProviderConfig</code>](/docs/provider/x#xproviderconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a X.com OAuth2 provider.\n```ts\nXProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## XProviderConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#xproviderconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#xproviderconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#xproviderconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#xproviderconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#xproviderconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"xproviderconfig.clientid\" Tag=\"h4\" parent=\"XProviderConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"xproviderconfig.clientsecret\" Tag=\"h4\" parent=\"XProviderConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"xproviderconfig.pkce\" Tag=\"h4\" parent=\"XProviderConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"xproviderconfig.query\" Tag=\"h4\" parent=\"XProviderConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"xproviderconfig.scopes\" Tag=\"h4\" parent=\"XProviderConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/provider/yahoo.mdx",
    "content": "---\ntitle: YahooProvider\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/yahoo.ts\ndescription: Reference doc for the `YahooProvider`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse this provider to authenticate with Yahoo.\n\n```ts {5-8}\nimport { YahooProvider } from \"@openauthjs/openauth/provider/yahoo\"\n\nexport default issuer({\n  providers: {\n    yahoo: YahooProvider({\n      clientID: \"1234567890\",\n      clientSecret: \"0987654321\"\n    })\n  }\n})\n```\n</Section>\n---\n## Methods\n### YahooProvider\n<Segment>\n<Section type=\"signature\">\n```ts\nYahooProvider(config)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">config</code> [<code class=\"type\">YahooConfig</code>](/docs/provider/yahoo#yahooconfig)</p>\nThe config for the provider.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">Provider</code>\n</InlineSection>\nCreate a Yahoo OAuth2 provider.\n```ts\nYahooProvider({\n  clientID: \"1234567890\",\n  clientSecret: \"0987654321\"\n})\n```\n</Segment>\n## YahooConfig\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">clientID</code>](#yahooconfig.clientid) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">clientSecret</code>](#yahooconfig.clientsecret) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pkce?</code>](#yahooconfig.pkce) <code class=\"primitive\">boolean</code></p>\n- <p>[<code class=\"key\">query?</code>](#yahooconfig.query) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></p>\n- <p>[<code class=\"key\">scopes</code>](#yahooconfig.scopes) <code class=\"primitive\">string</code><code class=\"symbol\">[]</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"yahooconfig.clientid\" Tag=\"h4\" parent=\"YahooConfig.\">clientID</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client ID.\n\nThis is just a string to identify your app.\n```ts\n{\n  clientID: \"my-client\"\n}\n```\n</Segment>\n<NestedTitle id=\"yahooconfig.clientsecret\" Tag=\"h4\" parent=\"YahooConfig.\">clientSecret</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe client secret.\n\nThis is a private key that's used to authenticate your app. It should be kept secret.\n```ts\n{\n  clientSecret: \"0987654321\"\n}\n```\n</Segment>\n<NestedTitle id=\"yahooconfig.pkce\" Tag=\"h4\" parent=\"YahooConfig.\">pkce?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.\nSome providers like x.com require this.\n</Segment>\n<NestedTitle id=\"yahooconfig.query\" Tag=\"h4\" parent=\"YahooConfig.\">query?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAny additional parameters that you want to pass to the authorization endpoint.\n```ts\n{\n  query: {\n    access_type: \"offline\",\n    prompt: \"consent\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"yahooconfig.scopes\" Tag=\"h4\" parent=\"YahooConfig.\">scopes</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\">[]</code>\n</InlineSection>\n</Section>\nA list of OAuth scopes that you want to request.\n```ts\n{\n  scopes: [\"email\", \"profile\"]\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/start/sst.mdx",
    "content": "---\ntitle: OpenAuth with SST and Next.js\ndescription: Add OpenAuth to your Next.js app and deploy it with SST.\n---\n\nimport { Image } from \"astro:assets\"\n\nimport nextAppDark from \"./nextjs-dark.png\"\nimport nextAppLight from \"./nextjs-light.png\"\n\nWe are going to create a new Next.js app, add authentication to it with OpenAuth, and deploy it with SST.\n\n:::tip[View source]\nYou can [view the source](https://github.com/openauthjs/openauth/tree/master/examples/quickstart/sst) of this example in our repo.\n:::\n\nWe are going to authenticate users by sending them a code to verify their email address.\n\n---\n\n## 1. Create a project\n\nLet's start by creating our Next.js app and starting it in dev mode.\n\n```bash\nnpx create-next-app@latest oa-nextjs\ncd oa-nextjs\n```\n\nWe are picking **TypeScript** and not selecting **ESLint**.\n\n---\n\n##### Init SST\n\nNow let's initialize SST in our app.\n\n```bash\nnpx sst@latest init\n```\n\nSelect the defaults and pick **AWS**. This'll create a `sst.config.ts` file in your project root.\n\n---\n\n## 2. Add OpenAuth server\n\nNext, let's add a directory for our OpenAuth server.\n\n```bash\nmkdir auth\n```\n\nAdd our OpenAuth server to a `auth/index.ts` file.\n\n```ts title=\"auth/index.ts\"\nimport { handle } from \"hono/aws-lambda\"\nimport { issuer } from \"@openauthjs/openauth\"\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { subjects } from \"./subjects\"\n\nasync function getUser(email: string) {\n  // Get user from database and return user ID\n  return \"123\"\n}\n\nconst app = issuer({\n  subjects,\n  storage: MemoryStorage(),\n  // Remove after setting custom domain\n  allow: async () => true,\n  providers: {\n    code: CodeProvider(\n      CodeUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"code\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.claims.email)\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n\n\nexport const handler = handle(app)\n```\n\n---\n\n##### Define subjects\n\nWe are also going to define our subjects. Add the following to a `auth/subjects.ts` file.\n\n```ts title=\"auth/subjects.ts\"\nimport { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nexport const subjects = createSubjects({\n  user: object({\n    id: string(),\n  }),\n})\n```\n\nLet's install our dependencies.\n\n```bash\nnpm install @openauthjs/openauth valibot hono\n```\n\n---\n\n##### Add Auth component\n\nNow let's add this to our SST app. Replace the `run` function in `sst.config.ts` with the following.\n\n```ts title=\"sst.config.ts\" {6}\nconst auth = new sst.aws.Auth(\"MyAuth\", {\n  issuer: \"auth/index.handler\",\n});\n\nnew sst.aws.Nextjs(\"MyWeb\", {\n  link: [auth]\n});\n```\n\nThis is defining our OpenAuth component and linking it to our Next.js app.\n\n---\n\n##### Start dev mode\n\nRun the following to start dev mode. This'll start SST, your Next.js app, and your OpenAuth server.\n\n```bash\nnpx sst dev\n```\n\nOnce complete, it should give you the URL of your OpenAuth server.\n\n```bash\n✓  Complete\n   MyAuth: https://fv62a3niazbkrazxheevotace40affnk.lambda-url.us-east-1.on.aws\n```\n\nAlso click on **MyWeb** in the sidebar and open your Next.js app by going to `http://localhost:3000`.\n\n---\n\n## 3. Add OpenAuth client\n\nNext, let's add our OpenAuth client to our Next.js app. Add the following to `app/auth.ts`.\n\n```ts title=\"app/auth.ts\" {7}\nimport { Resource } from \"sst\"\nimport { createClient } from \"@openauthjs/openauth/client\"\nimport { cookies as getCookies } from \"next/headers\"\n\nexport const client = createClient({\n  clientID: \"nextjs\",\n  issuer: Resource.MyAuth.url\n})\n\nexport async function setTokens(access: string, refresh: string) {\n  const cookies = await getCookies()\n\n  cookies.set({\n    name: \"access_token\",\n    value: access,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  cookies.set({\n    name: \"refresh_token\",\n    value: refresh,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n```\n\nHere we are _linking_ to our auth server. And once the user is authenticated, we'll be saving their access and refresh tokens in _http only_ cookies.\n\n---\n\n##### Add auth actions\n\nLet's add the server actions that our Next.js app will need to authenticate users. Add the following to `app/actions.ts`.\n\n```ts title=\"app/actions.ts\"\n\"use server\"\n\nimport { redirect } from \"next/navigation\"\nimport { headers as getHeaders, cookies as getCookies } from \"next/headers\"\nimport { subjects } from \"../auth/subjects\"\nimport { client, setTokens } from \"./auth\"\n\nexport async function auth() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (!accessToken) {\n    return false\n  }\n\n  const verified = await client.verify(subjects, accessToken.value, {\n    refresh: refreshToken?.value,\n  })\n\n  if (verified.err) {\n    return false\n  }\n  if (verified.tokens) {\n    await setTokens(verified.tokens.access, verified.tokens.refresh)\n  }\n\n  return verified.subject\n}\n\nexport async function login() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (accessToken) {\n    const verified = await client.verify(subjects, accessToken.value, {\n      refresh: refreshToken?.value,\n    })\n    if (!verified.err && verified.tokens) {\n      await setTokens(verified.tokens.access, verified.tokens.refresh)\n      redirect(\"/\")\n    }\n  }\n\n  const headers = await getHeaders()\n  const host = headers.get(\"host\")\n  const protocol = host?.includes(\"localhost\") ? \"http\" : \"https\"\n  const { url } = await client.authorize(`${protocol}://${host}/api/callback`, \"code\")\n  redirect(url)\n}\n\nexport async function logout() {\n  const cookies = await getCookies()\n  cookies.delete(\"access_token\")\n  cookies.delete(\"refresh_token\")\n\n  redirect(\"/\")\n}\n```\n\nThis is adding an `auth` action that checks if a user is authenticated, `login` that starts the OAuth flow, and `logout` that clears the session.\n\n---\n\n##### Add callback route\n\nWhen the OpenAuth flow is complete, users will be redirected back to our Next.js app. Let's add a callback route to handle this in `app/api/callback/route.ts`.\n\n```ts title=\"app/api/callback/route.ts\"\nimport { client, setTokens } from \"../../auth\"\nimport { type NextRequest, NextResponse } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  const url = new URL(req.url)\n  const code = url.searchParams.get(\"code\")\n\n  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)\n\n  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })\n\n  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)\n\n  return NextResponse.redirect(`${url.origin}/`)\n}\n```\n\nOnce the user is authenticated, we redirect them to the root of our app.\n\n---\n\n## 4. Add auth to app\n\nNow we are ready to add authentication to our app. Replace the `<Home />` component in `app/page.tsx` with the following.\n\n```tsx title=\"app/page.tsx\"\nimport { auth, login, logout } from \"./actions\"\n\nexport default async function Home() {\n  const subject = await auth()\n\n  return (\n    <div className={styles.page}>\n      <main className={styles.main}>\n        <Image\n          className={styles.logo}\n          src=\"/next.svg\"\n          alt=\"Next.js logo\"\n          width={180}\n          height={38}\n          priority\n        />\n        <ol>\n          {subject ? (\n            <>\n              <li>\n                Logged in as <code>{subject.properties.id}</code>.\n              </li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          ) : (\n            <>\n              <li>Login with your email and password.</li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          )}\n        </ol>\n\n        <div className={styles.ctas}>\n          {subject ? (\n            <form action={logout}>\n              <button className={styles.secondary}>Logout</button>\n            </form>\n          ) : (\n            <form action={login}>\n              <button className={styles.primary}>Login with OpenAuth</button>\n            </form>\n          )}\n        </div>\n      </main>\n    </div>\n  )\n}\n```\n\nLet's also add these styles to `app/page.module.css`.\n\n```css title=\"app/page.module.css\"\n.ctas button {\n  appearance: none;\n  background: transparent;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\nbutton.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\nbutton.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n```\n\n---\n\n## 4. Test your app\n\nHead to `http://localhost:3000` and click the login button, you should be redirected to the OpenAuth server asking you to put in your email.\n\nIf you check the **Functions** tab in your `sst dev` session, you'll see the code being console logged. You can use this code to login.\n\n<picture>\n  <source srcset={nextAppDark.src} media=\"(prefers-color-scheme: dark)\" />\n  <source srcset={nextAppLight.src} media=\"(prefers-color-scheme: light)\" />\n  <Image src={nextAppLight} alt=\"Next.js app login with OpenAuth\" />\n</picture>\n\nThis should log you in and print your user ID.\n\n---\n\n## Deploy your app\n\nNow let's deploy your app to AWS.\n\n```bash\nnpx sst deploy --stage production\n```\n\nYou can use any stage name here but it's good to create a new stage for production.\n\n```bash\n✓  Complete\n   MyAuth: https://vp3honbl3od4gmo7mei37mchky0waxew.lambda-url.us-east-1.on.aws\n   MyWeb: https://d2fjg1rqbqi95t.cloudfront.net\n```\n\nCongrats! Your app and your OpenAuth server should now be live!\n"
  },
  {
    "path": "www/src/content/docs/docs/start/standalone.mdx",
    "content": "---\ntitle: OpenAuth with Next.js\ndescription: Use OpenAuth to add authentication to your Next.js app.\n---\n\nimport { Image } from \"astro:assets\"\n\nimport nextAppDark from \"./nextjs-dark.png\"\nimport nextAppLight from \"./nextjs-light.png\"\n\nWe are going to create a new Next.js app and add authentication to it with OpenAuth.\n\n:::tip[View source]\nYou can [view the source](https://github.com/openauthjs/openauth/tree/master/examples/quickstart/standalone) of this example in our repo.\n:::\n\nWe are going to authenticate users by sending them a code to verify their email address.\n\n---\n\n## 1. Create a project\n\nLet's start by creating our Next.js app and starting it in dev mode.\n\n```bash\nbun create next-app oa-nextjs\ncd oa-nextjs\nbun dev\n```\n\nWe are picking **TypeScript** and not selecting **ESLint**.\n\nThis will start our Next.js app at `http://localhost:3000`.\n\n---\n\n## 2. Add OpenAuth server\n\nNext, let's add a directory for our OpenAuth server.\n\n```bash\nmkdir auth\n```\n\nAdd our OpenAuth server to a `auth/index.ts` file.\n\n```ts title=\"auth/index.ts\"\nimport { issuer } from \"@openauthjs/openauth\"\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\nimport { subjects } from \"./subjects\"\n\nasync function getUser(email: string) {\n  // Get user from database and return user ID\n  return \"123\"\n}\n\nexport default issuer({\n  subjects,\n  storage: MemoryStorage(),\n  providers: {\n    code: CodeProvider(\n      CodeUI({\n        sendCode: async (email, code) => {\n          console.log(email, code)\n        },\n      }),\n    ),\n  },\n  success: async (ctx, value) => {\n    if (value.provider === \"code\") {\n      return ctx.subject(\"user\", {\n        id: await getUser(value.claims.email)\n      })\n    }\n    throw new Error(\"Invalid provider\")\n  },\n})\n```\n\n---\n\n##### Define subjects\n\nWe are also going to define our subjects. Add the following to a `auth/subjects.ts` file.\n\n```ts title=\"auth/subjects.ts\"\nimport { object, string } from \"valibot\"\nimport { createSubjects } from \"@openauthjs/openauth/subject\"\n\nexport const subjects = createSubjects({\n  user: object({\n    id: string(),\n  }),\n})\n```\n\nLet's install our dependencies.\n\n```bash\nbun add @openauthjs/openauth valibot\n```\n\nAnd add a script to start our auth server to `package.json`.\n\n```js title=\"package.json\"\n\"dev:auth\": \"PORT=3001 bun run --hot auth/index.ts\",\n```\n\nNow run the auth server in a separate terminal.\n\n```bash\nbun dev:auth\n```\n\nThis will start our auth server at `http://localhost:3001`.\n\n---\n\n## 3. Add OpenAuth client\n\nNext, let's add our OpenAuth client to our Next.js app. Add the following to `app/auth.ts`.\n\n```ts title=\"app/auth.ts\"\nimport { createClient } from \"@openauthjs/openauth/client\"\nimport { cookies as getCookies } from \"next/headers\"\n\nexport const client = createClient({\n  clientID: \"nextjs\",\n  issuer: \"http://localhost:3001\",\n})\n\nexport async function setTokens(access: string, refresh: string) {\n  const cookies = await getCookies()\n\n  cookies.set({\n    name: \"access_token\",\n    value: access,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n  cookies.set({\n    name: \"refresh_token\",\n    value: refresh,\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    maxAge: 34560000,\n  })\n}\n```\n\nHere we are assuming that our auth server is running at `http://localhost:3001`. Once the user is authenticated, we'll be saving their access and refresh tokens in _http only_ cookies.\n\n---\n\n##### Add auth actions\n\nLet's add the server actions that our Next.js app will need to authenticate users. Add the following to `app/actions.ts`.\n\n```ts title=\"app/actions.ts\"\n\"use server\"\n\nimport { redirect } from \"next/navigation\"\nimport { headers as getHeaders, cookies as getCookies } from \"next/headers\"\nimport { subjects } from \"../auth/subjects\"\nimport { client, setTokens } from \"./auth\"\n\nexport async function auth() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (!accessToken) {\n    return false\n  }\n\n  const verified = await client.verify(subjects, accessToken.value, {\n    refresh: refreshToken?.value,\n  })\n\n  if (verified.err) {\n    return false\n  }\n  if (verified.tokens) {\n    await setTokens(verified.tokens.access, verified.tokens.refresh)\n  }\n\n  return verified.subject\n}\n\nexport async function login() {\n  const cookies = await getCookies()\n  const accessToken = cookies.get(\"access_token\")\n  const refreshToken = cookies.get(\"refresh_token\")\n\n  if (accessToken) {\n    const verified = await client.verify(subjects, accessToken.value, {\n      refresh: refreshToken?.value,\n    })\n    if (!verified.err && verified.tokens) {\n      await setTokens(verified.tokens.access, verified.tokens.refresh)\n      redirect(\"/\")\n    }\n  }\n\n  const headers = await getHeaders()\n  const host = headers.get(\"host\")\n  const protocol = host?.includes(\"localhost\") ? \"http\" : \"https\"\n  const { url } = await client.authorize(`${protocol}://${host}/api/callback`, \"code\")\n  redirect(url)\n}\n\nexport async function logout() {\n  const cookies = await getCookies()\n  cookies.delete(\"access_token\")\n  cookies.delete(\"refresh_token\")\n\n  redirect(\"/\")\n}\n```\n\nThis is adding an `auth` action that checks if a user is authenticated, `login` that starts the OAuth flow, and `logout` that clears the session.\n\n---\n\n##### Add callback route\n\nWhen the OpenAuth flow is complete, users will be redirected back to our Next.js app. Let's add a callback route to handle this in `app/api/callback/route.ts`.\n\n```ts title=\"app/api/callback/route.ts\"\nimport { client, setTokens } from \"../../auth\"\nimport { type NextRequest, NextResponse } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  const url = new URL(req.url)\n  const code = url.searchParams.get(\"code\")\n\n  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)\n\n  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })\n\n  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)\n\n  return NextResponse.redirect(`${url.origin}/`)\n}\n```\n\nOnce the user is authenticated, we redirect them to the root of our app.\n\n---\n\n## 4. Add auth to app\n\nNow we are ready to add authentication to our app. Replace the `<Home />` component in `app/page.tsx` with the following.\n\n```tsx title=\"app/page.tsx\"\nimport { auth, login, logout } from \"./actions\"\n\nexport default async function Home() {\n  const subject = await auth()\n\n  return (\n    <div className={styles.page}>\n      <main className={styles.main}>\n        <Image\n          className={styles.logo}\n          src=\"/next.svg\"\n          alt=\"Next.js logo\"\n          width={180}\n          height={38}\n          priority\n        />\n        <ol>\n          {subject ? (\n            <>\n              <li>\n                Logged in as <code>{subject.properties.id}</code>.\n              </li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          ) : (\n            <>\n              <li>Login with your email and password.</li>\n              <li>\n                And then check out <code>app/page.tsx</code>.\n              </li>\n            </>\n          )}\n        </ol>\n\n        <div className={styles.ctas}>\n          {subject ? (\n            <form action={logout}>\n              <button className={styles.secondary}>Logout</button>\n            </form>\n          ) : (\n            <form action={login}>\n              <button className={styles.primary}>Login with OpenAuth</button>\n            </form>\n          )}\n        </div>\n      </main>\n    </div>\n  )\n}\n```\n\nLet's also add these styles to `app/page.module.css`.\n\n```css title=\"app/page.module.css\"\n.ctas button {\n  appearance: none;\n  background: transparent;\n  border-radius: 128px;\n  height: 48px;\n  padding: 0 20px;\n  border: none;\n  border: 1px solid transparent;\n  transition:\n    background 0.2s,\n    color 0.2s,\n    border-color 0.2s;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  line-height: 20px;\n  font-weight: 500;\n}\n\nbutton.primary {\n  background: var(--foreground);\n  color: var(--background);\n  gap: 8px;\n}\n\nbutton.secondary {\n  border-color: var(--gray-alpha-200);\n  min-width: 180px;\n}\n```\n\n---\n\n## 4. Test your app\n\nHead to `http://localhost:3000` and click the login button, you should be redirected to the OpenAuth server asking you to put in your email.\n\nIf you check the terminal running the auth server, you'll see the code being console logged. You can use this code to login.\n\n<picture>\n  <source srcset={nextAppDark.src} media=\"(prefers-color-scheme: dark)\" />\n  <source srcset={nextAppLight.src} media=\"(prefers-color-scheme: light)\" />\n  <Image src={nextAppLight} alt=\"Next.js app login with OpenAuth\" />\n</picture>\n\nThis should log you in and print your user ID.\n\n---\n\n## Deploy your app\n\nTo are now ready to deploy your app and your OpenAuth server. A couple of changes you'll need to make.\n\n1. Use a more persistent `storage` like [DynamoDB](https://aws.amazon.com/dynamodb/) or [Cloudflare KV](https://developers.cloudflare.com/kv/) in your `auth/index.ts`.\n2. Instead of printing out the code, email that to the user.\n3. Finally, in your `app/auth.ts`, use the deployed auth server URL instead of `http://localhost:3001`.\n\nYou can also check out the [**SST quick start**](/docs/start/sst) for a fully deployed example.\n"
  },
  {
    "path": "www/src/content/docs/docs/storage/cloudflare.mdx",
    "content": "---\ntitle: Cloudflare KV\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/storage/cloudflare.ts\ndescription: Reference doc for the Cloudflare KV storage adapter.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigure OpenAuth to use [Cloudflare KV](https://developers.cloudflare.com/kv/) as a\nstorage adapter.\n\n```ts\nimport { CloudflareStorage } from \"@openauthjs/openauth/storage/cloudflare\"\n\nconst storage = CloudflareStorage({\n  namespace: \"my-namespace\"\n})\n\n\nexport default issuer({\n  storage,\n  // ...\n})\n```\n</Section>\n---\n## Methods\n### CloudflareStorage\n<Segment>\n<Section type=\"signature\">\n```ts\nCloudflareStorage(options)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">options</code> [<code class=\"type\">CloudflareStorageOptions</code>](#cloudflarestorageoptions)</p>\nThe config for the adapter.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">StorageAdapter</code>\n</InlineSection>\nCreates a Cloudflare KV store.\n</Segment>\n## CloudflareStorageOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">namespace</code>](#cloudflarestorageoptions.namespace) <code class=\"type\">KVNamespace</code></p>\n</Section>\nConfigure the Cloudflare KV store that's created.\n</Segment>\n<NestedTitle id=\"cloudflarestorageoptions.namespace\" Tag=\"h4\" parent=\"CloudflareStorageOptions.\">namespace</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"type\">KVNamespace</code>\n</InlineSection>\n</Section>\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/storage/dynamo.mdx",
    "content": "---\ntitle: DynamoDB\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/storage/dynamo.ts\ndescription: Reference doc for the DynamoDB storage adapter.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigure OpenAuth to use [DynamoDB](https://aws.amazon.com/dynamodb/) as a storage adapter.\n\n```ts\nimport { DynamoStorage } from \"@openauthjs/openauth/storage/dynamo\"\n\nconst storage = DynamoStorage({\n  table: \"my-table\",\n  pk: \"pk\",\n  sk: \"sk\"\n})\n\nexport default issuer({\n  storage,\n  // ...\n})\n```\n</Section>\n---\n## Methods\n### DynamoStorage\n<Segment>\n<Section type=\"signature\">\n```ts\nDynamoStorage(options)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">options</code> [<code class=\"type\">DynamoStorageOptions</code>](#dynamostorageoptions)</p>\nThe config for the adapter.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">StorageAdapter</code>\n</InlineSection>\nCreates a DynamoDB store.\n</Segment>\n## DynamoStorageOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">endpoint?</code>](#dynamostorageoptions.endpoint) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">pk?</code>](#dynamostorageoptions.pk) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">sk?</code>](#dynamostorageoptions.sk) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">table</code>](#dynamostorageoptions.table) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">ttl?</code>](#dynamostorageoptions.ttl) <code class=\"primitive\">string</code></p>\n</Section>\nConfigure the DynamoDB table that's created.\n```ts\n{\n  table: \"my-table\",\n  pk: \"pk\",\n  sk: \"sk\"\n}\n```\n</Segment>\n<NestedTitle id=\"dynamostorageoptions.endpoint\" Tag=\"h4\" parent=\"DynamoStorageOptions.\">endpoint?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"https://dynamodb.{region}.amazonaws.com\"\n</InlineSection>\nEndpoint URL for the DynamoDB service. Useful for local testing.\n</Segment>\n<NestedTitle id=\"dynamostorageoptions.pk\" Tag=\"h4\" parent=\"DynamoStorageOptions.\">pk?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"pk\"\n</InlineSection>\nThe primary key column name.\n</Segment>\n<NestedTitle id=\"dynamostorageoptions.sk\" Tag=\"h4\" parent=\"DynamoStorageOptions.\">sk?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"sk\"\n</InlineSection>\nThe sort key column name.\n</Segment>\n<NestedTitle id=\"dynamostorageoptions.table\" Tag=\"h4\" parent=\"DynamoStorageOptions.\">table</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe name of the DynamoDB table.\n</Segment>\n<NestedTitle id=\"dynamostorageoptions.ttl\" Tag=\"h4\" parent=\"DynamoStorageOptions.\">ttl?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"expiry\"\n</InlineSection>\nThe name of the time to live attribute.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/storage/memory.mdx",
    "content": "---\ntitle: Memory\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/storage/memory.ts\ndescription: Reference doc for the Memory storage adapter.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigure OpenAuth to use a simple in-memory store.\n\n:::caution\nThis is not meant to be used in production.\n:::\n\nThis is useful for testing and development. It's not meant to be used in production.\n\n```ts\nimport { MemoryStorage } from \"@openauthjs/openauth/storage/memory\"\n\nconst storage = MemoryStorage()\n\nexport default issuer({\n  storage,\n  // ...\n})\n```\n\nOptionally, you can persist the store to a file.\n\n```ts\nMemoryStorage({\n  persist: \"./persist.json\"\n})\n```\n</Section>\n---\n## Methods\n### MemoryStorage\n<Segment>\n<Section type=\"signature\">\n```ts\nMemoryStorage(input?)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">input?</code> [<code class=\"type\">MemoryStorageOptions</code>](#memorystorageoptions)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">StorageAdapter</code>\n</InlineSection>\n</Segment>\n## MemoryStorageOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">persist?</code>](#memorystorageoptions.persist) <code class=\"primitive\">string</code></p>\n</Section>\nConfigure the memory store.\n</Segment>\n<NestedTitle id=\"memorystorageoptions.persist\" Tag=\"h4\" parent=\"MemoryStorageOptions.\">persist?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nOptionally, backup the store to a file. So it'll be persisted when the issuer restarts.\n```ts\n{\n  persist: \"./persist.json\"\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/subject.mdx",
    "content": "---\ntitle: Subject\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/subject.ts\ndescription: Reference doc for creating subjects.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nSubjects are what the access token generated at the end of the auth flow will map to. Under\nthe hood, the access token is a JWT that contains this data.\n\n#### Define subjects\n\n```ts title=\"subjects.ts\"\nimport { object, string } from \"valibot\"\n\nconst subjects = createSubjects({\n  user: object({\n    userID: string()\n  })\n})\n```\n\nWe are using [valibot](https://github.com/fabian-hiller/valibot) here. You can use any\nvalidation library that's following the\n[standard-schema specification](https://github.com/standard-schema/standard-schema).\n\n:::tip\nYou typically want to place subjects in its own file so it can be imported by all of your apps.\n:::\n\nYou can start with one subject. Later you can add more for different types of users.\n\n#### Set the subjects\n\nThen you can pass it to the `issuer`.\n\n```ts title=\"issuer.ts\"\nimport { subjects } from \"./subjects\"\n\nconst app = issuer({\n  providers: { ... },\n  subjects,\n  // ...\n})\n```\n\n#### Add the subject payload\n\nWhen your user completes the flow, you can add the subject payload in the `success` callback.\n\n```ts title=\"issuer.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 = ... // lookup user or create them\n    }\n    return ctx.subject(\"user\", {\n      userID\n    })\n  },\n  // ...\n})\n```\n\nHere we are looking up the userID from our database and adding it to the subject payload.\n\n:::caution\nYou should only store properties that won't change for the lifetime of the user.\n:::\n\nSince these will be stored in the access token, you should avoid storing information\nthat'll change often. For example, if you store the user's username, you'll need to\nrevoke the access token when the user changes their username.\n\n#### Decode the subject\n\nNow when your user logs in, you can use the OpenAuth client to decode the subject. For\nexample, in our SSR app we can do the following.\n\n```ts title=\"app/page.tsx\"\nimport { subjects } from \"../subjects\"\n\nconst verified = await client.verify(subjects, cookies.get(\"access_token\")!)\nconsole.log(verified.subject.properties.userID)\n```\n\nAll this is typesafe based on the shape of the subjects you defined.\n</Section>\n---\n## Methods\n### createSubjects\n<Segment>\n<Section type=\"signature\">\n```ts\ncreateSubjects(types)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">types</code> [<code class=\"type\">SubjectSchema</code>](/docs/subject#subjectschema)</p>\n</Section>\n<InlineSection>\n**Returns** [<code class=\"type\">SubjectSchema</code>](/docs/subject#subjectschema)\n</InlineSection>\nCreate a subject schema.\n```ts\nconst subjects = createSubjects({\n  user: object({\n    userID: string()\n  }),\n  admin: object({\n    workspaceID: string()\n  })\n})\n```\n\n\nThis is using [valibot](https://github.com/fabian-hiller/valibot) to define the shape of the\nsubjects. You can use any validation library that's following the\n[standard-schema specification](https://github.com/standard-schema/standard-schema).\n</Segment>\n## SubjectSchema\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, [<code class=\"type\">v1.StandardSchema</code>](https://github.com/standard-schema/standard-schema)<code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nSubject schema is a map of types that are used to define the subjects.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/ui/code.mdx",
    "content": "---\ntitle: CodeUI\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/code.tsx\ndescription: Reference doc for the `CodeUI`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigure the UI that's used by the Code provider.\n\n```ts {1,7-12}\nimport { CodeUI } from \"@openauthjs/openauth/ui/code\"\nimport { CodeProvider } from \"@openauthjs/openauth/provider/code\"\n\nexport default issuer({\n  providers: {\n    code: CodeAdapter(\n      CodeUI({\n        copy: {\n          code_info: \"We'll send a pin code to your email\"\n        },\n        sendCode: (claims, code) => console.log(claims.email, code)\n      })\n    )\n  },\n  // ...\n})\n```\n</Section>\n---\n## Methods\n### CodeUI\n<Segment>\n<Section type=\"signature\">\n```ts\nCodeUI(props)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">props</code> [<code class=\"type\">CodeUIOptions</code>](#codeuioptions)</p>\nConfigure the UI.\n</Section>\n<InlineSection>\n**Returns** <code class=\"type\">CodeProviderOptions</code>\n</InlineSection>\nCreates a UI for the Code provider flow.\n</Segment>\n## CodeUICopy\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">any</code>\n</InlineSection>\n</Section>\n</Segment>\n## CodeUIOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">copy?</code>](#codeuioptions.copy) <code class=\"primitive\">Object</code></p>\n  - <p>[<code class=\"key\">button_continue</code>](#copy.button_continue) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_didnt_get</code>](#copy.code_didnt_get) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_info</code>](#copy.code_info) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_invalid</code>](#copy.code_invalid) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_placeholder</code>](#copy.code_placeholder) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_resend</code>](#copy.code_resend) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_resent</code>](#copy.code_resent) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_sent</code>](#copy.code_sent) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">email_invalid</code>](#copy.email_invalid) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">email_placeholder</code>](#copy.email_placeholder) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">mode?</code>](#codeuioptions.mode) <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">phone</code><code class=\"symbol\">&rdquo;</code></p>\n- <p>[<code class=\"key\">sendCode</code>](#codeuioptions.sendcode) <code class=\"primitive\">(claims: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code></p>\n</Section>\nConfigure the password UI.\n</Segment>\n<NestedTitle id=\"codeuioptions.copy\" Tag=\"h4\" parent=\"CodeUIOptions.\">copy?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nCustom copy for the UI.\n</Segment>\n<NestedTitle id=\"copy.button_continue\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">button_continue</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Continue</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the continue button.\n</Segment>\n<NestedTitle id=\"copy.code_didnt_get\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_didnt_get</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Didn't get code?</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the link to resend the code.\n</Segment>\n<NestedTitle id=\"copy.code_info\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_info</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">We'll send a pin code to your email.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy informing that the pin code will be emailed.\n</Segment>\n<NestedTitle id=\"copy.code_invalid\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_invalid</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Invalid code</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the code is invalid.\n</Segment>\n<NestedTitle id=\"copy.code_placeholder\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_placeholder</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Code</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the pin code input.\n</Segment>\n<NestedTitle id=\"copy.code_resend\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_resend</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Resend</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the resend button.\n</Segment>\n<NestedTitle id=\"copy.code_resent\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_resent</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Code resent to </code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for when the code was resent.\n</Segment>\n<NestedTitle id=\"copy.code_sent\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">code_sent</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Code sent to </code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for when the code was sent.\n</Segment>\n<NestedTitle id=\"copy.email_invalid\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">email_invalid</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Email address is not valid</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the email is invalid.\n</Segment>\n<NestedTitle id=\"copy.email_placeholder\" Tag=\"h5\" parent=\"CodeUIOptions.copy.\">email_placeholder</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Email</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the email input.\n</Segment>\n<NestedTitle id=\"codeuioptions.mode\" Tag=\"h4\" parent=\"CodeUIOptions.\">mode?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">email</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">phone</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"email\"\n</InlineSection>\nThe mode to use for the input.\n</Segment>\n<NestedTitle id=\"codeuioptions.sendcode\" Tag=\"h4\" parent=\"CodeUIOptions.\">sendCode</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(claims: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to send the pin code to the user.\n\nThe `claims` object contains the email or phone number of the user. You can send the code\nusing this.\n```ts\nasync (claims, code) => {\n  // Send the code via the claim\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/ui/password.mdx",
    "content": "---\ntitle: PasswordUI\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/password.tsx\ndescription: Reference doc for the `PasswordUI`.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nConfigure the UI that's used by the Password provider.\n\n```ts {1,7-12}\nimport { PasswordUI } from \"@openauthjs/openauth/ui/password\"\nimport { PasswordProvider } from \"@openauthjs/openauth/provider/password\"\n\nexport default issuer({\n  providers: {\n    password: PasswordAdapter(\n      PasswordUI({\n        copy: {\n          error_email_taken: \"This email is already taken.\"\n        },\n        sendCode: (email, code) => console.log(email, code)\n      })\n    )\n  },\n  // ...\n})\n```\n</Section>\n---\n## Methods\n### PasswordUI\n<Segment>\n<Section type=\"signature\">\n```ts\nPasswordUI(input)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">input</code> [<code class=\"type\">PasswordUIOptions</code>](#passworduioptions)</p>\nConfigure the UI.\n</Section>\n<InlineSection>\n**Returns** [<code class=\"type\">PasswordConfig</code>](/docs/provider/password#passwordconfig)\n</InlineSection>\nCreates a UI for the Password provider flow.\n</Segment>\n## PasswordUIOptions\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">copy?</code>](#passworduioptions.copy) <code class=\"primitive\">Object</code></p>\n  - <p>[<code class=\"key\">button_continue</code>](#copy.button_continue) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">change_prompt</code>](#copy.change_prompt) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_resend</code>](#copy.code_resend) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">code_return</code>](#copy.code_return) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_email_taken</code>](#copy.error_email_taken) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_invalid_code</code>](#copy.error_invalid_code) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_invalid_email</code>](#copy.error_invalid_email) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_invalid_password</code>](#copy.error_invalid_password) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_password_mismatch</code>](#copy.error_password_mismatch) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">error_validation_error</code>](#copy.error_validation_error) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">input_code</code>](#copy.input_code) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">input_email</code>](#copy.input_email) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">input_password</code>](#copy.input_password) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">input_repeat</code>](#copy.input_repeat) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">login</code>](#copy.login) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">login_description</code>](#copy.login_description) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">login_prompt</code>](#copy.login_prompt) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">login_title</code>](#copy.login_title) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">register</code>](#copy.register) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">register_description</code>](#copy.register_description) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">register_prompt</code>](#copy.register_prompt) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">register_title</code>](#copy.register_title) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">sendCode</code>](#passworduioptions.sendcode) <code class=\"primitive\">(email: <code class=\"primitive\">string</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code></p>\n- <p>[<code class=\"key\">validatePassword?</code>](#passworduioptions.validatepassword) [<code class=\"type\">StandardSchema</code>](https://github.com/standard-schema/standard-schema)<code class=\"symbol\"> | </code><code class=\"primitive\">(password: <code class=\"primitive\">string</code>) => <code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\"> | </code><code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></code></p>\n</Section>\nConfigure the password UI.\n</Segment>\n<NestedTitle id=\"passworduioptions.copy\" Tag=\"h4\" parent=\"PasswordUIOptions.\">copy?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nCustom copy for the UI.\n</Segment>\n<NestedTitle id=\"copy.button_continue\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">button_continue</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Continue</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the continue button.\n</Segment>\n<NestedTitle id=\"copy.change_prompt\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">change_prompt</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Forgot password?</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the forgot password link.\n</Segment>\n<NestedTitle id=\"copy.code_resend\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">code_resend</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Resend code</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the resend code button.\n</Segment>\n<NestedTitle id=\"copy.code_return\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">code_return</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Back to</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the \"Back to\" link.\n</Segment>\n<NestedTitle id=\"copy.error_email_taken\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_email_taken</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">There is already an account with this email.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when email is already taken.\n</Segment>\n<NestedTitle id=\"copy.error_invalid_code\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_invalid_code</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Code is incorrect.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the confirmation code is incorrect.\n</Segment>\n<NestedTitle id=\"copy.error_invalid_email\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_invalid_email</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Email is not valid.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the email is invalid.\n</Segment>\n<NestedTitle id=\"copy.error_invalid_password\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_invalid_password</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Password is incorrect.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the password is incorrect.\n</Segment>\n<NestedTitle id=\"copy.error_password_mismatch\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_password_mismatch</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Passwords do not match.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the passwords do not match.\n</Segment>\n<NestedTitle id=\"copy.error_validation_error\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">error_validation_error</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Password does not meet requirements.</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nError message when the user enters a password that fails validation.\n</Segment>\n<NestedTitle id=\"copy.input_code\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">input_code</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Code</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the code input.\n</Segment>\n<NestedTitle id=\"copy.input_email\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">input_email</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Email</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the email input.\n</Segment>\n<NestedTitle id=\"copy.input_password\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">input_password</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Password</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the password input.\n</Segment>\n<NestedTitle id=\"copy.input_repeat\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">input_repeat</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Repeat password</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the repeat password input.\n</Segment>\n<NestedTitle id=\"copy.login\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">login</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Login</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the login button.\n</Segment>\n<NestedTitle id=\"copy.login_description\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">login_description</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Sign in with your email</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nDescription of the login page.\n</Segment>\n<NestedTitle id=\"copy.login_prompt\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">login_prompt</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Already have an account?</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the login link.\n</Segment>\n<NestedTitle id=\"copy.login_title\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">login_title</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Welcome to the app</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nTitle of the login page.\n</Segment>\n<NestedTitle id=\"copy.register\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">register</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Register</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the register button.\n</Segment>\n<NestedTitle id=\"copy.register_description\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">register_description</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Sign in with your email</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nDescription of the register page.\n</Segment>\n<NestedTitle id=\"copy.register_prompt\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">register_prompt</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Don't have an account?</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nCopy for the register link.\n</Segment>\n<NestedTitle id=\"copy.register_title\" Tag=\"h5\" parent=\"PasswordUIOptions.copy.\">register_title</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">Welcome to the app</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nTitle of the register page.\n</Segment>\n<NestedTitle id=\"passworduioptions.sendcode\" Tag=\"h4\" parent=\"PasswordUIOptions.\">sendCode</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">(email: <code class=\"primitive\">string</code>, code: <code class=\"primitive\">string</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">void</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to send the confirmation pin code to the user.\n```ts\n{\n  sendCode: async (email, code) => {\n    // Send an email with the code\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"passworduioptions.validatepassword\" Tag=\"h4\" parent=\"PasswordUIOptions.\">validatePassword?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** [<code class=\"type\">StandardSchema</code>](https://github.com/standard-schema/standard-schema)<code class=\"symbol\"> | </code><code class=\"primitive\">(password: <code class=\"primitive\">string</code>) => <code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\"> | </code><code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">undefined</code><code class=\"symbol\"> | </code><code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Section>\nCallback to validate the password on sign up and password reset.\n```ts\n{\n  validatePassword: (password) => {\n     return password.length < 8 ? \"Password must be at least 8 characters\" : undefined\n  }\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/ui/select.mdx",
    "content": "---\ntitle: Select\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/select.tsx\ndescription: Reference doc for the `Select` UI.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nThe UI that's displayed when loading the root page of the OpenAuth server. You can configure\nwhich providers should be displayed in the select UI.\n\n```ts\nimport { Select } from \"@openauthjs/openauth/ui/select\"\n\nexport default issuer({\n  select: Select({\n    providers: {\n      github: {\n        hide: true\n      },\n      google: {\n        display: \"Google\"\n      }\n    }\n  })\n  // ...\n})\n```\n</Section>\n---\n## Methods\n### Select\n<Segment>\n<Section type=\"signature\">\n```ts\nSelect(props?)\n```\n</Section>\n<Section type=\"parameters\">\n#### Parameters\n- <p><code class=\"key\">props?</code> [<code class=\"type\">SelectProps</code>](#selectprops)</p>\n</Section>\n<InlineSection>\n**Returns** <code class=\"primitive\">(providers: <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">string</code><code class=\"symbol\">&gt;</code>, _req: <code class=\"type\">Request</code>) => <code class=\"primitive\">Promise</code><code class=\"symbol\">&lt;</code><code class=\"type\">Response</code><code class=\"symbol\">&gt;</code></code>\n</InlineSection>\n</Segment>\n## SelectProps\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">providers?</code>](#selectprops.providers) <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">Object</code><code class=\"symbol\">&gt;</code></p>\n  - <p>[<code class=\"key\">display?</code>](#providers[].display) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">hide?</code>](#providers[].hide) <code class=\"primitive\">boolean</code></p>\n</Section>\n</Segment>\n<NestedTitle id=\"selectprops.providers\" Tag=\"h4\" parent=\"SelectProps.\">providers?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Record</code><code class=\"symbol\">&lt;</code><code class=\"primitive\">string</code>, <code class=\"primitive\">Object</code><code class=\"symbol\">&gt;</code>\n</InlineSection>\n</Section>\nAn object with all the providers and their config; where the key is the provider name.\n```ts\n{\n  github: {\n    hide: true\n  },\n  google: {\n    display: \"Google\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"providers[].display\" Tag=\"h5\" parent=\"SelectProps.providers[].\">display?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe display name of the provider.\n</Segment>\n<NestedTitle id=\"providers[].hide\" Tag=\"h5\" parent=\"SelectProps.providers[].\">hide?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">boolean</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** false\n</InlineSection>\nWhether to hide the provider from the select UI.\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/docs/ui/theme.mdx",
    "content": "---\ntitle: Themes\neditUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/theme.ts\ndescription: Reference docs for themes.\n---\n\nimport { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'\nimport { Tabs, TabItem } from '@astrojs/starlight/components'\n\n<div class=\"tsdoc\">\n<Section type=\"about\">\nUse one of the built-in themes.\n```ts\nimport { THEME_SST } from \"@openauthjs/openauth/ui/theme\"\n\nexport default issuer({\n  theme: THEME_SST,\n  // ...\n})\n```\n\n\nOr define your own.\n\n\n```ts\nimport type { Theme } from \"@openauthjs/openauth/ui/theme\"\n\nconst MY_THEME: Theme = {\n  title: \"Acne\",\n  radius: \"none\",\n  favicon: \"https://www.example.com/favicon.svg\",\n  // ...\n}\n\nexport default issuer({\n  theme: MY_THEME,\n  // ...\n})\n```\n</Section>\n---\n## Themes\n### THEME_OPENAUTH\n<Segment>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">...</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nBuilt-in default OpenAuth theme.\n</Segment>\n### THEME_SST\n<Segment>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">...</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nBuilt-in theme based on [SST](https://sst.dev).\n</Segment>\n### THEME_SUPABASE\n<Segment>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">...</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nBuilt-in theme based on [Supabase](https://supabase.com).\n</Segment>\n### THEME_TERMINAL\n<Segment>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">...</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nBuilt-in theme based on [Terminal](https://terminal.shop).\n</Segment>\n### THEME_VERCEL\n<Segment>\n\n<InlineSection>\n**Default** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">...</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\nBuilt-in theme based on [Vercel](https://vercel.com).\n</Segment>\n## ColorScheme\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">dark</code>](#colorscheme.dark) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">light</code>](#colorscheme.light) <code class=\"primitive\">string</code></p>\n</Section>\nA type to define values for light and dark mode.\n```ts\n{\n  light: \"#FFF\",\n  dark: \"#000\"\n}\n```\n</Segment>\n<NestedTitle id=\"colorscheme.dark\" Tag=\"h4\" parent=\"ColorScheme.\">dark</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe value for dark mode.\n</Segment>\n<NestedTitle id=\"colorscheme.light\" Tag=\"h4\" parent=\"ColorScheme.\">light</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe value for light mode.\n</Segment>\n## Theme\n<Segment>\n<Section type=\"parameters\">\n- <p>[<code class=\"key\">background?</code>](#theme.background) <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)</p>\n- <p>[<code class=\"key\">css?</code>](#theme.css) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">favicon?</code>](#theme.favicon) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">font?</code>](#theme.font) <code class=\"primitive\">Object</code></p>\n  - <p>[<code class=\"key\">family?</code>](#font.family) <code class=\"primitive\">string</code></p>\n  - <p>[<code class=\"key\">scale?</code>](#font.scale) <code class=\"primitive\">string</code></p>\n- <p>[<code class=\"key\">logo?</code>](#theme.logo) <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)</p>\n- <p>[<code class=\"key\">primary</code>](#theme.primary) <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)</p>\n- <p>[<code class=\"key\">radius?</code>](#theme.radius) <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">none</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">sm</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">md</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">lg</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">full</code><code class=\"symbol\">&rdquo;</code></p>\n- <p>[<code class=\"key\">title?</code>](#theme.title) <code class=\"primitive\">string</code></p>\n</Section>\nA type to define your custom theme.\n</Segment>\n<NestedTitle id=\"theme.background\" Tag=\"h4\" parent=\"Theme.\">background?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)\n</InlineSection>\n</Section>\nThe background color of the theme.\n\nTakes a color or both light and dark colors.\n```ts\n{\n  background: \"#FFF\"\n}\n```\n</Segment>\n<NestedTitle id=\"theme.css\" Tag=\"h4\" parent=\"Theme.\">css?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nCustom CSS that's added to the page in a `<style>` tag.\n\nThis can be used to import custom fonts.\n```ts\n{\n  css: `@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@100;200;300;400;500;600;700;800;900&display=swap');`\n}\n```\n</Segment>\n<NestedTitle id=\"theme.favicon\" Tag=\"h4\" parent=\"Theme.\">favicon?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nA URL to the favicon of your app.\n```ts\n{\n  favicon: \"https://www.example.com/favicon.svg\"\n}\n```\n</Segment>\n<NestedTitle id=\"theme.font\" Tag=\"h4\" parent=\"Theme.\">font?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">Object</code>\n</InlineSection>\n</Section>\nThe font family and scale of the theme.\n</Segment>\n<NestedTitle id=\"font.family\" Tag=\"h5\" parent=\"Theme.font.\">family?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe font family of the theme.\n```ts\n{\n  font: {\n    family: \"Geist Mono, monospace\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"font.scale\" Tag=\"h5\" parent=\"Theme.font.\">scale?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\n\n<InlineSection>\n**Default** \"1\"\n</InlineSection>\nThe font scale of the theme. Can be used to increase or decrease the font sizes across\nthe UI.\n```ts\n{\n  font: {\n    scale: \"1.25\"\n  }\n}\n```\n</Segment>\n<NestedTitle id=\"theme.logo\" Tag=\"h4\" parent=\"Theme.\">logo?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)\n</InlineSection>\n</Section>\nA URL to the logo of your app.\n\nTakes a single image or both light and dark mode versions.\n```ts\n{\n  logo: \"https://www.example.com/logo.svg\"\n}\n```\n</Segment>\n<NestedTitle id=\"theme.primary\" Tag=\"h4\" parent=\"Theme.\">primary</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code><code class=\"symbol\"> | </code>[<code class=\"type\">ColorScheme</code>](#colorscheme)\n</InlineSection>\n</Section>\nThe primary color of the theme.\n\nTakes a color or both light and dark colors.\n```ts\n{\n  primary: \"#FF5E00\"\n}\n```\n</Segment>\n<NestedTitle id=\"theme.radius\" Tag=\"h4\" parent=\"Theme.\">radius?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"symbol\">&ldquo;</code><code class=\"primitive\">none</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">sm</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">md</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">lg</code><code class=\"symbol\">&rdquo;</code><code class=\"symbol\"> | </code><code class=\"symbol\">&ldquo;</code><code class=\"primitive\">full</code><code class=\"symbol\">&rdquo;</code>\n</InlineSection>\n</Section>\nThe border radius of the UI elements.\n```ts\n{\n  radius: \"none\"\n}\n```\n</Segment>\n<NestedTitle id=\"theme.title\" Tag=\"h4\" parent=\"Theme.\">title?</NestedTitle>\n<Segment>\n<Section type=\"parameters\">\n<InlineSection>\n**Type** <code class=\"primitive\">string</code>\n</InlineSection>\n</Section>\nThe name of your app. Also used as the title of the page.\n```ts\n{\n  title: \"Acne\"\n}\n```\n</Segment>\n</div>"
  },
  {
    "path": "www/src/content/docs/index.mdx",
    "content": "---\ntitle: OpenAuth\ndescription: Universal, standards-based auth provider.\ntemplate: splash\nhero:\n  title: Universal, standards-based auth\n  tagline: A universal, standards-based auth provider.\n  image:\n    dark: ../../assets/logo-dark.svg\n    light: ../../assets/logo-light.svg\n    alt: OpenAuth logo\n---\n"
  },
  {
    "path": "www/src/custom.css",
    "content": ""
  },
  {
    "path": "www/src/env.d.ts",
    "content": "/// <reference path=\"../.astro/types.d.ts\" />\n/// <reference types=\"astro/client\" />\n"
  },
  {
    "path": "www/src/styles/lander.css",
    "content": ":root[data-has-hero] {\n  header.header {\n    display: none;\n  }\n  .main-frame {\n    padding-top: 0;\n\n    .main-pane > main {\n      padding: 0;\n    }\n  }\n  main > .content-panel .sl-markdown-content {\n    margin-top: 0;\n  }\n}\n"
  },
  {
    "path": "www/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\"\n}\n"
  }
]